Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
messageeditor.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4/* TRANSLATOR MessageEditor
5
6 This is the right panel of the main window.
7*/
8
11
12#include <QApplication>
13#include <QBoxLayout>
14#include <QCheckBox>
15#ifndef QT_NO_CLIPBOARD
16#include <QClipboard>
17#endif
18#include <QDebug>
19#include <QDockWidget>
20#include <QHeaderView>
21#include <QKeyEvent>
22#include <QMainWindow>
23#include <QPainter>
24#include <QTreeView>
25#include <QVBoxLayout>
26
28
29using namespace Qt::Literals::StringLiterals;
30
31/*
32 MessageEditor class impl.
33
34 Handles layout of dock windows and the editor page.
35*/
36MessageEditor::MessageEditor(MultiDataModel *dataModel, QMainWindow *parent)
37 : QScrollArea(parent->centralWidget()),
38 m_dataModel(dataModel),
39 m_currentModel(-1),
40 m_currentNumerus(-1),
41 m_lengthVariants(false),
42 m_fontSize(font().pointSize()),
43 m_undoAvail(false),
44 m_redoAvail(false),
45 m_cutAvail(false),
46 m_copyAvail(false),
47 m_visualizeWhitespace(true),
48 m_selectionHolder(0),
49 m_focusWidget(0)
50{
51 setObjectName("scroll area"_L1);
52
53 QPalette p;
54 p.setBrush(QPalette::Window, p.brush(QPalette::Active, QPalette::Base));
55 setPalette(p);
56
57 setupEditorPage();
58
59 // Signals
60#ifndef QT_NO_CLIPBOARD
61 connect(qApp->clipboard(), &QClipboard::dataChanged,
62 this, &MessageEditor::clipboardChanged);
63#endif
64 connect(m_dataModel, &MultiDataModel::modelAppended,
65 this, &MessageEditor::messageModelAppended);
66 connect(m_dataModel, &MultiDataModel::modelDeleted,
67 this, &MessageEditor::messageModelDeleted);
68 connect(m_dataModel, &MultiDataModel::allModelsDeleted,
69 this, &MessageEditor::allModelsDeleted);
70 connect(m_dataModel, &MultiDataModel::languageChanged,
71 this, &MessageEditor::setTargetLanguage);
72
73 m_tabOrderTimer.setSingleShot(true);
74 connect(&m_tabOrderTimer, &QTimer::timeout,
75 this, &MessageEditor::reallyFixTabOrder);
76
77#ifndef QT_NO_CLIPBOARD
78 clipboardChanged();
79#endif
80
81 setWhatsThis(tr("This whole panel allows you to view and edit "
82 "the translation of some source text."));
84}
85
87{
88 if (FormatTextEdit *fte = qobject_cast<FormatTextEdit *>(m_selectionHolder))
89 disconnect(fte, &FormatTextEdit::editorDestroyed, this, &MessageEditor::editorDestroyed);
90}
91
92void MessageEditor::setupEditorPage()
93{
94 QFrame *editorPage = new QFrame;
95 editorPage->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
96
97 m_source = new FormWidget(tr("Source text"), false);
98 m_source->setHideWhenEmpty(false);
99 m_source->setWhatsThis(tr("This area shows the source text."));
100 connect(m_source, &FormWidget::selectionChanged,
101 this, &MessageEditor::selectionChanged);
102
103 m_pluralSource = new FormWidget(tr("Source text (Plural)"), false);
104 m_pluralSource->setHideWhenEmpty(true);
105 m_pluralSource->setWhatsThis(tr("This area shows the plural form of the source text."));
106 connect(m_pluralSource, &FormWidget::selectionChanged,
107 this, &MessageEditor::selectionChanged);
108
109 m_commentText = new FormWidget(tr("Developer comments"), false);
110 m_commentText->setHideWhenEmpty(true);
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"
114 " occurs.") );
115 connect(m_commentText, &FormWidget::selectionChanged,
116 this, &MessageEditor::selectionChanged);
117
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);
123
124 QBoxLayout *subLayout = new QVBoxLayout;
125
126 subLayout->setContentsMargins(5, 5, 5, 5);
127 subLayout->addWidget(m_source);
128 subLayout->addWidget(m_pluralSource);
129 subLayout->addWidget(m_commentText);
130
131 QBoxLayout *horizontalLayout = new QHBoxLayout;
132 horizontalLayout->addLayout(subLayout);
133 horizontalLayout->addWidget(m_ncrModeBox, 0, Qt::AlignTop | Qt::AlignRight);
134
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);
141
142 setWidget(editorPage);
143 setWidgetResizable(true);
144}
145
146QPalette MessageEditor::paletteForModel(int model) const
147{
148 QBrush brush = m_dataModel->brushForModel(model);
149 QPalette pal;
150
151 if (m_dataModel->isModelWritable(model)) {
152 pal.setBrush(QPalette::Window, brush);
153 } else {
154 QPixmap pm(brush.texture().size());
155 pm.fill();
156 QPainter p(&pm);
157 p.fillRect(brush.texture().rect(), brush);
158 pal.setBrush(QPalette::Window, pm);
159 }
160 return pal;
161}
162
163void MessageEditor::messageModelAppended()
164{
165 int model = m_editors.size();
166 m_editors.append(MessageEditorData());
167 MessageEditorData &ed = m_editors.last();
168 ed.pluralEditMode = false;
169 ed.fontSize = m_fontSize;
170 ed.container = new QWidget;
171 if (model > 0) {
172 ed.container->setPalette(paletteForModel(model));
173 ed.container->setAutoFillBackground(true);
174 if (model == 1) {
175 m_editors[0].container->setPalette(paletteForModel(0));
176 m_editors[0].container->setAutoFillBackground(true);
177 }
178 }
179 bool writable = m_dataModel->isModelWritable(model);
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.") );
185 ed.transCommentText->getEditor()->installEventFilter(this);
188 this, &MessageEditor::selectionChanged);
189 connect(ed.transCommentText, &FormWidget::textChanged,
190 this, &MessageEditor::emitTranslatorCommentChanged);
191 connect(ed.transCommentText, &FormWidget::textChanged,
192 this, &MessageEditor::resetHoverSelection);
194 this, &MessageEditor::resetHoverSelection);
195 fixTabOrder();
196 QBoxLayout *box = new QVBoxLayout(ed.container);
197 box->setContentsMargins(5, 5, 5, 5);
198 box->addWidget(ed.transCommentText);
199 box->addSpacing(ed.transCommentText->getEditor()->fontMetrics().height() / 2);
200 m_layout->addWidget(ed.container);
201 setTargetLanguage(model);
202}
203
204void MessageEditor::allModelsDeleted()
205{
206 for (const MessageEditorData &med : std::as_const(m_editors))
207 med.container->deleteLater();
208 m_editors.clear();
209 m_currentModel = -1;
210 // Do not emit activeModelChanged() - the main window will refresh anyway
211 m_currentNumerus = -1;
213}
214
215void MessageEditor::messageModelDeleted(int model)
216{
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())
221 --m_currentModel;
222 // Do not emit activeModelChanged() - the main window will refresh anyway
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();
227 } else {
228 m_currentNumerus = -1;
229 }
230 }
231 if (m_editors.size() == 1) {
232 m_editors[0].container->setAutoFillBackground(false);
233 } else {
234 for (int i = model; i < m_editors.size(); ++i)
235 m_editors[i].container->setPalette(paletteForModel(i));
236 }
237}
238
239void MessageEditor::addPluralForm(int model, const QString &label, bool writable)
240{
241 FormMultiWidget *transEditor = new FormMultiWidget(label);
242 connect(transEditor, &FormMultiWidget::editorCreated,
243 this, &MessageEditor::editorCreated);
244 transEditor->setEditingEnabled(writable);
245 transEditor->setHideWhenEmpty(!writable);
246 if (!m_editors[model].transTexts.isEmpty())
247 transEditor->setVisible(false);
248 transEditor->setMultiEnabled(m_lengthVariants);
249 static_cast<QBoxLayout *>(m_editors[model].container->layout())->insertWidget(
250 m_editors[model].transTexts.size(), transEditor);
251
252 connect(transEditor, &FormMultiWidget::selectionChanged,
253 this, &MessageEditor::selectionChanged);
254 connect(transEditor, &FormMultiWidget::textChanged,
255 this, &MessageEditor::emitTranslationChanged);
256 connect(transEditor, &FormMultiWidget::textChanged,
257 this, &MessageEditor::resetHoverSelection);
258 connect(transEditor, &FormMultiWidget::cursorPositionChanged,
259 this, &MessageEditor::resetHoverSelection);
260
261 m_editors[model].transTexts << transEditor;
262}
263
264void MessageEditor::editorCreated(QTextEdit *te)
265{
266 QFont font;
267 font.setPointSize(static_cast<int>(m_fontSize));
268
269 FormMultiWidget *snd = static_cast<FormMultiWidget *>(sender());
270 for (int model = 0; ; ++model) {
271 MessageEditorData med = m_editors.at(model);
272 med.transCommentText->getEditor()->setFont(font);
273 if (med.transTexts.contains(snd)) {
274 te->setFont(font);
275
276 te->installEventFilter(this);
277
278 if (m_visualizeWhitespace) {
279 QTextOption option = te->document()->defaultTextOption();
280
281 option.setFlags(option.flags()
282 | QTextOption::ShowLineAndParagraphSeparators
283 | QTextOption::ShowTabsAndSpaces);
284 te->document()->setDefaultTextOption(option);
285 }
286
287 fixTabOrder();
288 return;
289 }
290 }
291}
292
293void MessageEditor::editorDestroyed()
294{
295 if (m_selectionHolder == sender())
296 resetSelection();
297}
298
299void MessageEditor::fixTabOrder()
300{
301 m_tabOrderTimer.start(0);
302}
303
304void MessageEditor::reallyFixTabOrder()
305{
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);
311 prev = te;
312 }
313 QTextEdit *te = med.transCommentText->getEditor();
314 setTabOrder(prev, te);
315 prev = te;
316 }
317}
318
319/*
320 \internal
321 Returns all translations for an item.
322 The number of translations is dependent on if we have a plural form or not.
323 If we don't have a plural form, then this should only contain one item.
324 Otherwise it will contain the number of numerus forms for the particular language.
325*/
326QStringList MessageEditor::translations(int model) const
327{
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();
332 return translations;
333}
334
335static void clearSelection(QTextEdit *t)
336{
337 bool oldBlockState = t->blockSignals(true);
338 QTextCursor c = t->textCursor();
339 c.clearSelection();
340 t->setTextCursor(c);
341 t->blockSignals(oldBlockState);
342}
343
344void MessageEditor::selectionChanged(QTextEdit *te)
345{
346 if (te != m_selectionHolder) {
347 if (m_selectionHolder) {
348 clearSelection(m_selectionHolder);
349 if (FormatTextEdit *fte = qobject_cast<FormatTextEdit*>(m_selectionHolder)) {
350 disconnect(fte, &FormatTextEdit::editorDestroyed,
351 this, &MessageEditor::editorDestroyed);
352 }
353 }
354 m_selectionHolder = (te->textCursor().hasSelection() ? te : nullptr);
355 if (FormatTextEdit *fte = qobject_cast<FormatTextEdit*>(m_selectionHolder)) {
356 connect(fte, &FormatTextEdit::editorDestroyed,
357 this, &MessageEditor::editorDestroyed);
358 }
359#ifndef QT_NO_CLIPBOARD
360 updateCanCutCopy();
361#endif
362 }
363}
364
365void MessageEditor::resetHoverSelection()
366{
367 if (m_selectionHolder &&
368 (m_selectionHolder == m_source->getEditor()
369 || m_selectionHolder == m_pluralSource->getEditor()))
370 resetSelection();
371}
372
373void MessageEditor::resetSelection()
374{
375 if (m_selectionHolder) {
376 clearSelection(m_selectionHolder);
377 if (FormatTextEdit *fte = qobject_cast<FormatTextEdit*>(m_selectionHolder)) {
378 disconnect(fte, &FormatTextEdit::editorDestroyed,
379 this, &MessageEditor::editorDestroyed);
380 }
381 m_selectionHolder = nullptr;
382#ifndef QT_NO_CLIPBOARD
383 updateCanCutCopy();
384#endif
385 }
386}
387
388void MessageEditor::activeModelAndNumerus(int *model, int *numerus) const
389{
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) {
394 *model = j;
395 *numerus = i;
396 return;
397 }
398 if (m_focusWidget == m_editors[j].transCommentText->getEditor()) {
399 *model = j;
400 *numerus = -1;
401 return;
402 }
403 }
404 *model = -1;
405 *numerus = -1;
406}
407
408QTextEdit *MessageEditor::activeTranslation() const
409{
410 if (m_currentNumerus < 0)
411 return 0;
412 const QList<FormatTextEdit *> &editors =
413 m_editors[m_currentModel].transTexts[m_currentNumerus]->getEditors();
414 for (QTextEdit *te : editors)
415 if (te->hasFocus())
416 return te;
417 return editors.first();
418}
419
420QTextEdit *MessageEditor::activeOr1stTranslation() const
421{
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();
427 return 0;
428 }
429 return activeTranslation();
430}
431
432QTextEdit *MessageEditor::activeTransComment() const
433{
434 if (m_currentModel < 0 || m_currentNumerus >= 0)
435 return 0;
436 return m_editors[m_currentModel].transCommentText->getEditor();
437}
438
439QTextEdit *MessageEditor::activeEditor() const
440{
441 if (QTextEdit *te = activeTransComment())
442 return te;
443 return activeTranslation();
444}
445
446QTextEdit *MessageEditor::activeOr1stEditor() const
447{
448 if (QTextEdit *te = activeTransComment())
449 return te;
450 return activeOr1stTranslation();
451}
452
453void MessageEditor::setTargetLanguage(int model)
454{
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]);
459 if (!i)
460 m_editors[model].firstForm = label;
461 if (i >= m_editors[model].transTexts.size())
462 addPluralForm(model, label, m_dataModel->isModelWritable(model));
463 else
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.") );
469 }
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()));
476}
477
478MessageEditorData *MessageEditor::modelForWidget(const QObject *o)
479{
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())
483 if (te == o)
484 return &m_editors[j];
485 if (m_editors[j].transCommentText->getEditor() == o)
486 return &m_editors[j];
487 }
488 return 0;
489}
490
491bool MessageEditor::eventFilter(QObject *o, QEvent *e)
492{
493 // handle copying from the source
494 if (e->type() == QEvent::ShortcutOverride) {
495 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
496
497 if (ke->modifiers() & Qt::ControlModifier) {
498#ifndef QT_NO_CLIPBOARD
499 if (ke->key() == Qt::Key_C) {
500 if (m_source->getEditor()->textCursor().hasSelection()) {
501 m_source->getEditor()->copy();
502 return true;
503 }
504 if (m_pluralSource->getEditor()->textCursor().hasSelection()) {
505 m_pluralSource->getEditor()->copy();
506 return true;
507 }
508 } else
509#endif
510 if (ke->key() == Qt::Key_A) {
511 return true;
512 }
513 }
514 } else if (e->type() == QEvent::KeyPress) {
515 // Ctrl-Tab is still passed through to the textedit and causes a tab to be inserted.
516 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
517 if (ke->key() == Qt::Key_Tab &&
518 !(ke->modifiers() & Qt::ControlModifier)) {
519 focusNextChild();
520 return true;
521 }
522 } else if (e->type() == QEvent::FocusIn) {
523 QWidget *widget = static_cast<QWidget *>(o);
524 if (widget != m_focusWidget)
525 trackFocus(widget);
526 } else if (e->type() == QEvent::ApplicationPaletteChange
527 || e->type() == QEvent::PaletteChange) {
528 QPalette p;
529 p.setBrush(QPalette::Window, p.brush(QPalette::Active, QPalette::Base));
530 setPalette(p);
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);
535 }
536 }
537 }
538
539 return QScrollArea::eventFilter(o, e);
540}
541
542void MessageEditor::grabFocus(QWidget *widget)
543{
544 if (widget != m_focusWidget) {
545 widget->setFocus();
546 trackFocus(widget);
547 }
548}
549
550void MessageEditor::trackFocus(QWidget *widget)
551{
552 m_focusWidget = widget;
553
554 int model, numerus;
555 activeModelAndNumerus(&model, &numerus);
556 if (model != m_currentModel || numerus != m_currentNumerus) {
557 resetSelection();
558 m_currentModel = model;
559 m_currentNumerus = numerus;
560 emit activeModelChanged(activeModel());
561 updateUndoRedo();
562#ifndef QT_NO_CLIPBOARD
563 updateCanPaste();
564#endif
565 }
566}
567
569{
570 m_source->clearTranslation();
571 m_pluralSource->clearTranslation();
572 m_commentText->clearTranslation();
573 m_ncrModeBox->setHidden(true);
574
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();
580 }
581#ifndef QT_NO_CLIPBOARD
582 emit pasteAvailable(false);
583#endif
584 updateUndoRedo();
585}
586
587void MessageEditor::toggleNcrMode()
588{
589 for (int j = 0; j < m_editors.size(); ++j) {
590
591 MessageItem *currentMessage = m_dataModel->messageItem(m_currentIndex, j);
592 if (!currentMessage)
593 continue;
594
595 bool newNcrMode = m_ncrModeBox->isChecked();
596 if (currentMessage->ncrMode() != newNcrMode) {
597 currentMessage->setNcrMode(newNcrMode);
598 m_source->setTranslation(currentMessage->text());
599 m_pluralSource->setTranslation(currentMessage->pluralText());
600 auto translations = currentMessage->translations();
601 for (int i = 0; i < translations.size(); i++)
602 setNumerusTranslation(j, translations.at(i), i);
603 }
604 break;
605 }
606}
607
609{
610 m_currentIndex = index;
611
612 bool hadMsg = false;
613 for (int j = 0; j < m_editors.size(); ++j) {
614
615 MessageEditorData &ed = m_editors[j];
616
617 MessageItem *item = m_dataModel->messageItem(index, j);
618 if (!item) {
619 ed.container->hide();
620 continue;
621 }
622 ed.container->show();
623
624 if (!hadMsg) {
625
626 // Source text form
627 m_source->setTranslation(item->text());
628 m_pluralSource->setTranslation(item->pluralText());
629 // Use location from first non-obsolete message
630 if (!item->fileName().isEmpty()) {
631 QString toolTip = tr("'%1'\nLine: %2").arg(item->fileName(), QString::number(item->lineNumber()));
632 m_source->setToolTip(toolTip);
633 } else {
634 m_source->setToolTip({});
635 }
636
637 // Comment field
638 QString commentText = item->comment().simplified();
639
640 if (!item->extraComment().isEmpty()) {
641 if (!commentText.isEmpty())
642 commentText += u'\n';
643 commentText += item->extraComment().simplified();
644 }
645
646 m_commentText->setTranslation(commentText);
647
648 hadMsg = true;
649 }
650
651 setEditingEnabled(j, m_dataModel->isModelWritable(j)
654
655 // Translation label
656 ed.pluralEditMode = item->translations().size() > 1;
657 ed.transTexts.first()->setLabel(ed.pluralEditMode ? ed.firstForm : ed.invariantForm);
658
659 // Translation forms
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());
664 if (shouldShow)
665 setNumerusTranslation(j, normalizedTranslations.at(i), i);
666 else
667 setNumerusTranslation(j, QString(), i);
668 ed.transTexts.at(i)->setVisible(i == 0 || shouldShow);
669 }
670
671 ed.transCommentText->setTranslation(item->translatorComment().trimmed(), false);
672
673 m_ncrModeBox->setCheckState(item->ncrMode() ? Qt::Checked : Qt::Unchecked);
674 m_ncrModeBox->setHidden(false);
675 }
676
677 updateUndoRedo();
678}
679
680void MessageEditor::setNumerusTranslation(int model, const QString &translation, int numerus)
681{
682 MessageEditorData &ed = m_editors[model];
683 if (numerus >= ed.transTexts.size())
684 numerus = 0;
685 FormMultiWidget *transForm = ed.transTexts[numerus];
686 transForm->setTranslation(translation, false);
687}
688
689void MessageEditor::setTranslation(int latestModel, const QString &translation)
690{
691 int numerus;
692 if (m_currentNumerus < 0) {
693 numerus = 0;
694 } else {
695 latestModel = m_currentModel;
696 numerus = m_currentNumerus;
697 }
698 FormMultiWidget *transForm = m_editors[latestModel].transTexts[numerus];
699 transForm->getEditors().first()->setFocus();
700 transForm->setTranslation(translation, true);
701}
702
703void MessageEditor::setEditingEnabled(int model, bool enabled)
704{
705 MessageEditorData &ed = m_editors[model];
706 for (FormMultiWidget *widget : std::as_const(ed.transTexts))
707 widget->setEditingEnabled(enabled);
709
710#ifndef QT_NO_CLIPBOARD
711 updateCanPaste();
712#endif
713}
714
716{
717 m_lengthVariants = on;
718 for (const MessageEditorData &ed : std::as_const(m_editors))
719 for (FormMultiWidget *widget : ed.transTexts)
720 widget->setMultiEnabled(on);
721}
722
723void MessageEditor::undo()
724{
725 activeEditor()->document()->undo();
726}
727
729{
730 activeEditor()->document()->redo();
731}
732
733void MessageEditor::updateUndoRedo()
734{
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();
741 }
742
743 if (newUndoAvail != m_undoAvail) {
744 m_undoAvail = newUndoAvail;
745 emit undoAvailable(newUndoAvail);
746 }
747
748 if (newRedoAvail != m_redoAvail) {
749 m_redoAvail = newRedoAvail;
750 emit redoAvailable(newRedoAvail);
751 }
752}
753
754#ifndef QT_NO_CLIPBOARD
756{
757 m_selectionHolder->cut();
758}
759
761{
762 m_selectionHolder->copy();
763}
764
765void MessageEditor::updateCanCutCopy()
766{
767 bool newCopyState = false;
768 bool newCutState = false;
769
770 if (m_selectionHolder) {
771 newCopyState = true;
772 newCutState = !m_selectionHolder->isReadOnly();
773 }
774
775 if (newCopyState != m_copyAvail) {
776 m_copyAvail = newCopyState;
777 emit copyAvailable(m_copyAvail);
778 }
779
780 if (newCutState != m_cutAvail) {
781 m_cutAvail = newCutState;
782 emit cutAvailable(m_cutAvail);
783 }
784}
785
787{
788 activeEditor()->paste();
789}
790
791void MessageEditor::updateCanPaste()
792{
793 QTextEdit *te;
794 emit pasteAvailable(!m_clipboardEmpty
795 && (te = activeEditor()) && !te->isReadOnly());
796}
797
798void MessageEditor::clipboardChanged()
799{
800 // this is expensive, so move it out of the common path in updateCanPaste
801 m_clipboardEmpty = qApp->clipboard()->text().isNull();
802 updateCanPaste();
803}
804#endif
805
807{
808 QTextEdit *te;
809 if (((te = activeEditor()) && te->hasFocus())
810 || (te = m_source->getEditor())->underMouse()
811 || (te = m_pluralSource->getEditor())->underMouse())
812 te->selectAll();
813}
814
815void MessageEditor::emitTranslationChanged(QTextEdit *widget)
816{
817 grabFocus(widget); // DND proofness
818 updateUndoRedo();
819 emit translationChanged(translations(m_currentModel));
820}
821
822void MessageEditor::emitTranslatorCommentChanged(QTextEdit *widget)
823{
824 grabFocus(widget); // DND proofness
825 updateUndoRedo();
826 emit translatorCommentChanged(m_editors[m_currentModel].transCommentText->getTranslation());
827}
828
830{
831 if (m_currentModel < 0 || m_currentIndex.model() < 0)
832 return;
833 MessageItem *item = m_dataModel->messageItem(m_currentIndex, m_currentModel);
834 setTranslation(m_currentModel,
835 m_currentNumerus > 0 && !item->pluralText().isEmpty() ?
836 item->pluralText() : item->text());
837}
838
840{
841 if (!widget()->hasFocus())
842 if (QTextEdit *activeEditor = activeOr1stEditor())
843 activeEditor->setFocus();
844}
845
847{
848 if (m_currentModel != model) {
849 if (model < 0) {
850 resetSelection();
851 m_currentNumerus = -1;
852 m_currentModel = -1;
853 m_focusWidget = 0;
854 emit activeModelChanged(activeModel());
855 updateUndoRedo();
856#ifndef QT_NO_CLIPBOARD
857 updateCanPaste();
858#endif
859 } else {
860 m_editors[model].transTexts.first()->getEditors().first()->setFocus();
861 }
862 }
863}
864
865bool MessageEditor::focusNextUnfinished(int start)
866{
867 for (int j = start; j < m_editors.size(); ++j)
868 if (m_dataModel->isModelWritable(j))
869 if (MessageItem *item = m_dataModel->messageItem(m_currentIndex, j))
871 m_editors[j].transTexts.first()->getEditors().first()->setFocus();
872 return true;
873 }
874 return false;
875}
876
878{
879 focusNextUnfinished(0);
880}
881
883{
884 return focusNextUnfinished(m_currentModel + 1);
885}
886
888{
889 m_visualizeWhitespace = value;
893
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);
899 }
900}
901
902void MessageEditor::setFontSize(const float fontSize)
903{
904 if (m_fontSize != fontSize) {
905 m_fontSize = fontSize;
906 applyFontSize();
907 }
908}
909
911{
912 return m_fontSize;
913}
914
915void MessageEditor::applyFontSize()
916{
917 QFont font;
918 font.setPointSize(static_cast<int>(m_fontSize));
919
920 m_source->getEditor()->setFont(font);
921 m_pluralSource->getEditor()->setFont(font);
922 m_commentText->getEditor()->setFont(font);
923
924 for (const MessageEditorData &med : std::as_const(m_editors)) {
925 for (FormMultiWidget *fmw : med.transTexts)
926 for (QTextEdit *te : fmw->getEditors())
927 te->setFont(font);
928 med.transCommentText->getEditor()->setFont(font);
929 }
930}
931
933{
934 if (m_fontSize >= 32)
935 return;
936
937 m_fontSize *= 1.2f;
938 applyFontSize();
939}
940
942{
943 if (m_fontSize > 8) {
944 m_fontSize /= 1.2f;
945 applyFontSize();
946 }
947}
948
950{
951 m_fontSize = font().pointSize();
952 applyFontSize();
953}
954
955QT_END_NAMESPACE
void selectionChanged(QTextEdit *)
void setHideWhenEmpty(bool optional)
void setTranslation(const QString &text, bool userAction=false)
void textChanged(QTextEdit *)
void setMultiEnabled(bool enable)
void setEditingEnabled(bool enable)
void cursorPositionChanged()
void setTranslation(const QString &text, bool userAction=false)
void selectionChanged(QTextEdit *)
void setEditingEnabled(bool enable)
void cursorPositionChanged()
FormatTextEdit * getEditor()
void setHideWhenEmpty(bool optional)
void setVisualizeWhitespace(bool value)
void setLengthVariants(bool on)
int activeModel() const
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
bool ncrMode() const
void setNcrMode(bool mode)
const TranslatorMessage & message() const
TranslatorMessage::Type type() const
int model() const
MessageItem * messageItem(const MultiDataIndex &index, int model) const
DataModel * model(int i)
void allModelsDeleted()
void languageChanged(int model)
bool isModelWritable(int model) const
void modelDeleted(int model)
static void clearSelection(QTextEdit *t)
FormWidget * transCommentText