Warning, file /sdk/lokalize/src/editorview.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 (some bits of KBabel code were reused)
0003 
0004   SPDX-FileCopyrightText: 2007-2014 Nick Shaforostoff <shafff@ukr.net>
0005   SPDX-FileCopyrightText: 1999-2000 Matthias Kiefer <matthias.kiefer@gmx.de>
0006   SPDX-FileCopyrightText: 2001-2004 Stanislav Visnovsky <visnovsky@kde.org>
0007   SPDX-FileCopyrightText: 2018-2019 Simon Depiets <sdepiets@gmail.com>
0008 
0009   SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-Qt-Commercial-exception-1.0
0010 */
0011 
0012 #include "editorview.h"
0013 
0014 #include "lokalize_debug.h"
0015 
0016 #include "xlifftextedit.h"
0017 #include "project.h"
0018 #include "catalog.h"
0019 #include "cmd.h"
0020 #include "prefs_lokalize.h"
0021 #include "prefs.h"
0022 
0023 #include <QTimer>
0024 #include <QMenu>
0025 #include <QDragEnterEvent>
0026 
0027 #include <QLabel>
0028 #include <QHBoxLayout>
0029 #include <QTabBar>
0030 #include <QStringBuilder>
0031 
0032 #include <klocalizedstring.h>
0033 #include <kmessagebox.h>
0034 #include <kled.h>
0035 #include <kstandardshortcut.h>
0036 #include <kcolorscheme.h>
0037 
0038 //parent is set on qsplitter insertion
0039 LedsWidget::LedsWidget(QWidget* parent): QWidget(parent)
0040 {
0041     KColorScheme colorScheme(QPalette::Normal);
0042 
0043     QHBoxLayout* layout = new QHBoxLayout(this);
0044     layout->addStretch();
0045     layout->addWidget(new QLabel(i18nc("@label whether entry is fuzzy", "Not ready:"), this));
0046     layout->addWidget(ledFuzzy = new KLed(colorScheme.foreground(KColorScheme::NeutralText).color()/*Qt::green*/, KLed::Off, KLed::Sunken, KLed::Rectangular));
0047     layout->addWidget(new QLabel(i18nc("@label whether entry is untranslated", "Untranslated:"), this));
0048     layout->addWidget(ledUntr = new KLed(colorScheme.foreground(KColorScheme::NegativeText).color()/*Qt::red*/, KLed::Off, KLed::Sunken, KLed::Rectangular));
0049     layout->addSpacing(1);
0050     layout->addWidget(lblColumn = new QLabel(this));
0051     layout->addStretch();
0052     setMaximumHeight(minimumSizeHint().height());
0053 }
0054 
0055 void LedsWidget::contextMenuEvent(QContextMenuEvent* event)
0056 {
0057     QMenu menu;
0058     menu.addAction(i18nc("@action", "Hide"));
0059     if (!menu.exec(event->globalPos()))
0060         return; //NOTE the config doesn't seem to work
0061     Settings::setLeds(false);
0062     SettingsController::instance()->dirty = true;
0063     hide();
0064 }
0065 
0066 void LedsWidget::cursorPositionChanged(int column)
0067 {
0068     lblColumn->setText(i18nc("@info:label cursor position", "Column: %1", column));
0069 }
0070 
0071 
0072 EditorView::EditorView(QWidget *parent, Catalog* catalog/*,keyEventHandler* kh*/)
0073     : QSplitter(Qt::Vertical, parent)
0074     , m_catalog(catalog)
0075     , m_sourceTextEdit(new TranslationUnitTextEdit(catalog, DocPosition::Source, this))
0076     , m_targetTextEdit(new TranslationUnitTextEdit(catalog, DocPosition::Target, this))
0077     , m_pluralTabBar(new QTabBar(this))
0078     , m_leds(nullptr)
0079     , m_modifiedAfterFind(false)
0080 {
0081     m_pluralTabBar->hide();
0082     m_sourceTextEdit->setWhatsThis(i18n("<qt><p><b>Original String</b></p>\n"
0083                                         "<p>This part of the window shows the original message\n"
0084                                         "of the currently displayed entry.</p></qt>"));
0085     m_sourceTextEdit->viewport()->setBackgroundRole(QPalette::Window);
0086 
0087     m_sourceTextEdit->setVisualizeSeparators(Settings::self()->visualizeSeparators());
0088     m_targetTextEdit->setVisualizeSeparators(Settings::self()->visualizeSeparators());
0089 
0090 
0091     connect(m_targetTextEdit, &TranslationUnitTextEdit::contentsModified, this, &EditorView::resetFindForCurrent);
0092     connect(m_targetTextEdit, &TranslationUnitTextEdit::toggleApprovementRequested, this, &EditorView::toggleApprovement);
0093     connect(this, &EditorView::signalApprovedEntryDisplayed, m_targetTextEdit, &TranslationUnitTextEdit::reflectApprovementState);
0094     connect(m_sourceTextEdit, &TranslationUnitTextEdit::tagInsertRequested, m_targetTextEdit, &TranslationUnitTextEdit::insertTag);
0095 
0096     connect(m_sourceTextEdit, &TranslationUnitTextEdit::binaryUnitSelectRequested, this, &EditorView::binaryUnitSelectRequested);
0097     connect(m_targetTextEdit, &TranslationUnitTextEdit::binaryUnitSelectRequested, this, &EditorView::binaryUnitSelectRequested);
0098     connect(m_sourceTextEdit, &TranslationUnitTextEdit::gotoEntryRequested, this, &EditorView::gotoEntryRequested);
0099     connect(m_targetTextEdit, &TranslationUnitTextEdit::gotoEntryRequested, this, &EditorView::gotoEntryRequested);
0100 
0101     connect(m_sourceTextEdit, &TranslationUnitTextEdit::tmLookupRequested, this, &EditorView::tmLookupRequested);
0102     connect(m_targetTextEdit, &TranslationUnitTextEdit::tmLookupRequested, this, &EditorView::tmLookupRequested);
0103 
0104     connect(m_sourceTextEdit, &TranslationUnitTextEdit::findRequested, this, &EditorView::findRequested);
0105     connect(m_targetTextEdit, &TranslationUnitTextEdit::findRequested, this, &EditorView::findRequested);
0106     connect(m_sourceTextEdit, &TranslationUnitTextEdit::findNextRequested, this, &EditorView::findNextRequested);
0107     connect(m_targetTextEdit, &TranslationUnitTextEdit::findNextRequested, this, &EditorView::findNextRequested);
0108     connect(m_sourceTextEdit, &TranslationUnitTextEdit::replaceRequested, this, &EditorView::replaceRequested);
0109     connect(m_targetTextEdit, &TranslationUnitTextEdit::replaceRequested, this, &EditorView::replaceRequested);
0110     connect(m_sourceTextEdit, &TranslationUnitTextEdit::zoomRequested, m_targetTextEdit, &TranslationUnitTextEdit::zoomRequestedSlot);
0111     connect(m_targetTextEdit, &TranslationUnitTextEdit::zoomRequested, m_sourceTextEdit, &TranslationUnitTextEdit::zoomRequestedSlot);
0112 
0113     connect(this, &EditorView::doExplicitCompletion, m_targetTextEdit, &TranslationUnitTextEdit::doExplicitCompletion);
0114 
0115     addWidget(m_pluralTabBar);
0116     addWidget(m_sourceTextEdit);
0117     addWidget(m_targetTextEdit);
0118 
0119     QWidget::setTabOrder(m_targetTextEdit, m_sourceTextEdit);
0120     QWidget::setTabOrder(m_sourceTextEdit, m_targetTextEdit);
0121     setFocusProxy(m_targetTextEdit);
0122 //     QTimer::singleShot(3000,this,SLOT(setupWhatsThis()));
0123     settingsChanged();
0124 }
0125 
0126 EditorView::~EditorView()
0127 {
0128 }
0129 
0130 
0131 void EditorView::resetFindForCurrent(const DocPosition& pos)
0132 {
0133     m_modifiedAfterFind = true;
0134     Q_EMIT signalChanged(pos.entry);
0135 }
0136 
0137 
0138 void EditorView::settingsChanged()
0139 {
0140     //Settings::self()->config()->setGroup("Editor");
0141     m_sourceTextEdit->document()->setDefaultFont(Settings::msgFont());
0142     m_targetTextEdit->document()->setDefaultFont(Settings::msgFont());
0143     m_sourceTextEdit->setVisualizeSeparators(Settings::self()->visualizeSeparators());
0144     m_targetTextEdit->setVisualizeSeparators(Settings::self()->visualizeSeparators());
0145     if (m_leds) m_leds->setVisible(Settings::leds());
0146     else if (Settings::leds()) {
0147         m_leds = new LedsWidget(this);
0148         insertWidget(2, m_leds);
0149         connect(m_targetTextEdit, &TranslationUnitTextEdit::cursorPositionChanged, m_leds, &LedsWidget::cursorPositionChanged);
0150         connect(m_targetTextEdit, &TranslationUnitTextEdit::nonApprovedEntryDisplayed, m_leds->ledFuzzy, &KLed::on);
0151         connect(m_targetTextEdit, &TranslationUnitTextEdit::approvedEntryDisplayed, m_leds->ledFuzzy, &KLed::off);
0152         connect(m_targetTextEdit, &TranslationUnitTextEdit::untranslatedEntryDisplayed, m_leds->ledUntr, &KLed::on);
0153         connect(m_targetTextEdit, &TranslationUnitTextEdit::translatedEntryDisplayed, m_leds->ledUntr, &KLed::off);
0154         m_targetTextEdit->showPos(m_targetTextEdit->currentPos());
0155     }
0156 }
0157 
0158 
0159 
0160 void EditorView::gotoEntry()
0161 {
0162     return gotoEntry(DocPosition(), 0);
0163 }
0164 //main function in this file :)
0165 void EditorView::gotoEntry(DocPosition pos, int selection)
0166 {
0167     setUpdatesEnabled(false);
0168 
0169     bool refresh = (pos.entry == -1);
0170     if (refresh) pos = m_targetTextEdit->currentPos();
0171     //qCWarning(LOKALIZE_LOG)<<"refresh"<<refresh;
0172     //qCWarning(LOKALIZE_LOG)<<"offset"<<pos.offset;
0173     //TODO trigger refresh directly via Catalog signal
0174 
0175     if (Q_UNLIKELY(m_catalog->isPlural(pos.entry))) {
0176         if (Q_UNLIKELY(m_catalog->numberOfPluralForms() != m_pluralTabBar->count())) {
0177             int i = m_pluralTabBar->count();
0178             if (m_catalog->numberOfPluralForms() > m_pluralTabBar->count())
0179                 while (i < m_catalog->numberOfPluralForms())
0180                     m_pluralTabBar->addTab(i18nc("@title:tab", "Plural Form %1", ++i));
0181             else
0182                 while (i > m_catalog->numberOfPluralForms())
0183                     m_pluralTabBar->removeTab(i--);
0184         }
0185         m_pluralTabBar->show();
0186         m_pluralTabBar->blockSignals(true);
0187         m_pluralTabBar->setCurrentIndex(pos.form);
0188         m_pluralTabBar->blockSignals(false);
0189     } else
0190         m_pluralTabBar->hide();
0191 
0192     //bool keepCursor=DocPos(pos)==DocPos(_msgidEdit->currentPos());
0193     bool keepCursor = false;
0194     CatalogString sourceWithTags = m_sourceTextEdit->showPos(pos, CatalogString(), keepCursor);
0195 
0196     //qCWarning(LOKALIZE_LOG)<<"calling showPos";
0197     QString targetString = m_targetTextEdit->showPos(pos, sourceWithTags, keepCursor).string;
0198     //qCWarning(LOKALIZE_LOG)<<"ss"<<_msgstrEdit->textCursor().anchor()<<_msgstrEdit->textCursor().position();
0199     m_sourceTextEdit->cursorToStart();
0200     m_targetTextEdit->cursorToStart();
0201 
0202     bool untrans = targetString.isEmpty();
0203     //qCWarning(LOKALIZE_LOG)<<"ss1"<<_msgstrEdit->textCursor().anchor()<<_msgstrEdit->textCursor().position();
0204 
0205     if (pos.offset || selection) {
0206         TranslationUnitTextEdit* msgEdit = (pos.part == DocPosition::Source ? m_sourceTextEdit : m_targetTextEdit);
0207         QTextCursor t = msgEdit->textCursor();
0208         t.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, pos.offset);
0209         //NOTE this was kinda bug due to on-the-fly msgid wordwrap
0210         if (selection)
0211             t.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, selection);
0212         msgEdit->setTextCursor(t);
0213     } else if (!untrans) {
0214         QTextCursor t = m_targetTextEdit->textCursor();
0215         //what if msg starts with a tag?
0216         if (Q_UNLIKELY(targetString.startsWith('<'))) {
0217             int offset = targetString.indexOf(QRegExp(QStringLiteral(">[^<]")));
0218             if (offset != -1)
0219                 t.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, offset + 1);
0220         } else if (Q_UNLIKELY(targetString.startsWith(TAGRANGE_IMAGE_SYMBOL))) {
0221             int offset = targetString.indexOf(QRegExp(QStringLiteral("[^") + QChar(TAGRANGE_IMAGE_SYMBOL) + ']'));
0222             if (offset != -1)
0223                 t.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, offset + 1);
0224         }
0225         m_targetTextEdit->setTextCursor(t);
0226     }
0227     //qCWarning(LOKALIZE_LOG)<<"set-->"<<_msgstrEdit->textCursor().anchor()<<_msgstrEdit->textCursor().position();
0228     //qCWarning(LOKALIZE_LOG)<<"anchor"<<t.anchor()<<"pos"<<t.position();
0229     m_targetTextEdit->setFocus();
0230     setUpdatesEnabled(true);
0231 }
0232 
0233 
0234 //BEGIN edit actions that are easier to do in this class
0235 void EditorView::unwrap()
0236 {
0237     unwrap(nullptr);
0238 }
0239 void EditorView::unwrap(TranslationUnitTextEdit* editor)
0240 {
0241     if (!editor)
0242         editor = m_targetTextEdit;
0243 
0244     QTextCursor t = editor->document()->find(QRegExp("[^(\\\\n)]$"));
0245     if (t.isNull())
0246         return;
0247 
0248     if (editor == m_targetTextEdit)
0249         m_catalog->beginMacro(i18nc("@item Undo action item", "Unwrap"));
0250     t.movePosition(QTextCursor::EndOfLine);
0251     if (!t.atEnd())
0252         t.deleteChar();
0253 
0254     QRegExp rx("[^(\\\\n)>]$");
0255     //remove '\n's skipping "\\\\n"
0256     while (!(t = editor->document()->find(rx, t)).isNull()) {
0257         t.movePosition(QTextCursor::EndOfLine);
0258         if (!t.atEnd())
0259             t.deleteChar();
0260     }
0261     if (editor == m_targetTextEdit)
0262         m_catalog->endMacro();
0263 }
0264 
0265 void EditorView::insertTerm(const QString& term)
0266 {
0267     m_targetTextEdit->insertPlainTextWithCursorCheck(term);
0268     m_targetTextEdit->setFocus();
0269 }
0270 
0271 
0272 QString EditorView::selectionInTarget() const
0273 {
0274     //TODO remove IMAGES
0275     return m_targetTextEdit->textCursor().selectedText();
0276 }
0277 
0278 QString EditorView::selectionInSource() const
0279 {
0280     //TODO remove IMAGES
0281     return m_sourceTextEdit->textCursor().selectedText();
0282 }
0283 
0284 void EditorView::setProperFocus()
0285 {
0286     m_targetTextEdit->setFocus();
0287 }
0288 
0289 
0290 //END edit actions that are easier to do in this class
0291 
0292 
0293 
0294 QObject* EditorView::viewPort()
0295 {
0296     return m_targetTextEdit;
0297 }
0298 
0299 void EditorView::toggleBookmark(bool checked)
0300 {
0301     if (Q_UNLIKELY(m_targetTextEdit->currentPos().entry == -1))
0302         return;
0303 
0304     m_catalog->setBookmark(m_targetTextEdit->currentPos().entry, checked);
0305 }
0306 
0307 void EditorView::toggleApprovement()
0308 {
0309     //qCWarning(LOKALIZE_LOG)<<"called";
0310     if (Q_UNLIKELY(m_targetTextEdit->currentPos().entry == -1))
0311         return;
0312 
0313     bool newState = !m_catalog->isApproved(m_targetTextEdit->currentPos().entry);
0314     SetStateCmd::push(m_catalog, m_targetTextEdit->currentPos(), newState);
0315     Q_EMIT signalApprovedEntryDisplayed(newState);
0316 }
0317 
0318 void EditorView::setState(TargetState state)
0319 {
0320     if (Q_UNLIKELY(m_targetTextEdit->currentPos().entry == -1
0321                    || m_catalog->state(m_targetTextEdit->currentPos()) == state))
0322         return;
0323 
0324     SetStateCmd::instantiateAndPush(m_catalog, m_targetTextEdit->currentPos(), state);
0325     Q_EMIT signalApprovedEntryDisplayed(m_catalog->isApproved(m_targetTextEdit->currentPos()));
0326 }
0327 
0328 void EditorView::setEquivTrans(bool equivTrans)
0329 {
0330     m_catalog->push(new SetEquivTransCmd(m_catalog, m_targetTextEdit->currentPos(), equivTrans));
0331 }
0332 
0333