Warning, file /sdk/lokalize/src/alttransview.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002   This file is part of Lokalize
0003 
0004   SPDX-FileCopyrightText: 2007-2014 Nick Shaforostoff <shafff@ukr.net>
0005   SPDX-FileCopyrightText: 2018-2019 Simon Depiets <sdepiets@gmail.com>
0006 
0007   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0008 */
0009 
0010 #include "alttransview.h"
0011 
0012 #include "lokalize_debug.h"
0013 
0014 #include "diff.h"
0015 #include "catalog.h"
0016 #include "cmd.h"
0017 #include "project.h"
0018 #include "xlifftextedit.h"
0019 #include "tmview.h" //TextBrowser
0020 #include "mergecatalog.h"
0021 #include "prefs_lokalize.h"
0022 
0023 #include <QStringBuilder>
0024 #include <QDragEnterEvent>
0025 #include <QMimeData>
0026 #include <QFileInfo>
0027 #include <QDir>
0028 #include <QToolTip>
0029 #include <QAction>
0030 
0031 #include <KLocalizedString>
0032 #include <KMessageBox>
0033 
0034 AltTransView::AltTransView(QWidget* parent, Catalog* catalog, const QVector<QAction*>& actions)
0035     : QDockWidget(i18nc("@title:window", "Alternate Translations"), parent)
0036     , m_browser(new TM::TextBrowser(this))
0037     , m_catalog(catalog)
0038     , m_normTitle(i18nc("@title:window", "Alternate Translations"))
0039     , m_hasInfoTitle(m_normTitle + QStringLiteral(" [*]"))
0040     , m_hasInfo(false)
0041     , m_everShown(false)
0042     , m_actions(actions)
0043 {
0044     setObjectName(QStringLiteral("msgIdDiff"));
0045     setWidget(m_browser);
0046     hide();
0047 
0048     m_browser->setReadOnly(true);
0049     m_browser->viewport()->setBackgroundRole(QPalette::Window);
0050     QTimer::singleShot(0, this, &AltTransView::initLater);
0051 }
0052 
0053 void AltTransView::initLater()
0054 {
0055     setAcceptDrops(true);
0056 
0057     KConfig config;
0058     KConfigGroup group(&config, "AltTransView");
0059     m_everShown = group.readEntry("EverShown", false);
0060 
0061 
0062     int i = m_actions.size();
0063     while (--i >= 0) {
0064         connect(m_actions.at(i), &QAction::triggered, this, [this, i] { slotUseSuggestion(i); });
0065     }
0066 
0067     connect(m_browser, &TM::TextBrowser::textInsertRequested, this, &AltTransView::textInsertRequested);
0068     //connect(m_browser, &TM::TextBrowser::customContextMenuRequested, this, &AltTransView::contextMenu);
0069 }
0070 
0071 AltTransView::~AltTransView()
0072 {
0073 }
0074 
0075 void AltTransView::dragEnterEvent(QDragEnterEvent* event)
0076 {
0077     if (event->mimeData()->hasUrls() && Catalog::extIsSupported(event->mimeData()->urls().first().path()))
0078         event->acceptProposedAction();
0079 }
0080 
0081 void AltTransView::dropEvent(QDropEvent *event)
0082 {
0083     event->acceptProposedAction();
0084     attachAltTransFile(event->mimeData()->urls().first().toLocalFile());
0085 
0086     //update
0087     m_prevEntry.entry = -1;
0088     QTimer::singleShot(0, this, &AltTransView::process);
0089 }
0090 
0091 void AltTransView::attachAltTransFile(const QString& path)
0092 {
0093     MergeCatalog* altCat = new MergeCatalog(m_catalog, m_catalog, /*saveChanges*/false);
0094     altCat->loadFromUrl(path);
0095     m_catalog->attachAltTransCatalog(altCat);
0096 }
0097 
0098 void AltTransView::addAlternateTranslation(int entry, const QString& trans)
0099 {
0100     AltTrans altTrans;
0101     altTrans.target = trans;
0102     m_catalog->attachAltTrans(entry, altTrans);
0103 
0104     m_prevEntry = DocPos();
0105     QTimer::singleShot(0, this, &AltTransView::process);
0106 }
0107 
0108 void AltTransView::fileLoaded()
0109 {
0110     m_prevEntry.entry = -1;
0111     QString absPath = m_catalog->url();
0112     QString relPath = QDir(Project::instance()->poDir()).relativeFilePath(absPath);
0113 
0114     QFileInfo info(Project::instance()->altTransDir() % '/' % relPath);
0115     if (info.canonicalFilePath() != absPath && info.exists())
0116         attachAltTransFile(info.canonicalFilePath());
0117     else
0118         qCWarning(LOKALIZE_LOG) << "alt trans file doesn't exist:" << Project::instance()->altTransDir() % '/' % relPath;
0119 }
0120 
0121 void AltTransView::slotNewEntryDisplayed(const DocPosition& pos)
0122 {
0123     m_entry = DocPos(pos);
0124     QTimer::singleShot(0, this, &AltTransView::process);
0125 }
0126 
0127 void AltTransView::process()
0128 {
0129     if (m_entry == m_prevEntry) return;
0130     if (m_catalog->numberOfEntries() <= m_entry.entry)
0131         return;//because of Qt::QueuedConnection
0132 
0133     m_prevEntry = m_entry;
0134     m_browser->clear();
0135     m_entryPositions.clear();
0136 
0137     const QVector<AltTrans>& entries = m_catalog->altTrans(m_entry.toDocPosition());
0138     m_entries = entries;
0139 
0140     if (entries.isEmpty()) {
0141         if (m_hasInfo) {
0142             m_hasInfo = false;
0143             setWindowTitle(m_normTitle);
0144         }
0145         return;
0146     }
0147     if (!m_hasInfo) {
0148         m_hasInfo = true;
0149         setWindowTitle(m_hasInfoTitle);
0150     }
0151     if (!isVisible() && !Settings::altTransViewEverShownWithData()) {
0152         if (KMessageBox::questionYesNo(this, i18n("There is useful data available in Alternate Translations view.\n\n"
0153                                        "For Gettext PO files it displays difference between current source text "
0154                                        "and the source text corresponding to the fuzzy translation found by msgmerge when updating PO based on POT template.\n\n"
0155                                        "Do you want to show the view with the data?"), m_normTitle) == KMessageBox::Yes)
0156             show();
0157 
0158         Settings::setAltTransViewEverShownWithData(true);
0159     }
0160 
0161     CatalogString source = m_catalog->sourceWithTags(m_entry.toDocPosition());
0162     QString context = m_catalog->context(m_entry.toDocPosition()).first();
0163     QString contextWithNewline = context + (context.isEmpty() ? "" : "\n");
0164 
0165     QTextBlockFormat blockFormatBase;
0166     QTextBlockFormat blockFormatAlternate; blockFormatAlternate.setBackground(QPalette().alternateBase());
0167     QTextCharFormat noncloseMatchCharFormat;
0168     QTextCharFormat closeMatchCharFormat;  closeMatchCharFormat.setFontWeight(QFont::Bold);
0169     int i = 0;
0170     int limit = entries.size();
0171     while (true) {
0172         const AltTrans& entry = entries.at(i);
0173 
0174         QTextCursor cur = m_browser->textCursor();
0175         QString html;
0176         html.reserve(1024);
0177         if (!entry.source.isEmpty()) {
0178             html += QStringLiteral("<p>");
0179 
0180             QString result = userVisibleWordDiff(entry.source.string, contextWithNewline + source.string, Project::instance()->accel(), Project::instance()->markup()).toHtmlEscaped();
0181             //result.replace("&","&amp;");
0182             //result.replace("<","&lt;");
0183             //result.replace(">","&gt;");
0184             result.replace(QStringLiteral("{KBABELADD}"), QStringLiteral("<font style=\"background-color:") % Settings::addColor().name() % QStringLiteral(";color:black\">"));
0185             result.replace(QStringLiteral("{/KBABELADD}"), QStringLiteral("</font>"));
0186             result.replace(QStringLiteral("{KBABELDEL}"), QStringLiteral("<font style=\"background-color:") % Settings::delColor().name() % QStringLiteral(";color:black\">"));
0187             result.replace(QStringLiteral("{/KBABELDEL}"), QStringLiteral("</font>"));
0188             result.replace(QStringLiteral("\\n"), QStringLiteral("\\n<br>"));
0189 
0190             html += result;
0191             html += QStringLiteral("<br>");
0192             cur.insertHtml(html); html.clear();
0193         }
0194         if (!entry.target.isEmpty()) {
0195             if (Q_LIKELY(i < m_actions.size())) {
0196                 m_actions.at(i)->setStatusTip(entry.target.string);
0197                 html += QString(QStringLiteral("[%1] ")).arg(m_actions.at(i)->shortcut().toString(QKeySequence::NativeText));
0198             } else
0199                 html += QStringLiteral("[ - ] ");
0200 
0201             cur.insertText(html); html.clear();
0202             insertContent(cur, entry.target);
0203         }
0204         m_entryPositions.insert(cur.anchor(), i);
0205 
0206         html += i ? QStringLiteral("<br></p>") : QStringLiteral("</p>");
0207         cur.insertHtml(html);
0208 
0209         if (Q_UNLIKELY(++i >= limit))
0210             break;
0211 
0212         cur.insertBlock(i % 2 ? blockFormatAlternate : blockFormatBase);
0213     }
0214 
0215     if (!m_everShown) {
0216         m_everShown = true;
0217         show();
0218 
0219         KConfig config;
0220         KConfigGroup group(&config, "AltTransView");
0221         group.writeEntry("EverShown", true);
0222     }
0223 }
0224 
0225 
0226 bool AltTransView::event(QEvent *event)
0227 {
0228     if (event->type() == QEvent::ToolTip) {
0229         QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
0230 
0231         if (m_entryPositions.isEmpty()) {
0232             QString tooltip = i18nc("@info:tooltip", "<p>Sometimes, if source text is changed, its translation becomes deprecated and is either marked as <emphasis>needing&nbsp;review</emphasis> (i.e. looses approval status), "
0233                                     "or (only in case of XLIFF file) moved to the <emphasis>alternate&nbsp;translations</emphasis> section accompanying the unit.</p>"
0234                                     "<p>This toolview also shows the difference between current source string and the previous source string, so that you can easily see which changes should be applied to existing translation to make it reflect current source.</p>"
0235                                     "<p>Double-clicking any word in this toolview inserts it into translation.</p>"
0236                                     "<p>Drop translation file onto this toolview to use it as a source for additional alternate translations.</p>"
0237                                    );
0238             QToolTip::showText(helpEvent->globalPos(), tooltip);
0239             return true;
0240         }
0241 
0242         int block1 = m_browser->cursorForPosition(m_browser->viewport()->mapFromGlobal(helpEvent->globalPos())).blockNumber();
0243         int block = *m_entryPositions.lowerBound(m_browser->cursorForPosition(m_browser->viewport()->mapFromGlobal(helpEvent->globalPos())).anchor());
0244         if (block1 != block)
0245             qCWarning(LOKALIZE_LOG) << "block numbers don't match";
0246         if (block >= m_entries.size())
0247             return false;
0248 
0249         QString origin = m_entries.at(block).origin;
0250         if (origin.isEmpty())
0251             return false;
0252 
0253         QString tooltip = i18nc("@info:tooltip", "Origin: %1", origin);
0254         QToolTip::showText(helpEvent->globalPos(), tooltip);
0255         return true;
0256     }
0257     return QDockWidget::event(event);
0258 }
0259 
0260 void AltTransView::slotUseSuggestion(int i)
0261 {
0262     if (Q_UNLIKELY(i >= m_entries.size()))
0263         return;
0264 
0265     TM::TMEntry tmEntry;
0266     tmEntry.target = m_entries.at(i).target;
0267     CatalogString source = m_catalog->sourceWithTags(m_entry.toDocPosition());
0268     tmEntry.diff = userVisibleWordDiff(m_entries.at(i).source.string, source.string, Project::instance()->accel(), Project::instance()->markup());
0269 
0270     CatalogString target = TM::targetAdapted(tmEntry, source);
0271 
0272     qCWarning(LOKALIZE_LOG) << "0" << target.string;
0273     if (Q_UNLIKELY(target.isEmpty()))
0274         return;
0275 
0276     m_catalog->beginMacro(i18nc("@item Undo action", "Use alternate translation"));
0277 
0278     QString old = m_catalog->targetWithTags(m_entry.toDocPosition()).string;
0279     if (!old.isEmpty()) {
0280         //FIXME test!
0281         removeTargetSubstring(m_catalog, m_entry.toDocPosition(), 0, old.size());
0282         //m_catalog->push(new DelTextCmd(m_catalog,m_pos,m_catalog->msgstr(m_pos)));
0283     }
0284     qCWarning(LOKALIZE_LOG) << "1" << target.string;
0285 
0286     //m_catalog->push(new InsTextCmd(m_catalog,m_pos,target)/*,true*/);
0287     insertCatalogString(m_catalog, m_entry.toDocPosition(), target, 0);
0288 
0289     m_catalog->endMacro();
0290 
0291     Q_EMIT refreshRequested();
0292 }
0293 
0294