diff --git a/src/catalog/ts/tsstorage.cpp b/src/catalog/ts/tsstorage.cpp index 192ee82..5140b15 100644 --- a/src/catalog/ts/tsstorage.cpp +++ b/src/catalog/ts/tsstorage.cpp @@ -1,554 +1,554 @@ /* Copyright 2008-2014 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 "tsstorage.h" #include "lokalize_debug.h" #include "gettextheader.h" #include "project.h" #include "version.h" #include "prefs_lokalize.h" #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN #define U QLatin1String #else #define U QStringLiteral #endif //static const char* const noyes[]={"no","yes"}; static const QString names[] = {U("source"), U("translation"), U("oldsource"), U("translatorcomment"), U("comment"), U("name"), U("numerus")}; enum TagNames {SourceTag, TargetTag, OldSourceTag, NoteTag, DevNoteTag, NameTag, PluralTag}; static const QString attrnames[] = {U("location"), U("type"), U("obsolete")}; enum AttrNames {LocationAttr, TypeAttr, ObsoleteAttr}; static const QString attrvalues[] = {U("obsolete"), U("vanished")}; enum AttValues {ObsoleteVal, VanishedVal}; TsStorage::TsStorage() : CatalogStorage() { } int TsStorage::capabilities() const { return 0;//MultipleNotes; } //BEGIN OPEN/SAVE int TsStorage::load(QIODevice* device) { QElapsedTimer chrono; chrono.start(); QXmlSimpleReader reader; reader.setFeature(QStringLiteral("http://qt-project.org/xml/features/report-whitespace-only-CharData"), true); reader.setFeature(QStringLiteral("http://xml.org/sax/features/namespaces"), false); QXmlInputSource source(device); QString errorMsg; int errorLine;//+errorColumn; bool success = m_doc.setContent(&source, &reader, &errorMsg, &errorLine/*,errorColumn*/); if (!success) { qCWarning(LOKALIZE_LOG) << "parse error" << errorMsg << errorLine; return errorLine + 1; } QDomElement file = m_doc.elementsByTagName(QStringLiteral("TS")).at(0).toElement(); m_sourceLangCode = file.attribute(QStringLiteral("sourcelanguage")); m_targetLangCode = file.attribute(QStringLiteral("language")); m_numberOfPluralForms = numberOfPluralFormsForLangCode(m_targetLangCode); //Create entry mapping. //Along the way: for langs with more than 2 forms //we create any form-entries additionally needed entries = m_doc.elementsByTagName(QStringLiteral("message")); qCWarning(LOKALIZE_LOG) << chrono.elapsed() << "secs, " << entries.size() << "entries"; return 0; } bool TsStorage::save(QIODevice* device, bool belongsToProject) { Q_UNUSED(belongsToProject) QTextStream stream(device); m_doc.save(stream, 4); return true; } //END OPEN/SAVE //BEGIN STORAGE TRANSLATION int TsStorage::size() const { //return m_map.size(); return entries.size(); } /** * helper structure used during XLIFF XML walk-through */ struct TsContentEditingData { enum ActionType {Get, DeleteText, InsertText, CheckLength}; QString stringToInsert; int pos; int lengthOfStringToRemove; ActionType actionType; ///Get TsContentEditingData(ActionType type = Get) : pos(-1) , lengthOfStringToRemove(-1) , actionType(type) {} ///DeleteText TsContentEditingData(int p, int l) : pos(p) , lengthOfStringToRemove(l) , actionType(DeleteText) {} ///InsertText TsContentEditingData(int p, const QString& s) : stringToInsert(s) , pos(p) , lengthOfStringToRemove(-1) , actionType(InsertText) {} }; static QString doContent(QDomElement elem, int startingPos, TsContentEditingData* data); /** * walks through XLIFF XML and performs actions depending on TsContentEditingData: * - reads content * - deletes content, or * - inserts content */ static QString content(QDomElement elem, TsContentEditingData* data = 0) { return doContent(elem, 0, data); } static QString doContent(QDomElement elem, int startingPos, TsContentEditingData* data) { //actually startingPos is current pos QString result; if (elem.isNull() || (!result.isEmpty() && data && data->actionType == TsContentEditingData::CheckLength)) return QString(); bool seenCharacterDataAfterElement = false; QDomNode n = elem.firstChild(); while (!n.isNull()) { if (n.isCharacterData()) { seenCharacterDataAfterElement = true; QDomCharacterData c = n.toCharacterData(); QString cData = c.data(); if (data && data->pos != -1 && data->pos >= startingPos && data->pos <= startingPos + cData.size()) { // time to do some action! ;) int localStartPos = data->pos - startingPos; //BEGIN DELETE TEXT if (data->actionType == TsContentEditingData::DeleteText) { //(data->lengthOfStringToRemove!=-1) if (localStartPos + data->lengthOfStringToRemove > cData.size()) { //text is fragmented into several QDomCharacterData int localDelLen = cData.size() - localStartPos; //qCWarning(LOKALIZE_LOG)<<"text is fragmented into several QDomCharacterData. localDelLen:"<lengthOfStringToRemove = data->lengthOfStringToRemove - localDelLen; //data->pos=startingPos; //qCWarning(LOKALIZE_LOG)<<"\tsetup:"<pos<lengthOfStringToRemove; } else { //qCWarning(LOKALIZE_LOG)<<"simple delete"<lengthOfStringToRemove; c.deleteData(localStartPos, data->lengthOfStringToRemove); data->actionType = TsContentEditingData::CheckLength; return QString('a');//so it exits 100% } } //END DELETE TEXT //INSERT else if (data->actionType == TsContentEditingData::InsertText) { c.insertData(localStartPos, data->stringToInsert); data->actionType = TsContentEditingData::CheckLength; return QString('a');//so it exits 100% } cData = c.data(); } //else // if (data&&data->pos!=-1/*&& n.nextSibling().isNull()*/) // qCWarning(LOKALIZE_LOG)<<"arg!"<pos"<pos; result += cData; startingPos += cData.size(); } n = n.nextSibling(); } if (!seenCharacterDataAfterElement) { //add empty charData child so that user could add some text elem.appendChild(elem.ownerDocument().createTextNode(QString())); } return result; } //flat-model interface (ignores XLIFF grouping) CatalogString TsStorage::catalogString(QDomElement contentElement) const { CatalogString catalogString; TsContentEditingData data(TsContentEditingData::Get); catalogString.string = content(contentElement, &data); return catalogString; } CatalogString TsStorage::catalogString(const DocPosition& pos) const { return catalogString(pos.part == DocPosition::Target ? targetForPos(pos) : sourceForPos(pos.entry)); } CatalogString TsStorage::targetWithTags(DocPosition pos) const { return catalogString(targetForPos(pos)); } CatalogString TsStorage::sourceWithTags(DocPosition pos) const { return catalogString(sourceForPos(pos.entry)); } QString TsStorage::source(const DocPosition& pos) const { return content(sourceForPos(pos.entry)); } QString TsStorage::target(const DocPosition& pos) const { return content(targetForPos(pos)); } QString TsStorage::sourceWithPlurals(const DocPosition& pos, bool truncateFirstLine) const { QString str = source(pos); if (truncateFirstLine) { int truncatePos = str.indexOf("\n"); if (truncatePos != -1) str.truncate(truncatePos); } return str; } QString TsStorage::targetWithPlurals(const DocPosition& pos, bool truncateFirstLine) const { QString str = target(pos); if (truncateFirstLine) { int truncatePos = str.indexOf("\n"); if (truncatePos != -1) str.truncate(truncatePos); } return str; } void TsStorage::targetDelete(const DocPosition& pos, int count) { TsContentEditingData data(pos.offset, count); content(targetForPos(pos), &data); } void TsStorage::targetInsert(const DocPosition& pos, const QString& arg) { qCWarning(LOKALIZE_LOG) << pos.entry << arg; QDomElement targetEl = targetForPos(pos); //BEGIN add <*target> if (targetEl.isNull()) { QDomNode unitEl = unitForPos(pos.entry); QDomNode refNode = unitEl.firstChildElement(names[SourceTag]); targetEl = unitEl.insertAfter(m_doc.createElement(names[TargetTag]), refNode).toElement(); if (pos.entry < size()) { targetEl.appendChild(m_doc.createTextNode(arg));//i bet that pos.offset is 0 ;) return; } } //END add <*target> if (arg.isEmpty()) return; //means we were called just to add tag TsContentEditingData data(pos.offset, arg); content(targetEl, &data); } void TsStorage::setTarget(const DocPosition& pos, const QString& arg) { Q_UNUSED(pos); Q_UNUSED(arg); //TODO } QVector TsStorage::altTrans(const DocPosition& pos) const { QVector result; QString oldsource = content(unitForPos(pos.entry).firstChildElement(names[OldSourceTag])); if (!oldsource.isEmpty()) result << AltTrans(CatalogString(oldsource), i18n("Previous source value, saved by lupdate tool")); return result; } QStringList TsStorage::sourceFiles(const DocPosition& pos) const { QStringList result; QDomElement elem = unitForPos(pos.entry).firstChildElement(attrnames[LocationAttr]); while (!elem.isNull()) { QString sourcefile = elem.attribute(QStringLiteral("filename")); QString linenumber = elem.attribute(QStringLiteral("line")); if (!(sourcefile.isEmpty() && linenumber.isEmpty())) result.append(sourcefile + ':' + linenumber); elem = elem.nextSiblingElement(attrnames[LocationAttr]); } //qSort(result); return result; } QVector TsStorage::notes(const DocPosition& pos) const { QVector result; QDomElement elem = unitForPos(pos.entry).firstChildElement(names[NoteTag]); while (!elem.isNull()) { Note note; note.content = elem.text(); result.append(note); elem = elem.nextSiblingElement(names[NoteTag]); } return result; } QVector TsStorage::developerNotes(const DocPosition& pos) const { QVector result; QDomElement elem = unitForPos(pos.entry).firstChildElement(names[DevNoteTag]); while (!elem.isNull()) { Note note; note.content = elem.text(); result.append(note); elem = elem.nextSiblingElement(names[DevNoteTag]); } return result; } Note TsStorage::setNote(DocPosition pos, const Note& note) { //qCWarning(LOKALIZE_LOG)< if needed + targetInsert(pos, QString()); //adds if needed QDomElement target = unitForPos(pos.entry).firstChildElement(names[TargetTag]); //asking directly to bypass plural state detection if (target.attribute(attrnames[TypeAttr]) == attrvalues[ObsoleteVal]) return; if (approved) target.removeAttribute(attrnames[TypeAttr]); else target.setAttribute(attrnames[TypeAttr], QStringLiteral("unfinished")); } bool TsStorage::isApproved(const DocPosition& pos) const { QDomElement target = unitForPos(pos.entry).firstChildElement(names[TargetTag]); return !target.hasAttribute(attrnames[TypeAttr]) || target.attribute(attrnames[TypeAttr]) == attrvalues[VanishedVal]; } bool TsStorage::isObsolete(int entry) const { QDomElement target = unitForPos(entry).firstChildElement(names[TargetTag]); QString v = target.attribute(attrnames[TypeAttr]); return v == attrvalues[ObsoleteVal] || v == attrvalues[VanishedVal]; } bool TsStorage::isEmpty(const DocPosition& pos) const { TsContentEditingData data(TsContentEditingData::CheckLength); return content(targetForPos(pos), &data).isEmpty(); } bool TsStorage::isEquivTrans(const DocPosition& pos) const { Q_UNUSED(pos) return true;//targetForPos(pos.entry).attribute("equiv-trans")!="no"; } void TsStorage::setEquivTrans(const DocPosition& pos, bool equivTrans) { Q_UNUSED(pos) Q_UNUSED(equivTrans) //targetForPos(pos.entry).setAttribute("equiv-trans",noyes[equivTrans]); } QDomElement TsStorage::unitForPos(int pos) const { return entries.at(pos).toElement(); } QDomElement TsStorage::targetForPos(DocPosition pos) const { QDomElement unit = unitForPos(pos.entry); QDomElement translation = unit.firstChildElement(names[TargetTag]); if (!unit.hasAttribute(names[PluralTag])) return translation; if (pos.form == -1) pos.form = 0; QDomNodeList forms = translation.elementsByTagName(QStringLiteral("numerusform")); while (pos.form >= forms.size()) translation.appendChild(unit.ownerDocument().createElement(QStringLiteral("numerusform"))); return forms.at(pos.form).toElement(); } QDomElement TsStorage::sourceForPos(int pos) const { return unitForPos(pos).firstChildElement(names[SourceTag]); } void TsStorage::setTargetLangCode(const QString& langCode) { m_targetLangCode = langCode; QDomElement file = m_doc.elementsByTagName(QStringLiteral("TS")).at(0).toElement(); if (m_targetLangCode != file.attribute(QStringLiteral("language")).replace('-', '_')) { QString l = langCode; file.setAttribute(QStringLiteral("language"), l.replace('_', '-')); } } //END STORAGE TRANSLATION diff --git a/src/languagetool/languagetoolresultjob.cpp b/src/languagetool/languagetoolresultjob.cpp index 2c12e17..8f3f932 100644 --- a/src/languagetool/languagetoolresultjob.cpp +++ b/src/languagetool/languagetoolresultjob.cpp @@ -1,160 +1,161 @@ /* Copyright (C) 2019-2020 Laurent Montel 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "languagetoolresultjob.h" #include "lokalize_debug.h" #include #include LanguageToolResultJob::LanguageToolResultJob(QObject *parent) : QObject(parent) { } LanguageToolResultJob::~LanguageToolResultJob() { } static bool hasNotEmptyText(const QString &text) { for (int i = 0; i < text.length(); ++i) { if (!text.at(i).isSpace()) { return true; } } return false; } bool LanguageToolResultJob::canStart() const { return canStartError() == LanguageToolResultJob::JobError::NotError; } LanguageToolResultJob::JobError LanguageToolResultJob::canStartError() const { if (!mNetworkAccessManager) { return LanguageToolResultJob::JobError::NetworkManagerNotDefined; } if (!hasNotEmptyText(mText)) { return LanguageToolResultJob::JobError::EmptyText; } if (mUrl.isEmpty()) { return LanguageToolResultJob::JobError::UrlNotDefined; } if (mLanguage.isEmpty()) { return LanguageToolResultJob::JobError::LanguageNotDefined; } return LanguageToolResultJob::JobError::NotError; } void LanguageToolResultJob::start() { const LanguageToolResultJob::JobError errorType = canStartError(); switch (errorType) { case LanguageToolResultJob::JobError::EmptyText: return; case LanguageToolResultJob::JobError::UrlNotDefined: case LanguageToolResultJob::JobError::NetworkManagerNotDefined: case LanguageToolResultJob::JobError::LanguageNotDefined: qCWarning(LOKALIZE_LOG) << "Impossible to start language tool"; return; case LanguageToolResultJob::JobError::NotError: break; } QNetworkRequest request(QUrl::fromUserInput(mUrl)); addRequestAttribute(request); const QByteArray ba = "text=" + mText.toUtf8() + "&language=" + mLanguage.toLatin1(); + //qCWarning(LOKALIZE_LOG) << "Sending LT query" << ba; QNetworkReply *reply = mNetworkAccessManager->post(request, ba); connect(reply, &QNetworkReply::finished, this, &LanguageToolResultJob::slotCheckGrammarFinished); connect(mNetworkAccessManager, &QNetworkAccessManager::finished, this, &LanguageToolResultJob::slotFinish); } void LanguageToolResultJob::slotFinish(QNetworkReply *reply) { if (reply->error() != QNetworkReply::NoError) { qCWarning(LOKALIZE_LOG) << " Error reply - "<errorString(); Q_EMIT error(reply->errorString()); } } void LanguageToolResultJob::slotCheckGrammarFinished() { QNetworkReply *reply = qobject_cast(sender()); if (reply) { const QByteArray data = reply->readAll(); Q_EMIT finished(QString::fromUtf8(data)); } reply->deleteLater(); deleteLater(); } void LanguageToolResultJob::addRequestAttribute(QNetworkRequest &request) const { request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/x-www-form-urlencoded")); } QString LanguageToolResultJob::language() const { return mLanguage; } void LanguageToolResultJob::setLanguage(const QString &language) { mLanguage = language; } QString LanguageToolResultJob::url() const { return mUrl; } void LanguageToolResultJob::setUrl(const QString &url) { mUrl = url; } QStringList LanguageToolResultJob::arguments() const { return mArguments; } void LanguageToolResultJob::setArguments(const QStringList &arguments) { mArguments = arguments; } QNetworkAccessManager *LanguageToolResultJob::networkAccessManager() const { return mNetworkAccessManager; } void LanguageToolResultJob::setNetworkAccessManager(QNetworkAccessManager *networkAccessManager) { mNetworkAccessManager = networkAccessManager; } QString LanguageToolResultJob::text() const { return mText; } void LanguageToolResultJob::setText(const QString &text) { mText = text; } diff --git a/src/xlifftextedit.cpp b/src/xlifftextedit.cpp index 5d7835f..4215064 100644 --- a/src/xlifftextedit.cpp +++ b/src/xlifftextedit.cpp @@ -1,1371 +1,1370 @@ /* **************************************************************************** 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 "xlifftextedit.h" #include "lokalize_debug.h" #include "catalog.h" #include "cmd.h" #include "syntaxhighlighter.h" #include "prefs_lokalize.h" #include "prefs.h" #include "project.h" #include "completionstorage.h" #include "languagetoolmanager.h" #include "languagetoolresultjob.h" #include "languagetoolparser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include inline static QImage generateImage(const QString& str, const QFont& font) { // im_count++; // QTime a;a.start(); QStyleOptionButton opt; opt.fontMetrics = QFontMetrics(font); opt.text = ' ' + str + ' '; opt.rect = opt.fontMetrics.boundingRect(opt.text).adjusted(0, 0, 5, 5); opt.rect.moveTo(0, 0); QImage result(opt.rect.size(), QImage::Format_ARGB32); result.fill(0);//0xAARRGGBB QPainter painter(&result); QApplication::style()->drawControl(QStyle::CE_PushButton, &opt, &painter); // im_time+=a.elapsed(); // qCWarning(LOKALIZE_LOG)<width() + 2 * frameWidth(); return QSize(w, h); } bool MyCompletionBox::eventFilter(QObject* object, QEvent* event) { if (event->type() == QEvent::KeyPress) { QKeyEvent* e = static_cast(event); if (e->key() == Qt::Key_PageDown || e->key() == Qt::Key_PageUp) { hide(); return false; } } return KCompletionBox::eventFilter(object, event); } TranslationUnitTextEdit::~TranslationUnitTextEdit() { disconnect(document(), &QTextDocument::contentsChange, this, &TranslationUnitTextEdit::contentsChanged); } TranslationUnitTextEdit::TranslationUnitTextEdit(Catalog* catalog, DocPosition::Part part, QWidget* parent) : KTextEdit(parent) , m_currentUnicodeNumber(0) , m_langUsesSpaces(true) , m_catalog(catalog) , m_part(part) , m_highlighter(new SyntaxHighlighter(this)) , m_enabled(Settings::autoSpellcheck()) , m_completionBox(0) , m_cursorSelectionStart(0) , m_cursorSelectionEnd(0) , m_languageToolTimer(new QTimer(this)) { setReadOnly(part == DocPosition::Source); setUndoRedoEnabled(false); setAcceptRichText(false); m_highlighter->setActive(m_enabled); setHighlighter(m_highlighter); if (part == DocPosition::Target) { connect(document(), &QTextDocument::contentsChange, this, &TranslationUnitTextEdit::contentsChanged); connect(this, &KTextEdit::cursorPositionChanged, this, &TranslationUnitTextEdit::emitCursorPositionChanged); + connect(m_languageToolTimer, &QTimer::timeout, this, &TranslationUnitTextEdit::launchLanguageTool); } connect(catalog, QOverload<>::of(&Catalog::signalFileLoaded), this, &TranslationUnitTextEdit::fileLoaded); //connect (Project::instance(), &Project::configChanged, this, &TranslationUnitTextEdit::projectConfigChanged); - connect(m_languageToolTimer, &QTimer::timeout, this, &TranslationUnitTextEdit::launchLanguageTool); m_languageToolTimer->setSingleShot(true); } void TranslationUnitTextEdit::setSpellCheckingEnabled(bool enable) { Settings::setAutoSpellcheck(enable); m_enabled = enable; m_highlighter->setActive(enable); SettingsController::instance()->dirty = true; } void TranslationUnitTextEdit::setVisualizeSeparators(bool enable) { if (enable) { QTextOption textoption = document()->defaultTextOption(); textoption.setFlags(textoption.flags() | QTextOption::ShowLineAndParagraphSeparators | QTextOption::ShowTabsAndSpaces); document()->setDefaultTextOption(textoption); } else { QTextOption textoption = document()->defaultTextOption(); textoption.setFlags(textoption.flags() & (~QTextOption::ShowLineAndParagraphSeparators) & (~QTextOption::ShowTabsAndSpaces)); document()->setDefaultTextOption(textoption); } } void TranslationUnitTextEdit::fileLoaded() { QString langCode = m_part == DocPosition::Source ? m_catalog->sourceLangCode() : m_catalog->targetLangCode(); QLocale langLocale(langCode); // First try to use a locale name derived from the language code m_highlighter->setCurrentLanguage(langLocale.name()); //qCWarning(LOKALIZE_LOG) << "Attempting to set highlighting for " << m_part << " as " << langLocale.name(); // If that fails, try to use the language code directly if (m_highlighter->currentLanguage() != langLocale.name() || m_highlighter->currentLanguage().isEmpty()) { m_highlighter->setCurrentLanguage(langCode); //qCWarning(LOKALIZE_LOG) << "Attempting to set highlighting for " << m_part << " as " << langCode; if (m_highlighter->currentLanguage() != langCode && langCode.length() > 2) { m_highlighter->setCurrentLanguage(langCode.left(2)); //qCWarning(LOKALIZE_LOG) << "Attempting to set highlighting for " << m_part << " as " << langCode.left(2); } } //qCWarning(LOKALIZE_LOG) << "Spellchecker found "<spellCheckerFound()<< " as "<currentLanguage(); //setSpellCheckingLanguage(m_highlighter->currentLanguage()); //"i use an english locale while translating kde pot files from english to hebrew" Bug #181989 Qt::LayoutDirection targetLanguageDirection = Qt::LeftToRight; static const QLocale::Language rtlLanguages[] = {QLocale::Arabic, QLocale::Hebrew, QLocale::Urdu, QLocale::Persian, QLocale::Pashto}; int i = sizeof(rtlLanguages) / sizeof(QLocale::Arabic); while (--i >= 0 && langLocale.language() != rtlLanguages[i]) ; if (i != -1) targetLanguageDirection = Qt::RightToLeft; setLayoutDirection(targetLanguageDirection); if (m_part == DocPosition::Source) return; //"Some language do not need space between words. For example Chinese." static const QLocale::Language noSpaceLanguages[] = {QLocale::Chinese}; i = sizeof(noSpaceLanguages) / sizeof(QLocale::Chinese); while (--i >= 0 && langLocale.language() != noSpaceLanguages[i]) ; m_langUsesSpaces = (i == -1); } void TranslationUnitTextEdit::reflectApprovementState() { if (m_part == DocPosition::Source || m_currentPos.entry == -1) return; bool approved = m_catalog->isApproved(m_currentPos.entry); disconnect(document(), &QTextDocument::contentsChange, this, &TranslationUnitTextEdit::contentsChanged); m_highlighter->setApprovementState(approved); m_highlighter->rehighlight(); connect(document(), &QTextDocument::contentsChange, this, &TranslationUnitTextEdit::contentsChanged); viewport()->setBackgroundRole(approved ? QPalette::Base : QPalette::AlternateBase); if (approved) emit approvedEntryDisplayed(); else emit nonApprovedEntryDisplayed(); bool untr = m_catalog->isEmpty(m_currentPos); if (untr) emit untranslatedEntryDisplayed(); else emit translatedEntryDisplayed(); } void TranslationUnitTextEdit::reflectUntranslatedState() { if (m_part == DocPosition::Source || m_currentPos.entry == -1) return; bool untr = m_catalog->isEmpty(m_currentPos); if (untr) emit untranslatedEntryDisplayed(); else emit translatedEntryDisplayed(); } /** * makes MsgEdit reflect current entry **/ CatalogString TranslationUnitTextEdit::showPos(DocPosition docPosition, const CatalogString& refStr, bool keepCursor) { docPosition.part = m_part; m_currentPos = docPosition; CatalogString catalogString = m_catalog->catalogString(m_currentPos); QString target = catalogString.string; _oldMsgstr = target; //_oldMsgstrAscii=document()->toPlainText(); <-- MOVED THIS TO THE END //BEGIN pos QTextCursor cursor = textCursor(); int pos = cursor.position(); int anchor = cursor.anchor(); //qCWarning(LOKALIZE_LOG)<<"called"<<"pos"<sourceWithTags(docPosition) : refStr); connect(document(), &QTextDocument::contentsChange, this, &TranslationUnitTextEdit::contentsChanged); _oldMsgstrAscii = document()->toPlainText(); //BEGIN pos QTextCursor t = textCursor(); t.movePosition(QTextCursor::Start); if (pos || anchor) { //qCWarning(LOKALIZE_LOG)<<"setting"<blockSignals(true); clear(); QTextCursor c = textCursor(); insertContent(c, catStr, refStr); document()->blockSignals(false); if (m_part == DocPosition::Target) m_highlighter->setSourceString(refStr.string); else //reflectApprovementState() does this for Target m_highlighter->rehighlight(); //explicitly because the signals were disabled if (Settings::self()->languageToolDelay() > 0) { m_languageToolTimer->start(Settings::self()->languageToolDelay() * 1000); } } #if 0 struct SearchFunctor { virtual int operator()(const QString& str, int startingPos); }; int SearchFunctor::operator()(const QString& str, int startingPos) { return str.indexOf(TAGRANGE_IMAGE_SYMBOL, startingPos); } struct AlternativeSearchFunctor: public SearchFunctor { int operator()(const QString& str, int startingPos); }; int AlternativeSearchFunctor::operator()(const QString& str, int startingPos) { int tagPos = str.indexOf(TAGRANGE_IMAGE_SYMBOL, startingPos); int diffStartPos = str.indexOf("{KBABEL", startingPos); int diffEndPos = str.indexOf("{/KBABEL", startingPos); int diffPos = qMin(diffStartPos, diffEndPos); if (diffPos == -1) diffPos = qMax(diffStartPos, diffEndPos); int result = qMin(tagPos, diffPos); if (result == -1) result = qMax(tagPos, diffPos); return result; } #endif void insertContent(QTextCursor& cursor, const CatalogString& catStr, const CatalogString& refStr, bool insertText) { //settings for TMView QTextCharFormat chF = cursor.charFormat(); QFont font = cursor.document()->defaultFont(); //font.setWeight(chF.fontWeight()); QMap posToTag; int i = catStr.tags.size(); while (--i >= 0) { //qCDebug(LOKALIZE_LOG)<<"\t"< sourceTagIdToIndex = refStr.tagIdToIndex(); int refTagIndexOffset = sourceTagIdToIndex.size(); i = 0; int prev = 0; while ((i = catStr.string.indexOf(TAGRANGE_IMAGE_SYMBOL, i)) != -1) { #if 0 SearchFunctor nextStopSymbol = AlternativeSearchFunctor(); char state = '0'; while ((i = nextStopSymbol(catStr.string, i)) != -1) { //handle diff display for TMView if (catStr.string.at(i) != TAGRANGE_IMAGE_SYMBOL) { if (catStr.string.at(i + 1) == '/') state = '0'; else if (catStr.string.at(i + 8) == 'D') state = '-'; else state = '+'; continue; } #endif if (insertText) cursor.insertText(catStr.string.mid(prev, i - prev)); else { cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, i - prev); cursor.deleteChar();//delete TAGRANGE_IMAGE_SYMBOL to insert it properly } if (!posToTag.contains(i)) { prev = ++i; continue; } int tagIndex = posToTag.value(i); InlineTag tag = catStr.tags.at(tagIndex); QString name = tag.id; QString text; if (tag.type == InlineTag::mrk) text = QStringLiteral("*"); else if (!tag.equivText.isEmpty()) text = tag.equivText; //TODO add number? when? -- right now this is done for gettext qt's 156 mark else text = QString::number(sourceTagIdToIndex.contains(tag.id) ? sourceTagIdToIndex.value(tag.id) : (tagIndex + refTagIndexOffset)); if (tag.start != tag.end) { //qCWarning(LOKALIZE_LOG)<<"b"<resource(QTextDocument::ImageResource, QUrl(name)).isNull()) cursor.document()->addResource(QTextDocument::ImageResource, QUrl(name), generateImage(text, font)); cursor.insertImage(name);//NOTE what if twice the same name? cursor.setCharFormat(chF); prev = ++i; } cursor.insertText(catStr.string.mid(prev)); } void TranslationUnitTextEdit::contentsChanged(int offset, int charsRemoved, int charsAdded) { Q_ASSERT(m_catalog->targetLangCode().length()); Q_ASSERT(Project::instance()->targetLangCode().length()); //qCWarning(LOKALIZE_LOG)<<"contentsChanged. offset"<toPlainText(); if (editTextAscii == _oldMsgstrAscii) { //qCWarning(LOKALIZE_LOG)<<"stopping"<targetWithTags(pos).string; const QStringRef addedText = editText.midRef(offset, charsAdded); //BEGIN XLIFF markup handling //protect from tag removal //TODO use midRef when Qt 4.8 is in distros bool markupRemoved = charsRemoved && target.midRef(offset, charsRemoved).contains(TAGRANGE_IMAGE_SYMBOL); bool markupAdded = charsAdded && addedText.contains(TAGRANGE_IMAGE_SYMBOL); if (markupRemoved || markupAdded) { bool modified = false; CatalogString targetWithTags = m_catalog->targetWithTags(m_currentPos); //special case when the user presses Del w/o selection if (!charsAdded && charsRemoved == 1) { int i = targetWithTags.tags.size(); while (--i >= 0) { if (targetWithTags.tags.at(i).start == offset || targetWithTags.tags.at(i).end == offset) { modified = true; pos.offset = targetWithTags.tags.at(i).start; m_catalog->push(new DelTagCmd(m_catalog, pos)); } } } else if (!markupAdded) { //check if all { plus } tags were selected modified = removeTargetSubstring(offset, charsRemoved, /*refresh*/false); if (modified && charsAdded) m_catalog->push(new InsTextCmd(m_catalog, pos, addedText.toString())); } //qCWarning(LOKALIZE_LOG)<<"calling showPos"; showPos(m_currentPos, CatalogString(),/*keepCursor*/true); if (!modified) { //qCWarning(LOKALIZE_LOG)<<"stop"; return; } } //END XLIFF markup handling else { if (charsRemoved) m_catalog->push(new DelTextCmd(m_catalog, pos, _oldMsgstr.mid(offset, charsRemoved))); _oldMsgstr = editText; //newStr becomes OldStr _oldMsgstrAscii = editTextAscii; //qCWarning(LOKALIZE_LOG)<<"char"<push(new InsTextCmd(m_catalog, pos, addedText.toString())); } /* TODO if (_leds) { if (m_catalog->msgstr(pos).isEmpty()) _leds->ledUntr->on(); else _leds->ledUntr->off(); } */ requestToggleApprovement(); reflectUntranslatedState(); // for mergecatalog (remove entry from index) // and for statusbar emit contentsModified(m_currentPos); if (charsAdded == 1) { int sp = target.lastIndexOf(CompletionStorage::instance()->rxSplit, offset - 1); int len = (offset - sp); int wordCompletionLength = Settings::self()->wordCompletionLength(); if (wordCompletionLength >= 3 && len >= wordCompletionLength) doCompletion(offset + 1); else if (m_completionBox) m_completionBox->hide(); } else if (m_completionBox) m_completionBox->hide(); //qCWarning(LOKALIZE_LOG)<<"finish"; //Start LanguageToolTimer if (Settings::self()->languageToolDelay() > 0) { m_languageToolTimer->start(Settings::self()->languageToolDelay() * 1000); } } bool TranslationUnitTextEdit::removeTargetSubstring(int delStart, int delLen, bool refresh) { if (Q_UNLIKELY(m_currentPos.entry == -1)) return false; if (!::removeTargetSubstring(m_catalog, m_currentPos, delStart, delLen)) return false; requestToggleApprovement(); if (refresh) { //qCWarning(LOKALIZE_LOG)<<"calling showPos"; showPos(m_currentPos, CatalogString(),/*keepCursor*/true/*false*/); } emit contentsModified(m_currentPos.entry); return true; } void TranslationUnitTextEdit::insertCatalogString(CatalogString catStr, int start, bool refresh) { QString REMOVEME = QStringLiteral("REMOVEME"); CatalogString sourceForReferencing = m_catalog->sourceWithTags(m_currentPos); const CatalogString target = m_catalog->targetWithTags(m_currentPos); QHash id2tagIndex; int i = sourceForReferencing.tags.size(); while (--i >= 0) id2tagIndex.insert(sourceForReferencing.tags.at(i).id, i); //remove markup that is already in target, to avoid duplicates if the string being inserted contains it as well foreach (const InlineTag& tag, target.tags) { if (id2tagIndex.contains(tag.id)) sourceForReferencing.tags[id2tagIndex.value(tag.id)].id = REMOVEME; } //iterating from the end is essential i = sourceForReferencing.tags.size(); while (--i >= 0) if (sourceForReferencing.tags.at(i).id == REMOVEME) sourceForReferencing.tags.removeAt(i); adaptCatalogString(catStr, sourceForReferencing); ::insertCatalogString(m_catalog, m_currentPos, catStr, start); if (refresh) { //qCWarning(LOKALIZE_LOG)<<"calling showPos"; showPos(m_currentPos, CatalogString(),/*keepCursor*/true/*false*/); QTextCursor cursor = textCursor(); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, catStr.string.size()); setTextCursor(cursor); } } const QString LOKALIZE_XLIFF_MIMETYPE = QStringLiteral("application/x-lokalize-xliff+xml"); QMimeData* TranslationUnitTextEdit::createMimeDataFromSelection() const { QMimeData *mimeData = new QMimeData; CatalogString catalogString = m_catalog->catalogString(m_currentPos); QTextCursor cursor = textCursor(); int start = qMin(cursor.anchor(), cursor.position()); int end = qMax(cursor.anchor(), cursor.position()); QMap tagPlaces; if (fillTagPlaces(tagPlaces, catalogString, start, end - start)) { //transform CatalogString //TODO substring method catalogString.string = catalogString.string.mid(start, end - start); QList::iterator it = catalogString.tags.begin(); while (it != catalogString.tags.end()) { if (!tagPlaces.contains(it->start)) it = catalogString.tags.erase(it); else { it->start -= start; it->end -= start; ++it; } } QByteArray a; QDataStream out(&a, QIODevice::WriteOnly); QVariant v; v.setValue(catalogString); out << v; mimeData->setData(LOKALIZE_XLIFF_MIMETYPE, a); } QString text = catalogString.string; text.remove(TAGRANGE_IMAGE_SYMBOL); mimeData->setText(text); return mimeData; } void TranslationUnitTextEdit::dragEnterEvent(QDragEnterEvent * event) { QObject* dragSource = event->source(); if (dragSource->objectName().compare("qt_scrollarea_viewport") == 0) dragSource = dragSource->parent(); //This is a deplacement within the Target area if (m_part == DocPosition::Target && this == dragSource) { QTextCursor cursor = textCursor(); int start = qMin(cursor.anchor(), cursor.position()); int end = qMax(cursor.anchor(), cursor.position()); m_cursorSelectionEnd = end; m_cursorSelectionStart = start; } QTextEdit::dragEnterEvent(event); } void TranslationUnitTextEdit::dropEvent(QDropEvent * event) { //Ensure the cursor moves to the correct location if (m_part == DocPosition::Target) { setTextCursor(cursorForPosition(event->pos())); //This is a copy modifier, disable the selection flags if (event->keyboardModifiers() & Qt::ControlModifier) { m_cursorSelectionEnd = 0; m_cursorSelectionStart = 0; } } QTextEdit::dropEvent(event); } void TranslationUnitTextEdit::insertFromMimeData(const QMimeData * source) { if (m_part == DocPosition::Source) return; if (source->hasFormat(LOKALIZE_XLIFF_MIMETYPE)) { //qCWarning(LOKALIZE_LOG)<<"has"; QVariant v; QByteArray data = source->data(LOKALIZE_XLIFF_MIMETYPE); QDataStream in(&data, QIODevice::ReadOnly); in >> v; //qCWarning(LOKALIZE_LOG)<<"ins"<(v).string<(v).ranges.size(); int start = 0; m_catalog->beginMacro(i18nc("@item Undo action item", "Insert text with markup")); QTextCursor cursor = textCursor(); if (cursor.hasSelection()) { start = qMin(cursor.anchor(), cursor.position()); int end = qMax(cursor.anchor(), cursor.position()); removeTargetSubstring(start, end - start); cursor.setPosition(start); setTextCursor(cursor); } else //sets right cursor position implicitly -- needed for mouse paste { QMimeData mimeData; mimeData.setText(QString()); if (m_cursorSelectionEnd != m_cursorSelectionStart) { int oldCursorPosition = textCursor().position(); removeTargetSubstring(m_cursorSelectionStart, m_cursorSelectionEnd - m_cursorSelectionStart); if (oldCursorPosition >= m_cursorSelectionEnd) { cursor.setPosition(oldCursorPosition - (m_cursorSelectionEnd - m_cursorSelectionStart)); setTextCursor(cursor); } } KTextEdit::insertFromMimeData(&mimeData); start = textCursor().position(); } insertCatalogString(v.value(), start); m_catalog->endMacro(); } else { QString text = source->text(); text.remove(TAGRANGE_IMAGE_SYMBOL); insertPlainTextWithCursorCheck(text); } } static bool isMasked(const QString & str, uint col) { if (col == 0 || str.isEmpty()) return false; uint counter = 0; int pos = col; while (pos >= 0 && str.at(pos) == '\\') { counter++; pos--; } return !(bool)(counter % 2); } void TranslationUnitTextEdit::keyPressEvent(QKeyEvent * keyEvent) { QString spclChars = QStringLiteral("abfnrtv'?\\"); if (keyEvent->matches(QKeySequence::MoveToPreviousPage)) emit gotoPrevRequested(); else if (keyEvent->matches(QKeySequence::MoveToNextPage)) emit gotoNextRequested(); else if (keyEvent->matches(QKeySequence::Undo)) emit undoRequested(); else if (keyEvent->matches(QKeySequence::Redo)) emit redoRequested(); else if (keyEvent->matches(QKeySequence::Find)) emit findRequested(); else if (keyEvent->matches(QKeySequence::FindNext)) emit findNextRequested(); else if (keyEvent->matches(QKeySequence::Replace)) emit replaceRequested(); else if (keyEvent->modifiers() == (Qt::AltModifier | Qt::ControlModifier)) { if (keyEvent->key() == Qt::Key_Home) emit gotoFirstRequested(); else if (keyEvent->key() == Qt::Key_End) emit gotoLastRequested(); } else if (keyEvent->matches(QKeySequence::MoveToNextLine) || keyEvent->matches(QKeySequence::MoveToPreviousLine)) { //static QTime lastUpDownPress; //if (lastUpDownPress.msecsTo(QTime::currentTime())<500) { keyEvent->setAccepted(true); bool up = keyEvent->key() == Qt::Key_Up; QTextCursor c = textCursor(); if (!c.movePosition(up ? QTextCursor::Up : QTextCursor::Down)) { QTextCursor::MoveOperation op; if (up && !c.atStart()) op = QTextCursor::Start; else if (!up && !c.atEnd()) op = QTextCursor::End; else if (up) { emit gotoPrevRequested(); op = QTextCursor::End; } else { emit gotoNextRequested(); op = QTextCursor::Start; } c.movePosition(op); } setTextCursor(c); } //lastUpDownPress=QTime::currentTime(); } else if (m_part == DocPosition::Source) return KTextEdit::keyPressEvent(keyEvent); //BEGIN GENERAL // ALT+123 feature TODO this is general so should be on another level else if ((keyEvent->modifiers()&Qt::AltModifier) && !keyEvent->text().isEmpty() && keyEvent->text().at(0).isDigit()) { QString text = keyEvent->text(); while (!text.isEmpty() && text.at(0).isDigit()) { m_currentUnicodeNumber = 10 * m_currentUnicodeNumber + (text.at(0).digitValue()); text.remove(0, 1); } KTextEdit::keyPressEvent(keyEvent); } //END GENERAL else if (!keyEvent->modifiers() && (keyEvent->key() == Qt::Key_Backspace || keyEvent->key() == Qt::Key_Delete)) { //only for cases when: //-BkSpace was hit and cursor was atStart //-Del was hit and cursor was atEnd if (Q_UNLIKELY(!m_catalog->isApproved(m_currentPos.entry) && !textCursor().hasSelection()) && ((textCursor().atStart() && keyEvent->key() == Qt::Key_Backspace) || (textCursor().atEnd() && keyEvent->key() == Qt::Key_Delete))) requestToggleApprovement(); else KTextEdit::keyPressEvent(keyEvent); } else if (keyEvent->key() == Qt::Key_Space && (keyEvent->modifiers()&Qt::AltModifier)) insertPlainTextWithCursorCheck(QChar(0x00a0U)); else if (keyEvent->key() == Qt::Key_Minus && (keyEvent->modifiers()&Qt::AltModifier)) insertPlainTextWithCursorCheck(QChar(0x0000AD)); //BEGIN clever editing else if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) { if (m_completionBox && m_completionBox->isVisible()) { if (m_completionBox->currentItem()) completionActivated(m_completionBox->currentItem()->text()); else qCWarning(LOKALIZE_LOG) << "avoided a crash. a case for bug 238835!"; m_completionBox->hide(); return; } if (m_catalog->type() != Gettext) return KTextEdit::keyPressEvent(keyEvent); QString str = toPlainText(); QTextCursor t = textCursor(); int pos = t.position(); QString ins; if (keyEvent->modifiers()&Qt::ShiftModifier) { if (pos > 0 && !str.isEmpty() && str.at(pos - 1) == QLatin1Char('\\') && !isMasked(str, pos - 1)) { ins = 'n'; } else { ins = QStringLiteral("\\n"); } } else if (!(keyEvent->modifiers()&Qt::ControlModifier)) { if (m_langUsesSpaces && pos > 0 && !str.isEmpty() && !str.at(pos - 1).isSpace()) { if (str.at(pos - 1) == QLatin1Char('\\') && !isMasked(str, pos - 1)) ins = QLatin1Char('\\'); // if there is no new line at the end if (pos < 2 || str.midRef(pos - 2, 2) != QLatin1String("\\n")) ins += QLatin1Char(' '); } else if (str.isEmpty()) { ins = QStringLiteral("\\n"); } } if (!str.isEmpty()) { ins += '\n'; insertPlainTextWithCursorCheck(ins); } else KTextEdit::keyPressEvent(keyEvent); } else if (m_catalog->type() != Gettext) KTextEdit::keyPressEvent(keyEvent); else if ((keyEvent->modifiers()&Qt::ControlModifier) ? (keyEvent->key() == Qt::Key_D) : (keyEvent->key() == Qt::Key_Delete) && textCursor().atEnd()) { qCWarning(LOKALIZE_LOG) << "workaround for Qt/X11 bug"; QTextCursor t = textCursor(); if (!t.hasSelection()) { int pos = t.position(); QString str = toPlainText(); //workaround for Qt/X11 bug: if Del on NumPad is pressed, then pos is beyond end if (pos == str.size()) --pos; if (!str.isEmpty() && str.at(pos) == '\\' && !isMasked(str, pos) && pos < str.length() - 1 && spclChars.contains(str.at(pos + 1))) { t.deleteChar(); } } t.deleteChar(); setTextCursor(t); } else if ((!keyEvent->modifiers() && keyEvent->key() == Qt::Key_Backspace) || ((keyEvent->modifiers() & Qt::ControlModifier) && keyEvent->key() == Qt::Key_H)) { QTextCursor t = textCursor(); if (!t.hasSelection()) { int pos = t.position(); QString str = toPlainText(); if (!str.isEmpty() && pos > 0 && spclChars.contains(str.at(pos - 1))) { if (pos > 1 && str.at(pos - 2) == QLatin1Char('\\') && !isMasked(str, pos - 2)) { t.deletePreviousChar(); t.deletePreviousChar(); setTextCursor(t); //qCWarning(LOKALIZE_LOG)<<"set-->"<key() == Qt::Key_Tab) insertPlainTextWithCursorCheck(QStringLiteral("\\t")); else KTextEdit::keyPressEvent(keyEvent); //END clever editing } void TranslationUnitTextEdit::keyReleaseEvent(QKeyEvent * e) { if ((e->key() == Qt::Key_Alt) && m_currentUnicodeNumber >= 32) { insertPlainTextWithCursorCheck(QChar(m_currentUnicodeNumber)); m_currentUnicodeNumber = 0; } else KTextEdit::keyReleaseEvent(e); } void TranslationUnitTextEdit::insertPlainTextWithCursorCheck(const QString & text) { insertPlainText(text); KTextEdit::ensureCursorVisible(); } QString TranslationUnitTextEdit::toPlainText() { QTextCursor cursor = textCursor(); cursor.select(QTextCursor::Document); QString text = cursor.selectedText(); text.replace(QChar(8233), '\n'); /* int ii=text.size(); while(--ii>=0) qCWarning(LOKALIZE_LOG)<push(new InsTagCmd(m_catalog, currentPos(), tag)); showPos(currentPos(), CatalogString(),/*keepCursor*/true); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, tag.end + 1 + tag.isPaired()); setFocus(); } int TranslationUnitTextEdit::strForMicePosIfUnderTag(QPoint mice, CatalogString & str, bool tryHarder) { if (m_currentPos.entry == -1) return -1; QTextCursor cursor = cursorForPosition(mice); int pos = cursor.position(); str = m_catalog->catalogString(m_currentPos); if (pos == -1 || pos >= str.string.size()) return -1; //qCWarning(LOKALIZE_LOG)<<"here1"<0) // { // cursor.movePosition(QTextCursor::Left); // mice.setX(mice.x()+cursorRect(cursor).width()/2); // pos=cursorForPosition(mice).position(); // } if (str.string.at(pos) != TAGRANGE_IMAGE_SYMBOL) { bool cont = tryHarder && --pos >= 0 && str.string.at(pos) == TAGRANGE_IMAGE_SYMBOL; if (!cont) return -1; } int result = str.tags.size(); while (--result >= 0 && str.tags.at(result).start != pos && str.tags.at(result).end != pos) ; return result; } void TranslationUnitTextEdit::mouseReleaseEvent(QMouseEvent * event) { if (event->button() == Qt::LeftButton) { CatalogString str; int pos = strForMicePosIfUnderTag(event->pos(), str); if (pos != -1 && m_part == DocPosition::Source) { emit tagInsertRequested(str.tags.at(pos)); event->accept(); return; } } KTextEdit::mouseReleaseEvent(event); } void TranslationUnitTextEdit::contextMenuEvent(QContextMenuEvent * event) { CatalogString str; int pos = strForMicePosIfUnderTag(event->pos(), str); if (pos != -1) { QString xid = str.tags.at(pos).xid; if (!xid.isEmpty()) { QMenu menu; int entry = m_catalog->unitById(xid); /* QAction* findUnit=menu.addAction(entry>=m_catalog->numberOfEntries()? i18nc("@action:inmenu","Show the binary unit"): i18nc("@action:inmenu","Go to the referenced entry")); */ QAction* result = menu.exec(event->globalPos()); if (result) { if (entry >= m_catalog->numberOfEntries()) emit binaryUnitSelectRequested(xid); else emit gotoEntryRequested(DocPosition(entry)); event->accept(); } return; } } if (textCursor().hasSelection() && m_part == DocPosition::Target) { QMenu menu; menu.addAction(i18nc("@action:inmenu", "Lookup selected text in translation memory")); if (menu.exec(event->globalPos())) emit tmLookupRequested(m_part, textCursor().selectedText()); return; } if (m_part != DocPosition::Source && m_part != DocPosition::Target) return; KTextEdit::contextMenuEvent(event); #if 0 QTextCursor wordSelectCursor = cursorForPosition(event->pos()); wordSelectCursor.select(QTextCursor::WordUnderCursor); if (m_highlighter->isWordMisspelled(wordSelectCursor.selectedText())) { QMenu menu; QMenu suggestions; foreach (const QString& s, m_highlighter->suggestionsForWord(wordSelectCursor.selectedText())) suggestions.addAction(s); if (!suggestions.isEmpty()) { QAction* answer = suggestions.exec(event->globalPos()); if (answer) { m_catalog->beginMacro(i18nc("@item Undo action item", "Replace text")); wordSelectCursor.insertText(answer->text()); m_catalog->endMacro(); } } } #endif // QMenu menu; // QAction* spellchecking=menu.addAction(); // event->accept(); } void TranslationUnitTextEdit::zoomRequestedSlot(qreal fontSize) { QFont curFont = font(); curFont.setPointSizeF(fontSize); setFont(curFont); } void TranslationUnitTextEdit::wheelEvent(QWheelEvent * event) { //Override default KTextEdit behavior which ignores Ctrl+wheelEvent when the field is not ReadOnly (i/o zooming) if (m_part == DocPosition::Target && !Settings::mouseWheelGo() && (event->modifiers() == Qt::ControlModifier)) { float delta = event->angleDelta().y() / 120.f; zoomInF(delta); //Also zoom in the source emit zoomRequested(font().pointSizeF()); return; } if (m_part == DocPosition::Source || !Settings::mouseWheelGo()) { if (event->modifiers() == Qt::ControlModifier) { float delta = event->angleDelta().y() / 120.f; zoomInF(delta); //Also zoom in the target emit zoomRequested(font().pointSizeF()); return; } return KTextEdit::wheelEvent(event); } switch (event->modifiers()) { case Qt::ControlModifier: if (event->angleDelta().y() > 0) emit gotoPrevFuzzyRequested(); else emit gotoNextFuzzyRequested(); break; case Qt::AltModifier: if (event->angleDelta().y() > 0) emit gotoPrevUntranslatedRequested(); else emit gotoNextUntranslatedRequested(); break; case Qt::ControlModifier + Qt::ShiftModifier: if (event->angleDelta().y() > 0) emit gotoPrevFuzzyUntrRequested(); else emit gotoNextFuzzyUntrRequested(); break; case Qt::ShiftModifier: return KTextEdit::wheelEvent(event); default: if (event->angleDelta().y() > 0) emit gotoPrevRequested(); else emit gotoNextRequested(); } } void TranslationUnitTextEdit::spellReplace() { QTextCursor wordSelectCursor = textCursor(); wordSelectCursor.select(QTextCursor::WordUnderCursor); if (!m_highlighter->isWordMisspelled(wordSelectCursor.selectedText())) return; const QStringList& suggestions = m_highlighter->suggestionsForWord(wordSelectCursor.selectedText()); if (suggestions.isEmpty()) return; m_catalog->beginMacro(i18nc("@item Undo action item", "Replace text")); wordSelectCursor.insertText(suggestions.first()); m_catalog->endMacro(); } bool TranslationUnitTextEdit::event(QEvent * event) { #ifdef Q_OS_MAC if (event->type() == QEvent::InputMethod) { QInputMethodEvent* e = static_cast(event); insertPlainTextWithCursorCheck(e->commitString()); e->accept(); return true; } #endif if (event->type() == QEvent::ToolTip) { QHelpEvent *helpEvent = static_cast(event); CatalogString str; int pos = strForMicePosIfUnderTag(helpEvent->pos(), str, true); if (pos != -1) { QString tooltip = str.tags.at(pos).displayName(); QToolTip::showText(helpEvent->globalPos(), tooltip); return true; } QString tip; QString langCode = m_highlighter->currentLanguage(); //qCWarning(LOKALIZE_LOG) << "Spellchecker found "<spellCheckerFound()<< " as "<currentLanguage(); bool nospell = langCode.isEmpty(); if (nospell) langCode = m_part == DocPosition::Source ? m_catalog->sourceLangCode() : m_catalog->targetLangCode(); QLocale l(langCode); if (l.language() != QLocale::C) tip = l.nativeLanguageName() + QLatin1String(" ("); tip += langCode; if (l.language() != QLocale::C) tip += ')'; if (nospell) tip += QLatin1String(" - ") + i18n("no spellcheck available"); QToolTip::showText(helpEvent->globalPos(), tip); } return KTextEdit::event(event); } void TranslationUnitTextEdit::slotLanguageToolFinished(const QString &result) { LanguageToolParser parser; const QJsonDocument doc = QJsonDocument::fromJson(result.toUtf8()); const QJsonObject fields = doc.object(); emit languageToolChanged(parser.parseResult(fields, toPlainText())); } void TranslationUnitTextEdit::slotLanguageToolError(const QString &str) { emit languageToolChanged(i18n("An error was reported: %1", str)); } void TranslationUnitTextEdit::launchLanguageTool() { if (toPlainText().length() == 0) return; + LanguageToolResultJob *job = new LanguageToolResultJob(this); job->setUrl(LanguageToolManager::self()->languageToolCheckPath()); job->setNetworkAccessManager(LanguageToolManager::self()->networkAccessManager()); - job->setText(toPlainText()); + job->setText(toPlainText().toHtmlEscaped().replace(QStringLiteral("%"), QStringLiteral("%25"))); job->setLanguage(m_catalog->targetLangCode()); connect(job, &LanguageToolResultJob::finished, this, &TranslationUnitTextEdit::slotLanguageToolFinished); connect(job, &LanguageToolResultJob::error, this, &TranslationUnitTextEdit::slotLanguageToolError); job->start(); } void TranslationUnitTextEdit::tagMenu() { doTag(false); } void TranslationUnitTextEdit::tagImmediate() { doTag(true); } void TranslationUnitTextEdit::doTag(bool immediate) { QMenu menu; QAction* txt = 0; CatalogString sourceWithTags = m_catalog->sourceWithTags(m_currentPos); int count = sourceWithTags.tags.size(); if (count) { QMap tagIdToIndex = m_catalog->targetWithTags(m_currentPos).tagIdToIndex(); bool hasActive = false; for (int i = 0; i < count; ++i) { //txt=menu.addAction(sourceWithTags.ranges.at(i)); txt = menu.addAction(QString::number(i)/*+" "+sourceWithTags.ranges.at(i).id*/); txt->setData(QVariant(i)); if (!hasActive && !tagIdToIndex.contains(sourceWithTags.tags.at(i).id)) { if (immediate) { insertTag(sourceWithTags.tags.at(txt->data().toInt())); return; } hasActive = true; menu.setActiveAction(txt); } } if (immediate) return; txt = menu.exec(mapToGlobal(cursorRect().bottomRight())); if (!txt) return; insertTag(sourceWithTags.tags.at(txt->data().toInt())); } else { if (Q_UNLIKELY(Project::instance()->markup().isEmpty())) return; //QRegExp tag("(<[^>]*>)+|\\&\\w+\\;"); QRegExp tag(Project::instance()->markup()); tag.setMinimal(true); QString en = m_catalog->sourceWithTags(m_currentPos).string; QString target(toPlainText()); en.remove('\n'); target.remove('\n'); int pos = 0; //tag.indexIn(en); int posInMsgStr = 0; while ((pos = tag.indexIn(en, pos)) != -1) { /* QString str(tag.cap(0)); str.replace("&","&&");*/ txt = menu.addAction(tag.cap(0)); pos += tag.matchedLength(); if (posInMsgStr != -1 && (posInMsgStr = target.indexOf(tag.cap(0), posInMsgStr)) == -1) { if (immediate) { insertPlainTextWithCursorCheck(txt->text()); return; } menu.setActiveAction(txt); } else if (posInMsgStr != -1) posInMsgStr += tag.matchedLength(); } if (!txt || immediate) return; //txt=menu.exec(_msgidEdit->mapToGlobal(QPoint(0,0))); txt = menu.exec(mapToGlobal(cursorRect().bottomRight())); if (txt) insertPlainTextWithCursorCheck(txt->text()); } } void TranslationUnitTextEdit::source2target() { CatalogString sourceWithTags = m_catalog->sourceWithTags(m_currentPos); QString text = sourceWithTags.string; QString out; QString ctxt = m_catalog->context(m_currentPos.entry).first(); QRegExp delimiter(QStringLiteral("\\s*,\\s*")); //TODO ask for the fillment if the first time. //BEGIN KDE specific part if (ctxt.startsWith(QLatin1String("NAME OF TRANSLATORS")) || text.startsWith(QLatin1String("_: NAME OF TRANSLATORS\\n"))) { if (!document()->toPlainText().split(delimiter).contains(Settings::authorLocalizedName())) { if (!document()->isEmpty()) out = QLatin1String(", "); out += Settings::authorLocalizedName(); } } else if (ctxt.startsWith(QLatin1String("EMAIL OF TRANSLATORS")) || text.startsWith(QLatin1String("_: EMAIL OF TRANSLATORS\\n"))) { if (!document()->toPlainText().split(delimiter).contains(Settings::authorEmail())) { if (!document()->isEmpty()) out = QLatin1String(", "); out += Settings::authorEmail(); } } else if (/*_catalog->isGeneratedFromDocbook() &&*/ text.startsWith(QLatin1String("ROLES_OF_TRANSLATORS"))) { if (!document()->isEmpty()) out = '\n'; out += QLatin1String("\n" "\n" "
") + Settings::authorEmail() + QLatin1String("
\n" "
"); } else if (text.startsWith(QLatin1String("CREDIT_FOR_TRANSLATORS"))) { if (!document()->isEmpty()) out = '\n'; out += QLatin1String("") + Settings::authorLocalizedName() + '\n' + QLatin1String("") + Settings::authorEmail() + QLatin1String(""); } //END KDE specific part else { m_catalog->beginMacro(i18nc("@item Undo action item", "Copy source to target")); removeTargetSubstring(0, -1,/*refresh*/false); insertCatalogString(sourceWithTags, 0,/*refresh*/false); m_catalog->endMacro(); showPos(m_currentPos, sourceWithTags,/*keepCursor*/false); requestToggleApprovement(); } if (!out.isEmpty()) { QTextCursor t = textCursor(); t.movePosition(QTextCursor::End); t.insertText(out); setTextCursor(t); } } void TranslationUnitTextEdit::requestToggleApprovement() { if (m_catalog->isApproved(m_currentPos.entry) || !Settings::autoApprove()) return; bool skip = m_catalog->isPlural(m_currentPos); if (skip) { skip = false; DocPos pos(m_currentPos); for (pos.form = 0; pos.form < m_catalog->numberOfPluralForms(); ++(pos.form)) skip = skip || !m_catalog->isModified(pos); } if (!skip) emit toggleApprovementRequested(); } void TranslationUnitTextEdit::cursorToStart() { QTextCursor t = textCursor(); t.movePosition(QTextCursor::Start); setTextCursor(t); } void TranslationUnitTextEdit::doCompletion(int pos) { QString target = m_catalog->targetWithTags(m_currentPos).string; int sp = target.lastIndexOf(CompletionStorage::instance()->rxSplit, pos - 1); int len = (pos - sp) - 1; QStringList s = CompletionStorage::instance()->makeCompletion(QString::fromRawData(target.unicode() + sp + 1, len)); if (!m_completionBox) { //BEGIN creation m_completionBox = new MyCompletionBox(this); connect(m_completionBox, &MyCompletionBox::activated, this, &TranslationUnitTextEdit::completionActivated); m_completionBox->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); //END creation } m_completionBox->setItems(s); if (s.size() && !s.first().isEmpty()) { m_completionBox->setCurrentRow(0); //qApp->removeEventFilter( m_completionBox ); if (!m_completionBox->isVisible()) //NOTE remove the check if kdelibs gets adapted m_completionBox->show(); m_completionBox->resize(m_completionBox->sizeHint()); QPoint p = cursorRect().bottomRight(); if (p.x() < 10) //workaround Qt bug p.rx() += textCursor().verticalMovementX() + QFontMetrics(currentFont()).horizontalAdvance('W'); m_completionBox->move(viewport()->mapToGlobal(p)); } else m_completionBox->hide(); } void TranslationUnitTextEdit::doExplicitCompletion() { doCompletion(textCursor().anchor()); } void TranslationUnitTextEdit::completionActivated(const QString & semiWord) { QTextCursor cursor = textCursor(); cursor.insertText(semiWord); setTextCursor(cursor); }