5
6
7
12#include <QApplication>
15#ifndef QT_NO_CLIPBOARD
29using namespace Qt::Literals::StringLiterals;
32
33
34
35
37 : QScrollArea(parent->centralWidget()),
38 m_dataModel(dataModel),
41 m_lengthVariants(
false),
42 m_fontSize(font().pointSize()),
47 m_visualizeWhitespace(
true),
51 setObjectName(
"scroll area"_L1);
54 p.setBrush(QPalette::Window, p.brush(QPalette::Active, QPalette::Base));
60#ifndef QT_NO_CLIPBOARD
61 connect(qApp->clipboard(), &QClipboard::dataChanged,
62 this, &MessageEditor::clipboardChanged);
64 connect(m_dataModel, &MultiDataModel::modelAppended,
65 this, &MessageEditor::messageModelAppended);
73 m_tabOrderTimer.setSingleShot(
true);
74 connect(&m_tabOrderTimer, &QTimer::timeout,
75 this, &MessageEditor::reallyFixTabOrder);
77#ifndef QT_NO_CLIPBOARD
81 setWhatsThis(tr(
"This whole panel allows you to view and edit "
82 "the translation of some source text."));
88 if (FormatTextEdit *fte = qobject_cast<FormatTextEdit *>(m_selectionHolder))
89 disconnect(fte, &FormatTextEdit::editorDestroyed,
this, &MessageEditor::editorDestroyed);
94 QFrame *editorPage =
new QFrame;
95 editorPage->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
97 m_source =
new FormWidget(tr(
"Source text"),
false);
99 m_source->setWhatsThis(tr(
"This area shows the source text."));
103 m_pluralSource =
new FormWidget(tr(
"Source text (Plural)"),
false);
105 m_pluralSource->setWhatsThis(tr(
"This area shows the plural form of the source text."));
109 m_commentText =
new FormWidget(tr(
"Developer comments"),
false);
111 m_commentText->setObjectName(
"comment/context view"_L1);
112 m_commentText->setWhatsThis(tr(
"This area shows a comment that"
113 " may guide you, and the context in which the text"
118 m_ncrModeBox =
new QCheckBox(tr(
"NCR mode"));
119 m_ncrModeBox->setWhatsThis(tr(
"Toggles Numeric Character Reference Mode "
120 "for displaying the source text and the translations."));
121 m_ncrModeBox->setHidden(
true);
122 connect(m_ncrModeBox, &QCheckBox::checkStateChanged,
this, &MessageEditor::toggleNcrMode);
124 QBoxLayout *subLayout =
new QVBoxLayout;
126 subLayout->setContentsMargins(5, 5, 5, 5);
127 subLayout->addWidget(m_source);
128 subLayout->addWidget(m_pluralSource);
129 subLayout->addWidget(m_commentText);
131 QBoxLayout *horizontalLayout =
new QHBoxLayout;
132 horizontalLayout->addLayout(subLayout);
133 horizontalLayout->addWidget(m_ncrModeBox, 0, Qt::AlignTop | Qt::AlignRight);
135 m_layout =
new QVBoxLayout;
136 m_layout->setSpacing(2);
137 m_layout->setContentsMargins(2, 2, 2, 2);
138 m_layout->addLayout(horizontalLayout);
139 m_layout->addStretch(1);
140 editorPage->setLayout(m_layout);
142 setWidget(editorPage);
143 setWidgetResizable(
true);
148 QBrush brush = m_dataModel->brushForModel(model);
152 pal.setBrush(QPalette::Window, brush);
154 QPixmap pm(brush.texture().size());
157 p.fillRect(brush.texture().rect(), brush);
158 pal.setBrush(QPalette::Window, pm);
165 int model = m_editors.size();
166 m_editors.append(MessageEditorData());
169 ed.fontSize = m_fontSize;
170 ed.container =
new QWidget;
172 ed.container->setPalette(paletteForModel(model));
173 ed.container->setAutoFillBackground(
true);
175 m_editors[0].container->setPalette(paletteForModel(0));
176 m_editors[0].container->setAutoFillBackground(
true);
180 ed.transCommentText =
new FormWidget(QString(),
true);
183 ed
.transCommentText->setWhatsThis(tr(
"Here you can enter comments for your own use."
184 " They have no effect on the translated applications.") );
189 connect(ed.transCommentText, &FormWidget::textChanged,
190 this, &MessageEditor::emitTranslatorCommentChanged);
191 connect(ed.transCommentText, &FormWidget::textChanged,
192 this, &MessageEditor::resetHoverSelection);
196 QBoxLayout *box =
new QVBoxLayout(ed.container);
197 box->setContentsMargins(5, 5, 5, 5);
200 m_layout->addWidget(ed.container);
201 setTargetLanguage(model);
206 for (
const MessageEditorData &med : std::as_const(m_editors))
207 med.container->deleteLater();
211 m_currentNumerus = -1;
217 m_editors[model].container->deleteLater();
218 m_editors.removeAt(model);
219 if (model <= m_currentModel) {
220 if (model < m_currentModel || m_currentModel == m_editors.size())
223 if (m_currentModel >= 0) {
224 if (m_currentNumerus >= m_editors[m_currentModel].transTexts.size())
225 m_currentNumerus = m_editors[m_currentModel].transTexts.size() - 1;
226 activeEditor()->setFocus();
228 m_currentNumerus = -1;
231 if (m_editors.size() == 1) {
232 m_editors[0].container->setAutoFillBackground(
false);
234 for (
int i = model; i < m_editors.size(); ++i)
235 m_editors[i].container->setPalette(paletteForModel(i));
239void MessageEditor::addPluralForm(
int model,
const QString &label,
bool writable)
242 connect(transEditor, &FormMultiWidget::editorCreated,
243 this, &MessageEditor::editorCreated);
246 if (!m_editors[model].transTexts.isEmpty())
247 transEditor->setVisible(
false);
249 static_cast<QBoxLayout *>(m_editors[model].container->layout())->insertWidget(
250 m_editors[model].transTexts.size(), transEditor);
261 m_editors[model].transTexts << transEditor;
267 font.setPointSize(
static_cast<
int>(m_fontSize));
270 for (
int model = 0; ; ++model) {
273 if (med.transTexts.contains(snd)) {
276 te->installEventFilter(
this);
278 if (m_visualizeWhitespace) {
279 QTextOption option = te->document()->defaultTextOption();
281 option.setFlags(option.flags()
282 | QTextOption::ShowLineAndParagraphSeparators
283 | QTextOption::ShowTabsAndSpaces);
284 te->document()->setDefaultTextOption(option);
295 if (m_selectionHolder == sender())
301 m_tabOrderTimer.start(0);
306 QWidget *prev =
this;
307 for (
const MessageEditorData &med : std::as_const(m_editors)) {
308 for (FormMultiWidget *fmw : med.transTexts)
309 for (QTextEdit *te : fmw->getEditors()) {
310 setTabOrder(prev, te);
313 QTextEdit *te = med.transCommentText->getEditor();
314 setTabOrder(prev, te);
320
321
322
323
324
325
328 QStringList translations;
329 for (
int i = 0; i < m_editors[model].transTexts.size() &&
330 m_editors[model].transTexts.at(i)->isVisible(); ++i)
331 translations << m_editors[model].transTexts[i]->getTranslation();
337 bool oldBlockState = t->blockSignals(
true);
338 QTextCursor c = t->textCursor();
341 t->blockSignals(oldBlockState);
346 if (te != m_selectionHolder) {
347 if (m_selectionHolder) {
350 disconnect(fte, &FormatTextEdit::editorDestroyed,
351 this, &MessageEditor::editorDestroyed);
354 m_selectionHolder = (te->textCursor().hasSelection() ? te :
nullptr);
356 connect(fte, &FormatTextEdit::editorDestroyed,
357 this, &MessageEditor::editorDestroyed);
359#ifndef QT_NO_CLIPBOARD
367 if (m_selectionHolder &&
375 if (m_selectionHolder) {
378 disconnect(fte, &FormatTextEdit::editorDestroyed,
379 this, &MessageEditor::editorDestroyed);
381 m_selectionHolder =
nullptr;
382#ifndef QT_NO_CLIPBOARD
388void MessageEditor::activeModelAndNumerus(
int *model,
int *numerus)
const
390 for (
int j = 0; j < m_editors.size(); ++j) {
391 for (
int i = 0; i < m_editors[j].transTexts.size(); ++i)
392 for (QTextEdit *te : m_editors[j].transTexts[i]->getEditors())
393 if (m_focusWidget == te) {
398 if (m_focusWidget == m_editors[j].transCommentText->getEditor()) {
410 if (m_currentNumerus < 0)
412 const QList<FormatTextEdit *> &editors =
413 m_editors[m_currentModel].transTexts[m_currentNumerus]->getEditors();
414 for (QTextEdit *te : editors)
417 return editors.first();
422 if (m_currentNumerus < 0) {
423 for (
int i = 0; i < m_editors.size(); ++i)
424 if (m_editors[i].container->isVisible()
425 && !m_editors[i].transTexts.first()->getEditors().first()->isReadOnly())
426 return m_editors[i].transTexts.first()->getEditors().first();
429 return activeTranslation();
434 if (m_currentModel < 0 || m_currentNumerus >= 0)
436 return m_editors[m_currentModel].transCommentText->getEditor();
441 if (QTextEdit *te = activeTransComment())
443 return activeTranslation();
448 if (QTextEdit *te = activeTransComment())
450 return activeOr1stTranslation();
455 const QStringList &numerusForms = m_dataModel
->model(model
)->numerusForms();
456 const QString &langLocalized = m_dataModel
->model(model
)->localizedLanguage();
457 for (
int i = 0; i < numerusForms.size(); ++i) {
458 const QString &label = tr(
"Translation to %1 (%2)").arg(langLocalized, numerusForms[i]);
460 m_editors[model].firstForm = label;
461 if (i >= m_editors[model].transTexts.size())
462 addPluralForm(model, label, m_dataModel->isModelWritable(model));
464 m_editors[model].transTexts[i]->setLabel(label);
465 m_editors[model].transTexts[i]->setVisible(!i || m_editors[model].pluralEditMode);
466 m_editors[model].transTexts[i]->setWhatsThis(
467 tr(
"This is where you can enter or modify"
468 " the translation of the above source text.") );
470 for (
int j = m_editors[model].transTexts.size() - numerusForms.size(); j > 0; --j)
471 delete m_editors[model].transTexts.takeLast();
472 m_editors[model].invariantForm = tr(
"Translation to %1").arg(langLocalized);
473 m_editors[model].transCommentText->setLabel(tr(
"Translator comments for %1").arg(langLocalized));
474 m_editors[model].container->setToolTip(
475 QLocale::languageToString(m_dataModel->model(model)->language()));
480 for (
int j = 0; j < m_editors.size(); ++j) {
481 for (
int i = 0; i < m_editors[j].transTexts.size(); ++i)
482 for (QTextEdit *te : m_editors[j].transTexts[i]->getEditors())
484 return &m_editors[j];
485 if (m_editors[j].transCommentText->getEditor() == o)
486 return &m_editors[j];
494 if (e->type() == QEvent::ShortcutOverride) {
495 QKeyEvent *ke =
static_cast<QKeyEvent *>(e);
497 if (ke->modifiers() & Qt::ControlModifier) {
498#ifndef QT_NO_CLIPBOARD
499 if (ke->key() == Qt::Key_C) {
504 if (m_pluralSource
->getEditor()->textCursor().hasSelection()) {
510 if (ke->key() == Qt::Key_A) {
514 }
else if (e->type() == QEvent::KeyPress) {
516 QKeyEvent *ke =
static_cast<QKeyEvent *>(e);
517 if (ke->key() == Qt::Key_Tab &&
518 !(ke->modifiers() & Qt::ControlModifier)) {
522 }
else if (e->type() == QEvent::FocusIn) {
523 QWidget *widget =
static_cast<QWidget *>(o);
524 if (widget != m_focusWidget)
526 }
else if (e->type() == QEvent::ApplicationPaletteChange
527 || e->type() == QEvent::PaletteChange) {
529 p.setBrush(QPalette::Window, p.brush(QPalette::Active, QPalette::Base));
531 if (m_editors.size() > 1) {
532 for (qsizetype i = 0; i < m_editors.size(); i++) {
533 m_editors[i].container->setPalette(paletteForModel(i));
534 m_editors[i].container->setAutoFillBackground(
true);
539 return QScrollArea::eventFilter(o, e);
544 if (widget != m_focusWidget) {
552 m_focusWidget = widget;
555 activeModelAndNumerus(&model, &numerus);
556 if (model != m_currentModel || numerus != m_currentNumerus) {
558 m_currentModel = model;
559 m_currentNumerus = numerus;
562#ifndef QT_NO_CLIPBOARD
573 m_ncrModeBox->setHidden(
true);
575 for (
int j = 0; j < m_editors.size(); ++j) {
576 setEditingEnabled(j,
false);
577 for (FormMultiWidget *widget : std::as_const(m_editors[j].transTexts))
578 widget->clearTranslation();
579 m_editors[j].transCommentText->clearTranslation();
581#ifndef QT_NO_CLIPBOARD
582 emit pasteAvailable(
false);
589 for (
int j = 0; j < m_editors.size(); ++j) {
595 bool newNcrMode = m_ncrModeBox->isChecked();
600 auto translations = currentMessage->translations();
601 for (
int i = 0; i < translations.size(); i++)
602 setNumerusTranslation(j, translations.at(i), i);
610 m_currentIndex = index;
613 for (
int j = 0; j < m_editors.size(); ++j) {
619 ed.container->hide();
622 ed.container->show();
630 if (!item->fileName().isEmpty()) {
631 QString toolTip = tr(
"'%1'\nLine: %2").arg(item->fileName(), QString::number(item->lineNumber()));
632 m_source->setToolTip(toolTip);
634 m_source->setToolTip({});
638 QString commentText = item->comment().simplified();
640 if (!item->extraComment().isEmpty()) {
641 if (!commentText.isEmpty())
642 commentText += u'\n';
643 commentText += item->extraComment().simplified();
657 ed.transTexts.first()->setLabel(ed
.pluralEditMode ? ed.firstForm : ed.invariantForm);
660 QStringList normalizedTranslations =
661 m_dataModel
->model(j
)->normalizedTranslations(*item);
662 for (
int i = 0; i < ed.transTexts.size(); ++i) {
663 bool shouldShow = (i < normalizedTranslations.size());
665 setNumerusTranslation(j, normalizedTranslations.at(i), i);
667 setNumerusTranslation(j, QString(), i);
668 ed.transTexts.at(i)->setVisible(i == 0 || shouldShow);
673 m_ncrModeBox->setCheckState(item->ncrMode() ? Qt::Checked : Qt::Unchecked);
674 m_ncrModeBox->setHidden(
false);
680void MessageEditor::setNumerusTranslation(
int model,
const QString &translation,
int numerus)
683 if (numerus >= ed.transTexts.size())
689void MessageEditor::setTranslation(
int latestModel,
const QString &translation)
692 if (m_currentNumerus < 0) {
695 latestModel = m_currentModel;
696 numerus = m_currentNumerus;
698 FormMultiWidget *transForm = m_editors[latestModel].transTexts[numerus];
699 transForm->getEditors().first()->setFocus();
703void MessageEditor::setEditingEnabled(
int model,
bool enabled)
706 for (FormMultiWidget *widget : std::as_const(ed.transTexts))
707 widget->setEditingEnabled(enabled);
710#ifndef QT_NO_CLIPBOARD
717 m_lengthVariants = on;
718 for (
const MessageEditorData &ed : std::as_const(m_editors))
719 for (FormMultiWidget *widget : ed.transTexts)
720 widget->setMultiEnabled(on);
725 activeEditor()->document()->undo();
730 activeEditor()->document()->redo();
735 bool newUndoAvail =
false;
736 bool newRedoAvail =
false;
737 if (QTextEdit *te = activeEditor()) {
738 QTextDocument *doc = te->document();
739 newUndoAvail = doc->isUndoAvailable();
740 newRedoAvail = doc->isRedoAvailable();
743 if (newUndoAvail != m_undoAvail) {
744 m_undoAvail = newUndoAvail;
745 emit undoAvailable(newUndoAvail);
748 if (newRedoAvail != m_redoAvail) {
749 m_redoAvail = newRedoAvail;
750 emit redoAvailable(newRedoAvail);
754#ifndef QT_NO_CLIPBOARD
757 m_selectionHolder->cut();
762 m_selectionHolder->copy();
767 bool newCopyState =
false;
768 bool newCutState =
false;
770 if (m_selectionHolder) {
772 newCutState = !m_selectionHolder->isReadOnly();
775 if (newCopyState != m_copyAvail) {
776 m_copyAvail = newCopyState;
777 emit copyAvailable(m_copyAvail);
780 if (newCutState != m_cutAvail) {
781 m_cutAvail = newCutState;
782 emit cutAvailable(m_cutAvail);
788 activeEditor()->paste();
794 emit pasteAvailable(!m_clipboardEmpty
795 && (te = activeEditor()) && !te->isReadOnly());
801 m_clipboardEmpty = qApp->clipboard()->text().isNull();
809 if (((te = activeEditor()) && te->hasFocus())
819 emit translationChanged(translations(m_currentModel));
822void MessageEditor::emitTranslatorCommentChanged(QTextEdit *widget)
826 emit translatorCommentChanged(m_editors[m_currentModel].transCommentText->getTranslation());
831 if (m_currentModel < 0 || m_currentIndex
.model() < 0)
834 setTranslation(m_currentModel,
835 m_currentNumerus > 0 && !item->pluralText().isEmpty() ?
836 item->pluralText() : item->text());
841 if (!widget()->hasFocus())
842 if (QTextEdit *activeEditor = activeOr1stEditor())
843 activeEditor->setFocus();
848 if (m_currentModel != model) {
851 m_currentNumerus = -1;
856#ifndef QT_NO_CLIPBOARD
860 m_editors[model].transTexts.first()->getEditors().first()->setFocus();
867 for (
int j = start; j < m_editors.size(); ++j)
871 m_editors[j].transTexts.first()->getEditors().first()->setFocus();
879 focusNextUnfinished(0);
884 return focusNextUnfinished(m_currentModel + 1);
889 m_visualizeWhitespace = value;
894 for (
const MessageEditorData &med : std::as_const(m_editors)) {
895 med.transCommentText->getEditor()->setVisualizeWhitespace(value);
896 for (FormMultiWidget *widget : med.transTexts)
897 for (FormatTextEdit *te : widget->getEditors())
898 te->setVisualizeWhitespace(value);
904 if (m_fontSize != fontSize) {
905 m_fontSize = fontSize;
918 font.setPointSize(
static_cast<
int>(m_fontSize));
924 for (
const MessageEditorData &med : std::as_const(m_editors)) {
925 for (FormMultiWidget *fmw : med.transTexts)
926 for (QTextEdit *te : fmw->getEditors())
928 med.transCommentText->getEditor()->setFont(font);
934 if (m_fontSize >= 32)
943 if (m_fontSize > 8) {
951 m_fontSize = font().pointSize();
void setVisualizeWhitespace(bool value)
void setLengthVariants(bool on)
void setVisualizeWhitespace(bool value)
void setEditorFocusForModel(int model)
void setFontSize(const float fontSize)
void setUnfinishedEditorFocus()
bool focusNextUnfinished()
void showMessage(const MultiDataIndex &index)
bool eventFilter(QObject *, QEvent *) override
void setNcrMode(bool mode)
const TranslatorMessage & message() const
TranslatorMessage::Type type() const
MessageItem * messageItem(const MultiDataIndex &index, int model) const
void languageChanged(int model)
bool isModelWritable(int model) const
void modelDeleted(int model)
static void clearSelection(QTextEdit *t)
FormWidget * transCommentText