5
6
7
30#include <QApplication>
34#include <QDesktopServices>
40#include <QInputDialog>
41#include <QItemDelegate>
44#include <QLibraryInfo>
50#include <QRegularExpression>
54#include <QSortFilterProxyModel>
55#include <QStackedWidget>
62#if QT_CONFIG(printsupport)
63#include <QPrintDialog>
67using namespace Qt::Literals::StringLiterals;
70static bool hasUiFormPreview(
const QString &fileName)
72 return fileName.endsWith(
".ui"_L1) || fileName.endsWith(
".jui"_L1);
75static bool hasQmlFormPreview(
const QString &fileName,
bool qmlPreviewChecked)
77 return fileName.endsWith(QLatin1String(
".qml")) && qmlPreviewChecked;
80static const int MessageMS = 2500;
86class GroupItemDelegate :
public QItemDelegate
89 GroupItemDelegate(QObject *parent, MultiDataModel *model)
90 : QItemDelegate(parent), m_dataModel(model)
94 void paint(QPainter *painter,
const QStyleOptionViewItem &option,
95 const QModelIndex &index)
const override
97 const QAbstractItemModel *model = index.model();
100 if (!model->parent(index).isValid()) {
101 if (index.column() - 1 == m_dataModel->modelCount()) {
102 QStyleOptionViewItem opt = option;
103 opt.font.setBold(
true);
104 QItemDelegate::paint(painter, opt, index);
108 QItemDelegate::paint(painter, option, index);
112 MultiDataModel *m_dataModel;
117 static const QVariant v = createMarkIcon(TranslationMarks::ObsoleteMark);
132 if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
138 return MainWindow::tr(
"Source text");
142 case 0:
return QString();
144 return MainWindow::tr(
"ID");
146 return MainWindow::tr(
"Source text");
150 if (role == Qt::DecorationRole && orientation == Qt::Horizontal && section - 1 < m_dataModel->modelCount())
171 if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
173 case 0:
return QString();
175 return m_translationType == TEXTBASED ? MainWindow::tr(
"Context")
176 : MainWindow::tr(
"Label");
178 return MainWindow::tr(
"Items");
181 if (role == Qt::DecorationRole && orientation == Qt::Horizontal && section - 1 < m_dataModel->modelCount())
206 if (event->type() == QEvent::FocusIn)
213#if QT_CONFIG(process)
216#if QT_CONFIG(printsupport)
220 m_translationSettingsDialog(0),
221 m_settingCurrentMessage(
false),
222 m_fileActiveModel(-1),
223 m_editActiveModel(-1),
227 setUnifiedTitleAndToolBarOnMac(
true);
230#if !defined(Q_OS_MACOS) && !defined(Q_OS_WIN)
231 setWindowIcon(QPixmap(
":/images/appicon.png"_L1));
234 m_dataModel =
new MultiDataModel(
this);
235 m_idBasedMessageModel =
new MessageModel(IDBASED,
this, m_dataModel);
236 m_textBasedMessageModel =
new MessageModel(TEXTBASED,
this, m_dataModel);
239 m_contextAndLabelDock =
new QDockWidget(
this);
240 m_contextAndLabelDock->setObjectName(
"ContextLabelDockWidget");
241 m_contextAndLabelDock->setAllowedAreas(Qt::AllDockWidgetAreas);
242 m_contextAndLabelDock->setWindowTitle(tr(
"Context/Label"));
243 m_contextAndLabelDock->setAcceptDrops(
true);
244 m_contextAndLabelDock->installEventFilter(
this);
246 m_contextAndLabelView =
new QTabWidget(
this);
248 QWidget* dockContent =
new QWidget(
this);
249 QBoxLayout* layout =
new QBoxLayout(QBoxLayout::LeftToRight, dockContent);
250 layout->addWidget(m_contextAndLabelView);
252 m_contextAndLabelDock->setWidget(dockContent);
255 m_sortedContextsModel =
new SortedGroupsModel(
this, m_dataModel, TEXTBASED);
256 m_sortedContextsModel->setSortRole(MessageModel::SortRole);
257 m_sortedContextsModel->setSortCaseSensitivity(Qt::CaseInsensitive);
258 m_sortedContextsModel->setSourceModel(m_textBasedMessageModel);
260 m_contextView =
new QTreeView(
this);
261 m_contextView->setRootIsDecorated(
false);
262 m_contextView->setItemsExpandable(
false);
263 m_contextView->setUniformRowHeights(
true);
264 m_contextView->setAlternatingRowColors(
true);
265 m_contextView->setAllColumnsShowFocus(
true);
266 m_contextView->setItemDelegate(
new GroupItemDelegate(
this, m_dataModel));
267 m_contextView->setSortingEnabled(
true);
268 m_contextView->setWhatsThis(tr(
"This panel lists the source contexts."));
269 m_contextView->setModel(m_sortedContextsModel);
270 m_contextView->header()->setSectionsMovable(
false);
271 m_contextView->setColumnHidden(0,
true);
272 m_contextView->header()->setStretchLastSection(
false);
274 m_contextAndLabelView->addTab(m_contextView,
"Text Based"_L1);
277 m_sortedLabelsModel =
new SortedGroupsModel(
this, m_dataModel, IDBASED);
278 m_sortedLabelsModel->setSortRole(MessageModel::SortRole);
279 m_sortedLabelsModel->setSortCaseSensitivity(Qt::CaseInsensitive);
280 m_sortedLabelsModel->setSourceModel(m_idBasedMessageModel);
282 m_labelView =
new QTreeView(
this);
283 m_labelView->setRootIsDecorated(
false);
284 m_labelView->setItemsExpandable(
false);
285 m_labelView->setUniformRowHeights(
true);
286 m_labelView->setAlternatingRowColors(
true);
287 m_labelView->setAllColumnsShowFocus(
true);
288 m_labelView->setItemDelegate(
new GroupItemDelegate(
this, m_dataModel));
289 m_labelView->setSortingEnabled(
true);
290 m_labelView->setWhatsThis(tr(
"This panel lists the source labels."));
291 m_labelView->setModel(m_sortedLabelsModel);
292 m_labelView->header()->setSectionsMovable(
false);
293 m_labelView->setColumnHidden(0,
true);
294 m_labelView->header()->setStretchLastSection(
false);
296 m_contextAndLabelView->addTab(m_labelView,
"ID Based"_L1);
299 m_messagesDock =
new QDockWidget(
this);
300 m_messagesDock->setObjectName(
"StringsDockWidget");
301 m_messagesDock->setAllowedAreas(Qt::AllDockWidgetAreas);
302 m_messagesDock->setWindowTitle(tr(
"Strings"));
303 m_messagesDock->setAcceptDrops(
true);
304 m_messagesDock->installEventFilter(
this);
306 m_sortedTextBasedMessagesModel =
new SortedMessagesModel(
this, m_dataModel, TEXTBASED);
307 m_sortedTextBasedMessagesModel->setSortRole(MessageModel::SortRole);
308 m_sortedTextBasedMessagesModel->setSortCaseSensitivity(Qt::CaseInsensitive);
309 m_sortedTextBasedMessagesModel->setSortLocaleAware(
true);
310 m_sortedTextBasedMessagesModel->setSourceModel(m_textBasedMessageModel);
312 m_sortedIdBasedMessagesModel =
new SortedMessagesModel(
this, m_dataModel, IDBASED);
313 m_sortedIdBasedMessagesModel->setSortRole(MessageModel::SortRole);
314 m_sortedIdBasedMessagesModel->setSortCaseSensitivity(Qt::CaseInsensitive);
315 m_sortedIdBasedMessagesModel->setSortLocaleAware(
true);
316 m_sortedIdBasedMessagesModel->setSourceModel(m_idBasedMessageModel);
318 m_messageView =
new QTreeView(m_messagesDock);
319 m_messageView->setSortingEnabled(
true);
320 m_messageView->setRootIsDecorated(
false);
321 m_messageView->setUniformRowHeights(
true);
322 m_messageView->setAllColumnsShowFocus(
true);
323 m_messageView->setItemsExpandable(
false);
324 m_messageView->setModel(m_sortedTextBasedMessagesModel);
325 m_messageView->header()->setSectionsMovable(
false);
326 m_messageView->setColumnHidden(0,
true);
328 m_messagesDock->setWidget(m_messageView);
331 m_messageEditor =
new MessageEditor(m_dataModel,
this);
332 m_messageEditor->setAcceptDrops(
true);
333 m_messageEditor->installEventFilter(
this);
335 QBoxLayout *lout =
new QBoxLayout(QBoxLayout::TopToBottom, m_ui.centralwidget);
336 lout->addWidget(m_messageEditor);
337 lout->setContentsMargins(QMargins());
338 m_ui.centralwidget->setLayout(lout);
341 m_phrasesDock =
new QDockWidget(
this);
342 m_phrasesDock->setObjectName(
"PhrasesDockwidget");
343 m_phrasesDock->setAllowedAreas(Qt::AllDockWidgetAreas);
344 m_phrasesDock->setWindowTitle(tr(
"Phrases and guesses"));
346 m_phraseView =
new PhraseView(m_dataModel, &m_phraseDict,
this);
347 m_phrasesDock->setWidget(m_phraseView);
350 m_sourceAndFormDock =
new QDockWidget(
this);
351 m_sourceAndFormDock->setObjectName(
"SourceAndFormDock");
352 m_sourceAndFormDock->setAllowedAreas(Qt::AllDockWidgetAreas);
353 m_sourceAndFormDock->setWindowTitle(tr(
"Sources and Forms"));
354 m_sourceAndFormView =
new QStackedWidget(
this);
355 m_sourceAndFormDock->setWidget(m_sourceAndFormView);
356 m_uiFormPreviewView =
new UiFormPreviewView(0, m_dataModel);
357 m_qmlFormPreviewView =
new QmlFormPreviewView(m_dataModel);
358 m_sourceCodeView =
new SourceCodeView(0);
359 m_sourceAndFormView->addWidget(m_sourceCodeView);
360 m_sourceAndFormView->addWidget(m_uiFormPreviewView);
361 m_sourceAndFormView->addWidget(m_qmlFormPreviewView);
364 m_errorsDock =
new QDockWidget(
this);
365 m_errorsDock->setObjectName(
"ErrorsDockWidget");
366 m_errorsDock->setAllowedAreas(Qt::AllDockWidgetAreas);
367 m_errorsDock->setWindowTitle(tr(
"Warnings"));
368 m_errorsView =
new ErrorsView(m_dataModel,
this);
369 m_errorsDock->setWidget(m_errorsView);
372 setDockNestingEnabled(
true);
373 setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
374 setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
375 setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
376 setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
377 addDockWidget(Qt::LeftDockWidgetArea, m_contextAndLabelDock);
378 addDockWidget(Qt::TopDockWidgetArea, m_messagesDock);
379 addDockWidget(Qt::BottomDockWidgetArea, m_phrasesDock);
380 addDockWidget(Qt::TopDockWidgetArea, m_sourceAndFormDock);
381 m_sourceAndFormDock->hide();
382 addDockWidget(Qt::BottomDockWidgetArea, m_errorsDock);
387 m_messageEditor->installEventFilter(m_phraseView);
390 QShortcut *contextShortcut =
new QShortcut(QKeySequence(Qt::Key_F6),
this);
391 connect(contextShortcut, &QShortcut::activated,
392 this, &MainWindow::showContextDock);
393 QShortcut *messagesShortcut =
new QShortcut(QKeySequence(Qt::Key_F7),
this);
394 connect(messagesShortcut, &QShortcut::activated,
395 this, &MainWindow::showMessagesDock);
396 QShortcut *errorsShortcut =
new QShortcut(QKeySequence(Qt::Key_F8),
this);
397 connect(errorsShortcut, &QShortcut::activated,
398 this, &MainWindow::showErrorDock);
399 QShortcut *sourceCodeShortcut =
new QShortcut(QKeySequence(Qt::Key_F9),
this);
400 connect(sourceCodeShortcut, &QShortcut::activated,
401 this, &MainWindow::showSourceCodeDock);
402 QShortcut *phrasesShortcut =
new QShortcut(QKeySequence(Qt::Key_F10),
this);
403 connect(phrasesShortcut, &QShortcut::activated,
404 this, &MainWindow::showPhrasesDock);
406 connect(m_phraseView, &PhraseView::phraseSelected,
407 m_messageEditor, &MessageEditor::setTranslation);
409 this, &
MainWindow::setCurrentMessageFromGuess);
410 connect(m_contextView->selectionModel(), &QItemSelectionModel::currentRowChanged,
411 this, &MainWindow::selectedContextChanged);
412 connect(m_labelView->selectionModel(), &QItemSelectionModel::currentRowChanged,
this,
413 &MainWindow::selectedLabelChanged);
414 connect(m_contextView->selectionModel(), &QItemSelectionModel::currentColumnChanged,
415 this, &MainWindow::updateLatestModel);
416 connect(m_labelView->selectionModel(), &QItemSelectionModel::currentColumnChanged,
this,
417 &MainWindow::updateLatestModel);
420 connect(m_contextAndLabelView, &QTabWidget::currentChanged,
this,
421 &MainWindow::contextAndLabelTabChanged);
423 m_translateDialog =
new TranslateDialog(
this);
424 m_batchTranslateDialog =
new BatchTranslationDialog(m_dataModel,
this);
425 m_findDialog =
new FindDialog(
this);
430 m_progressLabel =
new QLabel();
431 statusBar()->addPermanentWidget(m_progressLabel);
432 m_modifiedLabel =
new QLabel(tr(
" MOD ",
"status bar: file(s) modified"));
433 statusBar()->addPermanentWidget(m_modifiedLabel);
434 m_contextAndLabelView->setCurrentWidget(m_contextView);
435 contextAndLabelTabChanged();
441 connect(m_dataModel, &MultiDataModel::modifiedChanged,
442 this, &QWidget::setWindowModified);
443 connect(m_dataModel, &MultiDataModel::modifiedChanged,
444 m_modifiedLabel, &QWidget::setVisible);
456 connect(m_messageView, &QAbstractItemView::clicked,
457 this, &MainWindow::toggleFinished);
458 connect(m_messageView, &QAbstractItemView::activated,
459 m_messageEditor, &MessageEditor::setEditorFocus);
460 connect(m_contextView, &QAbstractItemView::activated,
461 m_messageView, qOverload<>(&QWidget::setFocus));
462 connect(m_labelView, &QAbstractItemView::activated, m_messageView,
463 qOverload<>(&QWidget::setFocus));
464 connect(m_messageEditor, &MessageEditor::translationChanged,
465 this, &MainWindow::updateTranslation);
466 connect(m_messageEditor, &MessageEditor::translatorCommentChanged,
467 this, &MainWindow::updateTranslatorComment);
468 connect(m_findDialog, &FindDialog::findNext,
469 this, &MainWindow::findNext);
470 connect(m_translateDialog, &TranslateDialog::requestMatchUpdate,
471 this, &MainWindow::updateTranslateHit);
472 connect(m_translateDialog, &TranslateDialog::activated,
475 QSize as(screen()->size());
477 resize(QSize(1000, 800).boundedTo(as));
482 connect(m_ui.actionLengthVariants, &QAction::toggled,
483 m_messageEditor, &MessageEditor::setLengthVariants);
484 m_messageEditor->setLengthVariants(m_ui.actionLengthVariants->isChecked());
485 m_messageEditor->setVisualizeWhitespace(m_ui.actionVisualizeWhitespace->isChecked());
487 m_focusWatcher =
new FocusWatcher(m_messageEditor,
this);
488 m_contextView->installEventFilter(m_focusWatcher);
489 m_labelView->installEventFilter(m_focusWatcher);
490 m_messageView->installEventFilter(m_focusWatcher);
491 m_messageEditor->installEventFilter(m_focusWatcher);
492 m_sourceAndFormView->installEventFilter(m_focusWatcher);
493 m_phraseView->installEventFilter(m_focusWatcher);
494 m_errorsView->installEventFilter(m_focusWatcher);
500#if QT_CONFIG(process)
501 if (m_assistantProcess && m_assistantProcess->state() == QProcess::Running) {
502 m_assistantProcess->terminate();
503 m_assistantProcess->waitForFinished(3000);
506 qDeleteAll(m_phraseBooks);
509#if QT_CONFIG(printsupport)
516 m_contextView->header()->setSectionResizeMode(1, QHeaderView::Stretch);
517 m_contextView->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
518 m_labelView->header()->setSectionResizeMode(1, QHeaderView::Stretch);
519 m_labelView->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
527 for (
int i = 1; i < mc + 1; ++i) {
528 m_contextView->header()->setSectionResizeMode(i, QHeaderView::Fixed);
529 m_contextView->header()->resizeSection(i, 24);
531 m_labelView->header()->setSectionResizeMode(i, QHeaderView::Fixed);
532 m_labelView->header()->resizeSection(i, 24);
534 m_messageView->header()->setSectionResizeMode(i, QHeaderView::Fixed);
535 m_messageView->header()->resizeSection(i, 24);
537 for (
int i = mc + 1; i < m_messageView->header()->count(); i++)
538 m_messageView->header()->setSectionResizeMode(i, QHeaderView::Stretch);
541 selectedMessageChanged(QModelIndex(), QModelIndex());
542 doUpdateLatestModel(-1);
544 QTreeView *view = qobject_cast<QTreeView *>(m_contextAndLabelView->currentWidget());
545 if (!view->currentIndex().isValid()) {
547 view->setCurrentIndex(m_activeSortedGroupsModel->index(0, 0));
550 view->selectionModel()->select(view->currentIndex(),
551 QItemSelectionModel::SelectCurrent
552 | QItemSelectionModel::Rows);
553 m_messageView->selectionModel()->select(m_messageView->currentIndex(),
554 QItemSelectionModel::SelectCurrent|QItemSelectionModel::Rows);
559 doUpdateLatestModel(0);
560 else if (m_currentIndex
.model() >= mc)
561 doUpdateLatestModel(mc - 1);
564 m_contextView->setUpdatesEnabled(
true);
565 m_labelView->setUpdatesEnabled(
true);
566 m_messageView->setUpdatesEnabled(
true);
571 m_ui.actionFind->setEnabled(m_dataModel->contextCount() > 0 || m_dataModel->labelCount() > 0);
572 m_ui.actionFindNext->setEnabled(
false);
573 m_ui.actionFindPrev->setEnabled(
false);
577 updateVisibleColumns();
593 bool waitCursor =
false;
594 statusBar()->showMessage(tr(
"Loading..."));
595 qApp->processEvents();
598 bool closeOld =
false;
599 for (QString name : names) {
601 QApplication::setOverrideCursor(Qt::WaitCursor);
605 bool readWrite = m_globalReadWrite;
606 if (name.startsWith(u'=')) {
612 name = fi.canonicalFilePath();
613 if (m_dataModel->isFileLoaded(name) >= 0)
617 DataModel *dm =
new DataModel(m_dataModel);
618 if (!dm->load(name, &langGuessed,
this)) {
622 if (opened.isEmpty()) {
623 if (!m_dataModel->isWellMergeable(dm)) {
624 QApplication::restoreOverrideCursor();
626 switch (QMessageBox::information(
this, tr(
"Loading File - Qt Linguist"),
627 tr(
"The file '%1' does not seem to be related to the currently open file(s) '%2'.\n\n"
628 "Close the open file(s) first?")
629 .arg(DataModel::prettifyPlainFileName(name), m_dataModel->condensedSrcFileNames(
true)),
630 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
632 case QMessageBox::Cancel:
635 case QMessageBox::Yes:
643 if (!opened.first().dataModel->isWellMergeable(dm)) {
644 QApplication::restoreOverrideCursor();
646 switch (QMessageBox::information(
this, tr(
"Loading File - Qt Linguist"),
647 tr(
"The file '%1' does not seem to be related to the file '%2'"
648 " which is being loaded as well.\n\n"
649 "Skip loading the first named file?")
650 .arg(DataModel::prettifyPlainFileName(name), opened.first().dataModel->srcFileName(
true)),
651 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
653 case QMessageBox::Cancel:
655 for (
const OpenedFile &op : std::as_const(opened))
658 case QMessageBox::Yes:
666 opened.append(OpenedFile(dm, readWrite, langGuessed));
671 QApplication::restoreOverrideCursor();
675 for (
const OpenedFile &op : std::as_const(opened))
681 for (
const OpenedFile &op : std::as_const(opened)) {
682 if (op.langGuessed) {
684 QApplication::restoreOverrideCursor();
687 if (!m_translationSettingsDialog)
688 m_translationSettingsDialog =
new TranslationSettingsDialog(
this);
689 m_translationSettingsDialog->setDataModel(op.dataModel);
690 m_translationSettingsDialog->exec();
695 QApplication::setOverrideCursor(Qt::WaitCursor);
696 m_contextView->setUpdatesEnabled(
false);
697 m_labelView->setUpdatesEnabled(
false);
698 m_messageView->setUpdatesEnabled(
false);
700 for (
const OpenedFile &op : std::as_const(opened)) {
701 m_phraseDict.append(QHash<QString, QList<Phrase *> >());
702 m_dataModel->append(op.dataModel, op.readWrite);
704 updatePhraseDictInternal(m_phraseDict.size() - 1);
705 totalCount += op.dataModel->messageCount();
707 statusBar()->showMessage(tr(
"%n translation unit(s) loaded.", 0, totalCount), MessageMS);
709 m_recentFiles.addFiles(m_dataModel->srcFileNames());
712 QApplication::restoreOverrideCursor();
718 m_globalReadWrite =
true;
719 pickTranslationFiles();
724 m_globalReadWrite =
false;
725 pickTranslationFiles();
731 if (model >= 0 && maybeSave(model)) {
732 m_phraseDict.removeAt(model);
733 m_contextView->setUpdatesEnabled(
false);
734 m_labelView->setUpdatesEnabled(
false);
735 m_messageView->setUpdatesEnabled(
false);
743 if (maybeSaveAll()) {
744 m_phraseDict.clear();
745 m_contextView->setUpdatesEnabled(
false);
746 m_labelView->setUpdatesEnabled(
false);
747 m_messageView->setUpdatesEnabled(
false);
751 m_recentFiles.closeGroup();
759 static const QString pattern(
"%1 (*.%2);;"_L1);
760 QStringList allExtensions;
762 for (
const Translator::FileFormat &format : std::as_const(Translator::registeredFileFormats())) {
763 if (format.fileType == Translator::FileFormat::TranslationSource && format.priority >= 0) {
764 filter.append(pattern.arg(format.description(), format.extension));
765 allExtensions.append(
"*."_L1 + format.extension);
768 QString allFilter = QObject::tr(
"Translation files (%1);;").arg(allExtensions.join(u' '));
770 filter.prepend(allFilter);
772 filter.append(allFilter);
773 filter.append(QObject::tr(
"All files (*)"));
780 if (!m_recentFiles.isEmpty())
781 dir = QFileInfo(m_recentFiles.lastOpenedFile()).path();
785 QFileInfo mainFile(m_dataModel->srcFileName(0));
786 QString mainFileBase = mainFile.baseName();
787 int pos = mainFileBase.indexOf(u'_');
789 varFilt = tr(
"Related files (%1);;")
790 .arg(mainFileBase.left(pos) +
"_*."_L1 + mainFile.completeSuffix());
794 openFiles(QFileDialog::getOpenFileNames(
this, tr(
"Open Translation Files"), dir,
795 varFilt + fileFilters(
true)));
797 const auto fileOpenCompleted = [
this](
const QString &fileName,
const QByteArray &fileContent) {
798 const QString copyFileName =
799 QDir::tempPath() + QLatin1String(
"/") + QFileInfo(fileName).fileName();
800 QFile tsFile(copyFileName);
801 if (tsFile.open(QIODevice::WriteOnly)) {
802 tsFile.write(fileContent);
804 openFiles({ copyFileName });
805 m_wasmFileMap[copyFileName] = std::move(fileName);
809 QFileDialog::getOpenFileContent(varFilt + fileFilters(
true), fileOpenCompleted);
815 QApplication::setOverrideCursor(Qt::WaitCursor);
816 if (m_dataModel->save(model,
this)) {
819 QString wasmFileName = m_dataModel->model(model)->srcFileName();
820 if (
const auto itr = m_wasmFileMap.find(wasmFileName); itr != m_wasmFileMap.end()) {
821 QFile tsFile(wasmFileName);
822 if (tsFile.open(QIODevice::ReadOnly)) {
824 QByteArray content = tsFile.readAll();
825 QFileDialog::saveFileContent(content, itr.value(),
this);
829 statusBar()->showMessage(tr(
"File saved."), MessageMS);
831 QApplication::restoreOverrideCursor();
839 m_recentFiles.closeGroup();
845 QMessageBox::warning(
this, tr(
"Qt Linguist"), tr(
"Please select a file to be saved."));
849 saveInternal(m_currentIndex
.model());
855 QMessageBox::warning(
this, tr(
"Qt Linguist"),
856 tr(
"This function is not available on WebAssembly"));
863 QString newFilename = QFileDialog::getSaveFileName(
864 this, QString(), m_dataModel->srcFileName(m_currentIndex.model()), fileFilters(
false));
865 if (!newFilename.isEmpty()) {
866 if (m_dataModel->saveAs(m_currentIndex
.model(), newFilename,
this)) {
868 statusBar()->showMessage(tr(
"File saved."), MessageMS);
869 m_recentFiles.addFiles(m_dataModel->srcFileNames());
879 QFileInfo oldFile(m_dataModel->srcFileName(m_currentIndex
.model()));
880 QString newFilename = oldFile.path() +
"/"_L1 + oldFile.completeBaseName() +
".qm"_L1;
882 newFilename = QFileDialog::getSaveFileName(
this, tr(
"Release"), newFilename,
883 tr(
"Qt message files for released applications (*.qm)\nAll files (*)"));
884 if (!newFilename.isEmpty()) {
886 statusBar()->showMessage(tr(
"File created."), MessageMS);
892 QFileInfo oldFile(m_dataModel->srcFileName(model));
893 QString newFilename = oldFile.path() + u'/' + oldFile.completeBaseName() +
".qm"_L1;
895 if (!newFilename.isEmpty()) {
896 if (m_dataModel->release(model, newFilename,
false,
false,
SaveEverything,
this))
897 statusBar()->showMessage(tr(
"File created."), MessageMS);
907 releaseInternal(m_currentIndex
.model());
917#if QT_CONFIG(printsupport)
918QPrinter *MainWindow::printer()
921 m_printer =
new QPrinter;
925void MainWindow::print()
928 QPrintDialog dlg(printer(),
this);
930 QApplication::setOverrideCursor(Qt::WaitCursor);
931 printer()->setDocName(m_dataModel->condensedSrcFileNames(
true));
932 statusBar()->showMessage(tr(
"Printing..."));
933 PrintOut pout(printer());
935 auto printGroupItem = [&pout, &pageNum,
this](
int index, TranslationType type) {
936 MultiGroupItem *mg = m_dataModel->multiGroupItem(index, type);
938 pout.setRule(PrintOut::ThickRule);
939 pout.setGuide(mg->group());
941 pout.addBox(100, tr(
"Label: %1").arg(mg->group()), PrintOut::Strong);
943 pout.addBox(100, tr(
"Context: %1").arg(mg->group()), PrintOut::Strong);
946 pout.addBox(92, mg->comment(), PrintOut::Emphasis);
948 pout.setRule(PrintOut::ThickRule);
950 for (
int j = 0; j < mg->messageCount(); ++j) {
951 pout.setRule(PrintOut::ThinRule);
952 bool printedSrc =
false;
954 for (
int k = 0; k < m_dataModel->modelCount(); ++k) {
955 if (
const MessageItem *m = mg->messageItem(k, j)) {
957 pout.addBox(40, m->text());
959 comment = m->comment();
964 if (m->message().isPlural() && m_dataModel->language(k) != QLocale::C) {
965 QStringList transls = m->translations();
966 pout.addBox(40, transls.join(u'\n'));
968 pout.addBox(40, m->translation());
972 switch (m->message().type()) {
973 case TranslatorMessage::Finished:
974 type = tr(
"finished");
976 case TranslatorMessage::Unfinished:
977 type = m->danger() ? tr(
"unresolved") :
"unfinished"_L1;
979 case TranslatorMessage::Obsolete:
980 case TranslatorMessage::Vanished:
981 type = tr(
"obsolete");
984 pout.addBox(12, type, PrintOut::Normal, Qt::AlignRight);
988 if (!comment.isEmpty()) {
990 pout.addBox(92, comment, PrintOut::Emphasis);
991 pout.flushLine(
true);
994 if (pout.pageNum() != pageNum) {
995 pageNum = pout.pageNum();
996 statusBar()->showMessage(tr(
"Printing... (page %1)").arg(pageNum));
1001 for (
int i = 0; i < m_dataModel->labelCount(); ++i)
1002 printGroupItem(i, IDBASED);
1003 for (
int i = 0; i < m_dataModel->contextCount(); ++i)
1004 printGroupItem(i, TEXTBASED);
1005 pout.flushLine(
true);
1006 QApplication::restoreOverrideCursor();
1007 statusBar()->showMessage(tr(
"Printing completed"), MessageMS);
1009 statusBar()->showMessage(tr(
"Printing aborted"), MessageMS);
1017 if ((m_findWhere & where) == 0)
1020 QString text = searchWhat;
1022 if (m_findOptions.testFlag(FindDialog::IgnoreAccelerators))
1026 if (m_findOptions.testFlag(FindDialog::UseRegExp))
1027 return m_findDialog->getRegExp().match(text).hasMatch();
1029 return text.indexOf(m_findText, 0, m_findOptions.testFlag(FindDialog::MatchCase)
1030 ? Qt::CaseSensitive : Qt::CaseInsensitive) >= 0;
1038 const QModelIndex &startIndex = m_messageView->currentIndex();
1039 QModelIndex index = (direction == FindNext
1040 ? nextMessage(startIndex)
1041 : prevMessage(startIndex));
1043 while (index.isValid()) {
1044 QModelIndex realIndex = m_activeSortedMessagesModel->mapToSource(index);
1045 MultiDataIndex dataIndex = m_activeMessageModel->dataIndex(realIndex, -1);
1046 bool hadMessage =
false;
1049 if (m_findStatusFilter != -1 && m_findStatusFilter != m
->type())
1052 if (m_findOptions.testFlag(FindDialog::SkipObsolete)
1059 if (searchItem(DataModel::SourceText, m->text()))
1061 if (searchItem(DataModel::SourceText, m->pluralText()))
1063 if (searchItem(DataModel::Comments, m->comment()))
1065 if (searchItem(DataModel::Comments, m->extraComment()))
1068 const auto translations = m->translations();
1069 for (
const QString &trans : translations)
1070 if (searchItem(DataModel::Translations, trans))
1072 if (searchItem(DataModel::Comments, m->translatorComment()))
1079 setCurrentMessage(realIndex, i);
1082 const QModelIndex &c1 =
1083 m_activeSortedGroupsModel
1085 m_activeSortedMessagesModel->mapToSource(startIndex))
1087 const QModelIndex &c2 =
1088 m_activeSortedGroupsModel->mapFromSource(realIndex).parent();
1089 const QModelIndex &m = m_activeSortedMessagesModel->mapFromSource(realIndex);
1091 if (c2.row() < c1.row() || (c1.row() == c2.row() && m.row() <= startIndex.row()))
1092 statusBar()->showMessage(tr(
"Search wrapped."), MessageMS);
1094 m_findDialog->hide();
1102 if (index == startIndex)
1105 index = (direction == FindNext
1106 ? nextMessage(index)
1107 : prevMessage(index));
1111 QMessageBox::warning(m_findDialog, tr(
"Qt Linguist"),
1112 tr(
"Cannot find the string '%1'.").arg(m_findText));
1117 m_activeMessageModel->blockSignals(
true);
1118 m_batchTranslateDialog->setPhraseBooks(m_phraseBooks, m_currentIndex.model());
1119 if (m_batchTranslateDialog->exec() != QDialog::Accepted)
1120 m_activeMessageModel->blockSignals(
false);
1126 m_latestCaseSensitivity = -1;
1127 QModelIndex idx = m_messageView->currentIndex();
1129 m_activeSortedMessagesModel->index(idx.row(), m_currentIndex
.model() + 1, idx.parent());
1130 m_messageView->setCurrentIndex(idx2);
1131 QString fn = QFileInfo(m_dataModel->srcFileName(m_currentIndex
.model())).baseName();
1132 m_translateDialog->setWindowTitle(tr(
"Search And Translate in '%1' - Qt Linguist").arg(fn));
1133 m_translateDialog->exec();
1136void MainWindow::updateTranslateHit(
bool &hit)
1141 && m->compare(m_translateDialog->findText(),
false, m_translateDialog->caseSensitivity());
1146 QString findText = m_translateDialog->findText();
1147 QString replaceText = m_translateDialog->replaceText();
1148 bool markFinished = m_translateDialog->markFinished();
1149 Qt::CaseSensitivity caseSensitivity = m_translateDialog->caseSensitivity();
1151 int translatedCount = 0;
1153 if (mode == TranslateDialog::TranslateAll) {
1154 auto setTranslations = [
this, &translatedCount, &findText, &caseSensitivity, &replaceText,
1159 if (m && !m
->isObsolete() && m->compare(findText,
false, caseSensitivity)) {
1160 if (!translatedCount)
1161 m_activeMessageModel->blockSignals(
true);
1162 m_dataModel->setTranslation(it, replaceText);
1171 if (translatedCount) {
1173 QMessageBox::warning(m_translateDialog, tr(
"Translate - Qt Linguist"),
1174 tr(
"Translated %n entry(s)", 0, translatedCount));
1177 if (mode == TranslateDialog::Translate) {
1178 m_dataModel->setTranslation(m_currentIndex, replaceText);
1182 const QModelIndex firstIndex = firstMessage();
1183 if (findText != m_latestFindText || caseSensitivity != m_latestCaseSensitivity) {
1184 m_latestFindText = findText;
1185 m_latestCaseSensitivity = caseSensitivity;
1186 m_searchIndex = firstIndex;
1191 QModelIndex realIndex = m_activeSortedMessagesModel->mapToSource(m_searchIndex);
1192 MultiDataIndex dataIndex = m_activeMessageModel->dataIndex(realIndex, m_currentIndex.model());
1193 m_searchIndex = nextMessage(m_searchIndex);
1194 if (MessageItem *m = m_dataModel->messageItem(dataIndex)) {
1195 if (!m->isObsolete() && m->compare(findText,
false, caseSensitivity)) {
1196 setCurrentMessage(realIndex, m_currentIndex.model());
1202 if (m_searchIndex == firstIndex) {
1203 if (QMessageBox::question(
1204 m_translateDialog, tr(
"Translate - Qt Linguist"),
1205 tr(
"No more occurrences of '%1'. Start over?").arg(findText),
1206 QMessageBox::Yes | QMessageBox::No)
1207 != QMessageBox::Yes) {
1208 m_searchIndex = prevMessage(m_searchIndex);
1215 if (!translatedCount) {
1217 QMessageBox::warning(m_translateDialog, tr(
"Translate - Qt Linguist"),
1218 tr(
"Cannot find the string '%1'.").arg(findText));
1224 QString name = QFileDialog::getSaveFileName(
this, tr(
"Create New Phrase Book"),
1225 m_phraseBookDir, tr(
"Qt phrase books (*.qph)\nAll files (*)"));
1226 if (!name.isEmpty()) {
1228 if (!m_translationSettingsDialog)
1229 m_translationSettingsDialog =
new TranslationSettingsDialog(
this);
1231 if (!m_translationSettingsDialog->exec())
1233 m_phraseBookDir = QFileInfo(name).absolutePath();
1234 if (savePhraseBook(&name, pb)) {
1235 if (doOpenPhraseBook(name))
1236 statusBar()->showMessage(tr(
"Phrase book created."), MessageMS);
1241bool MainWindow::isPhraseBookOpen(
const QString &name)
1243 for (
const PhraseBook *pb : std::as_const(m_phraseBooks)) {
1244 if (pb->fileName() == name)
1253 QString name = QFileDialog::getOpenFileName(
this, tr(
"Open Phrase Book"),
1254 m_phraseBookDir, tr(
"Qt phrase books (*.qph);;All files (*)"));
1256 if (!name.isEmpty()) {
1257 m_phraseBookDir = QFileInfo(name).absolutePath();
1258 if (!isPhraseBookOpen(name)) {
1259 if (
PhraseBook *phraseBook = doOpenPhraseBook(name)) {
1260 int n = phraseBook->phrases().size();
1261 statusBar()->showMessage(tr(
"%n phrase(s) loaded.", 0, n), MessageMS);
1267void MainWindow::closePhraseBook(QAction *action)
1269 PhraseBook *pb = m_phraseBookMenu[PhraseCloseMenu].value(action);
1270 if (!maybeSavePhraseBook(pb))
1273 m_phraseBookMenu[PhraseCloseMenu].remove(action);
1274 m_ui.menuClosePhraseBook->removeAction(action);
1276 QAction *act = m_phraseBookMenu[PhraseEditMenu].key(pb);
1277 m_phraseBookMenu[PhraseEditMenu].remove(act);
1278 m_ui.menuEditPhraseBook->removeAction(act);
1280 act = m_phraseBookMenu[PhrasePrintMenu].key(pb);
1281 m_ui.menuPrintPhraseBook->removeAction(act);
1283 m_phraseBooks.removeOne(pb);
1286 updatePhraseDicts();
1288 updatePhraseBookActions();
1291void MainWindow::editPhraseBook(QAction *action)
1293 PhraseBook *pb = m_phraseBookMenu[PhraseEditMenu].value(action);
1297 updatePhraseDicts();
1300#if QT_CONFIG(printsupport)
1302void MainWindow::printPhraseBook(QAction *action)
1304 PhraseBook *phraseBook = m_phraseBookMenu[PhrasePrintMenu].value(action);
1308 QPrintDialog dlg(printer(),
this);
1310 printer()->setDocName(phraseBook->fileName());
1311 statusBar()->showMessage(tr(
"Printing..."));
1312 PrintOut pout(printer());
1313 pout.setRule(PrintOut::ThinRule);
1314 const auto phrases = phraseBook->phrases();
1315 for (
const Phrase *p : phrases) {
1316 pout.setGuide(p->source());
1317 pout.addBox(29, p->source());
1319 pout.addBox(29, p->target());
1321 pout.addBox(34, p->definition(), PrintOut::Emphasis);
1323 if (pout.pageNum() != pageNum) {
1324 pageNum = pout.pageNum();
1325 statusBar()->showMessage(tr(
"Printing... (page %1)")
1328 pout.setRule(PrintOut::NoRule);
1329 pout.flushLine(
true);
1331 pout.flushLine(
true);
1332 statusBar()->showMessage(tr(
"Printing completed"), MessageMS);
1334 statusBar()->showMessage(tr(
"Printing aborted"), MessageMS);
1342 QStringList phraseBookList;
1343 QHash<QString, PhraseBook *> phraseBookHash;
1344 for (PhraseBook *pb : std::as_const(m_phraseBooks)) {
1345 if (pb->language() != QLocale::C && m_dataModel->language(m_currentIndex.model()) != QLocale::C) {
1346 if (pb->language() != m_dataModel->language(m_currentIndex.model()))
1348 if (pb->territory() == m_dataModel->model(m_currentIndex.model())->territory())
1349 phraseBookList.prepend(pb->friendlyPhraseBookName());
1351 phraseBookList.append(pb->friendlyPhraseBookName());
1353 phraseBookList.append(pb->friendlyPhraseBookName());
1355 phraseBookHash.insert(pb->friendlyPhraseBookName(), pb);
1357 if (phraseBookList.isEmpty()) {
1358 QMessageBox::warning(
this, tr(
"Add to phrase book"),
1359 tr(
"No appropriate phrasebook found."));
1363 QString selectedPhraseBook;
1364 if (phraseBookList.size() == 1) {
1365 selectedPhraseBook = phraseBookList.at(0);
1366 if (QMessageBox::information(
this, tr(
"Add to phrase book"),
1367 tr(
"Adding entry to phrasebook %1").arg(selectedPhraseBook),
1368 QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok)
1372 bool okPressed =
false;
1373 selectedPhraseBook = QInputDialog::getItem(
this, tr(
"Add to phrase book"),
1374 tr(
"Select phrase book to add to"),
1375 phraseBookList, 0,
false, &okPressed);
1381 Phrase *phrase =
new Phrase(currentMessage->text(), currentMessage->translation(),
1382 QString(),
nullptr);
1384 phraseBookHash.value(selectedPhraseBook)->append(phrase);
1389 m_contextView->sortByColumn(-1, Qt::AscendingOrder);
1390 m_labelView->sortByColumn(-1, Qt::AscendingOrder);
1391 m_messageView->sortByColumn(-1, Qt::AscendingOrder);
1396#if QT_CONFIG(process)
1397 if (!m_assistantProcess)
1398 m_assistantProcess =
new QProcess();
1400 if (m_assistantProcess->state() != QProcess::Running) {
1401 QString app = QLibraryInfo::path(QLibraryInfo::BinariesPath) + QDir::separator();
1402#if !defined(Q_OS_MAC)
1403 app +=
"assistant"_L1;
1405 app +=
"Assistant.app/Contents/MacOS/Assistant"_L1;
1408 m_assistantProcess->start(app, {
"-enableRemoteControl"_L1 });
1409 if (!m_assistantProcess->waitForStarted()) {
1410 QMessageBox::critical(
this, tr(
"Qt Linguist"),
1411 tr(
"Unable to launch Qt Assistant (%1)").arg(app));
1415 QTextStream str(m_assistantProcess);
1416 str <<
"SetSource qthelp://org.qt-project.linguist."_L1 << QT_VERSION_MAJOR << QT_VERSION_MINOR
1417 << QT_VERSION_PATCH <<
"/qtlinguist/qtlinguist-index.html"_L1 << u'\n' << Qt::endl;
1419 QDesktopServices::openUrl(
1420 QUrl::fromUserInput(QLatin1String(
"https://doc.qt.io/qt-6/qtlinguist-index.html")));
1426 QMessageBox box(
this);
1427 box.setTextFormat(Qt::RichText);
1428 QString version = tr(
"Version %1");
1429 version = version.arg(QLatin1String(QT_VERSION_STR));
1431 const QString description
1432 = tr(
"Qt Linguist is a tool for adding translations to Qt applications.");
1433 box.setText(QStringLiteral(
"<center><img src=\":/images/icons/linguist-128-32.png\"/></img><p>%1</p></center>"
1435 "<p>Copyright (C) The Qt Company Ltd.</p>").arg(version, description));
1437 box.setWindowTitle(QApplication::translate(
"AboutDialog",
"Qt Linguist"));
1438 box.setIcon(QMessageBox::NoIcon);
1444 QMessageBox::aboutQt(
this, tr(
"Qt Linguist"));
1449 bool enabled = !m_phraseBooks.isEmpty();
1450 m_ui.menuClosePhraseBook->setEnabled(enabled);
1451 m_ui.menuEditPhraseBook->setEnabled(enabled);
1452#if QT_CONFIG(printsupport)
1453 m_ui.menuPrintPhraseBook->setEnabled(enabled);
1459 if (maybeSaveAll() && maybeSavePhraseBooks())
1470 switch (QMessageBox::information(
this, tr(
"Qt Linguist"),
1471 tr(
"Do you want to save the modified files?"),
1472 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
1474 case QMessageBox::Cancel:
1476 case QMessageBox::Yes:
1490 switch (QMessageBox::information(
this, tr(
"Qt Linguist"),
1491 tr(
"Do you want to save '%1'?").arg(m_dataModel->srcFileName(model,
true)),
1492 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
1494 case QMessageBox::Cancel:
1496 case QMessageBox::Yes:
1497 saveInternal(model);
1508 bool enable =
false;
1509 bool enableRw =
false;
1517 m_ui.actionSaveAll->setEnabled(enableRw);
1518 m_ui.actionReleaseAll->setEnabled(enableRw);
1519 m_ui.actionCloseAll->setEnabled(enable);
1520#if QT_CONFIG(printsupport)
1521 m_ui.actionPrint->setEnabled(enable);
1523 m_ui.actionAccelerators->setEnabled(enable);
1524 m_ui.actionSurroundingWhitespace->setEnabled(enable);
1525 m_ui.actionEndingPunctuation->setEnabled(enable);
1526 m_ui.actionPhraseMatches->setEnabled(enable);
1527 m_ui.actionPlaceMarkerMatches->setEnabled(enable);
1528 m_ui.actionResetSorting->setEnabled(enable);
1532 m_fileActiveModel = m_editActiveModel = -2;
1535 cap = tr(
"Qt Linguist[*]");
1537 cap = tr(
"%1[*] - Qt Linguist").arg(m_dataModel->condensedSrcFileNames(
true));
1538 setWindowTitle(cap);
1541void MainWindow::selectedContextChanged(
const QModelIndex &sortedIndex,
const QModelIndex &oldIndex)
1543 if (sortedIndex.isValid()) {
1544 if (m_settingCurrentMessage)
1547 if (!m_activeTranslationType || *m_activeTranslationType == IDBASED)
1548 contextAndLabelTabChanged();
1549 QModelIndex sourceIndex = m_sortedContextsModel->mapToSource(sortedIndex);
1550 if (m_activeMessageModel->parent(currentMessageIndex()).row() == sourceIndex.row())
1552 QModelIndex contextIndex = setMessageViewRoot(sourceIndex);
1553 const QModelIndex &firstChild =
1554 m_activeSortedMessagesModel->index(0, sourceIndex.column(), contextIndex);
1555 m_messageView->setCurrentIndex(firstChild);
1556 }
else if (oldIndex.isValid()) {
1557 m_contextView->setCurrentIndex(oldIndex);
1561void MainWindow::selectedLabelChanged(
const QModelIndex &sortedIndex,
const QModelIndex &oldIndex)
1563 if (sortedIndex.isValid()) {
1564 if (m_settingCurrentMessage)
1567 if (!m_activeTranslationType || *m_activeTranslationType != IDBASED)
1568 contextAndLabelTabChanged();
1569 QModelIndex sourceIndex = m_sortedLabelsModel->mapToSource(sortedIndex);
1570 if (m_activeMessageModel->parent(currentMessageIndex()).row() == sourceIndex.row())
1572 QModelIndex labelIndex = setMessageViewRoot(sourceIndex);
1573 const QModelIndex &firstChild =
1574 m_activeSortedMessagesModel->index(0, sourceIndex.column(), labelIndex);
1575 m_messageView->setCurrentIndex(firstChild);
1576 }
else if (oldIndex.isValid()) {
1577 m_labelView->setCurrentIndex(oldIndex);
1582
1583
1584void MainWindow::selectedMessageChanged(
const QModelIndex &sortedIndex,
const QModelIndex &oldIndex)
1587 if (!sortedIndex.isValid() && oldIndex.isValid()) {
1588 m_messageView->setCurrentIndex(oldIndex);
1594 QModelIndex index = m_activeSortedMessagesModel->mapToSource(sortedIndex);
1595 if (index.isValid()) {
1596 model = (index.column() && (index.column() - 1 < m_dataModel
->modelCount()))
1597 ? index.column() - 1
1599 m_currentIndex = m_activeMessageModel->dataIndex(index, model);
1603 m_phraseView->setSourceText(m_currentIndex
.model(), m->text());
1605 m_phraseView->setSourceText(-1, QString());
1613 m_phraseView->setSourceText(-1, QString());
1615 m_errorsView->setEnabled(m != 0);
1616 updateDanger(m_currentIndex,
true);
1620 m_phraseView->setSourceText(-1, QString());
1622 updateSourceView(model, m);
1624 updatePhraseBookActions();
1625 m_ui.actionSelectAll->setEnabled(index.isValid());
1631 updateDanger(index,
true);
1634 if (hasUiFormPreview(m->fileName()))
1636 else if (hasQmlFormPreview(m->fileName(), m_ui.actionQmlPreview->isChecked()))
1637 if (!m_qmlFormPreviewView->setSourceContext(index.model(), m))
1638 m_ui.actionQmlPreview->setChecked(
false);
1643void MainWindow::updateTranslation(
const QStringList &translations)
1648 if (translations == m->translations())
1651 m->setTranslations(translations);
1652 if (!m->fileName().isEmpty() && hasUiFormPreview(m->fileName()))
1654 else if (!m->fileName().isEmpty()
1655 && hasQmlFormPreview(m->fileName(), m_ui.actionQmlPreview->isChecked()))
1656 if (!m_qmlFormPreviewView->setSourceContext(m_currentIndex.model(), m))
1657 m_ui.actionQmlPreview->setChecked(
false);
1658 updateDanger(m_currentIndex,
true);
1666void MainWindow::updateTranslatorComment(
const QString &comment)
1671 if (comment == m->translatorComment())
1674 m->setTranslatorComment(comment);
1681 m_activeMessageModel->blockSignals(
false);
1682 m_contextView->update();
1683 m_labelView->update();
1684 m_messageView->update();
1704void MainWindow::toggleFinished(
const QModelIndex &index)
1706 if (!index.isValid() || index.column() - 1 >= m_dataModel
->modelCount()
1710 QModelIndex item = m_activeSortedMessagesModel->mapToSource(index);
1711 MultiDataIndex dataIndex = m_activeMessageModel->dataIndex(item);
1723 if (!m_machineTranslationDialog)
1724 m_machineTranslationDialog =
new MachineTranslationDialog(
this);
1726 m_machineTranslationDialog->open();
1730
1731
1732
1733
1734QModelIndex
MainWindow::nextGroup(
const QModelIndex &index)
const
1736 QModelIndex sortedGroupIndex = m_activeSortedGroupsModel->mapFromSource(
1737 m_activeSortedMessagesModel->mapToSource(index));
1739 int nextRow = sortedGroupIndex.row() + 1;
1740 if (nextRow >= m_activeSortedGroupsModel->rowCount()) {
1741 const QSortFilterProxyModel *inactiveModel =
1742 m_activeSortedGroupsModel == m_sortedLabelsModel ? m_sortedContextsModel
1743 : m_sortedLabelsModel;
1744 if (inactiveModel->rowCount())
1745 m_contextAndLabelView->setCurrentIndex(1 - m_contextAndLabelView->currentIndex());
1748 sortedGroupIndex = m_activeSortedGroupsModel->index(nextRow, index.column());
1750 return m_activeSortedMessagesModel->mapFromSource(
1751 m_activeSortedGroupsModel->mapToSource(sortedGroupIndex));
1755
1756
1757QModelIndex
MainWindow::prevGroup(
const QModelIndex &index)
const
1759 QModelIndex sortedGroupIndex = m_activeSortedGroupsModel->mapFromSource(
1760 m_activeSortedMessagesModel->mapToSource(index));
1762 int prevRow = sortedGroupIndex.row() - 1;
1764 const QSortFilterProxyModel *inactiveModel =
1765 m_activeSortedGroupsModel == m_sortedLabelsModel ? m_sortedContextsModel
1766 : m_sortedLabelsModel;
1767 if (inactiveModel->rowCount())
1768 m_contextAndLabelView->setCurrentIndex(1 - m_contextAndLabelView->currentIndex());
1769 prevRow = m_activeSortedGroupsModel->rowCount() - 1;
1771 sortedGroupIndex = m_activeSortedGroupsModel->index(prevRow, index.column());
1773 return m_activeSortedMessagesModel->mapFromSource(
1774 m_activeSortedGroupsModel->mapToSource(sortedGroupIndex));
1779 QModelIndex id = m_activeSortedMessagesModel->index(0, 0);
1780 QModelIndex firstId;
1781 if (id.isValid() && m_activeSortedMessagesModel->hasChildren(id))
1782 firstId = m_activeSortedMessagesModel->index(0, 0, id);
1783 else if (id.isValid())
1788QModelIndex
MainWindow::nextMessage(
const QModelIndex ¤tIndex,
bool checkUnfinished)
const
1791 currentIndex.isValid() ? currentIndex : m_activeSortedMessagesModel->index(0, 0);
1794 QModelIndex par = idx.parent();
1795 if (par.isValid()) {
1796 row = idx.row() + 1;
1801 if (row >= m_activeSortedMessagesModel->rowCount(par)) {
1802 par = nextGroup(par);
1805 idx = m_activeSortedMessagesModel->index(row, idx.column(), par);
1807 if (!checkUnfinished)
1810 QModelIndex item = m_activeSortedMessagesModel->mapToSource(idx);
1811 MultiDataIndex index = m_activeMessageModel->dataIndex(item, -1);
1814 }
while (idx != currentIndex);
1815 return QModelIndex();
1818QModelIndex
MainWindow::prevMessage(
const QModelIndex ¤tIndex,
bool checkUnfinished)
const
1821 currentIndex.isValid() ? currentIndex : m_activeSortedMessagesModel->index(0, 0);
1823 int row = idx.row() - 1;
1824 QModelIndex par = idx.parent();
1825 if (!par.isValid()) {
1831 par = prevGroup(par);
1832 row = m_activeSortedMessagesModel->rowCount(par) - 1;
1834 idx = m_activeSortedMessagesModel->index(row, idx.column(), par);
1836 if (!checkUnfinished)
1839 QModelIndex item = m_activeSortedMessagesModel->mapToSource(idx);
1840 MultiDataIndex index = m_activeMessageModel->dataIndex(item, -1);
1843 }
while (idx != currentIndex);
1844 return QModelIndex();
1849 if (m_ui.actionNextUnfinished->isEnabled()) {
1850 if (!doNext(
true)) {
1853 statusBar()->showMessage(tr(
"No untranslated translation units left."), MessageMS);
1861 if (m_ui.actionNextUnfinished->isEnabled()) {
1862 if (!doPrev(
true)) {
1865 statusBar()->showMessage(tr(
"No untranslated translation units left."), MessageMS);
1883 QModelIndex index = prevMessage(m_messageView->currentIndex(), checkUnfinished);
1884 if (index.isValid())
1885 setCurrentMessage(m_activeSortedMessagesModel->mapToSource(index));
1886 if (checkUnfinished)
1890 return index.isValid();
1895 QModelIndex index = nextMessage(m_messageView->currentIndex(), checkUnfinished);
1896 if (index.isValid())
1897 setCurrentMessage(m_activeSortedMessagesModel->mapToSource(index));
1898 if (checkUnfinished)
1902 return index.isValid();
1906 FindDialog::FindOptions options,
int statusFilter)
1911 m_findWhere = where;
1912 m_findOptions = options;
1913 m_findStatusFilter = statusFilter;
1914 if (options.testFlag(FindDialog::UseRegExp)) {
1915 m_findDialog->getRegExp().setPatternOptions(options.testFlag(FindDialog::MatchCase)
1916 ? QRegularExpression::NoPatternOption
1917 : QRegularExpression::CaseInsensitiveOption);
1919 m_ui.actionFindNext->setEnabled(
true);
1920 m_ui.actionFindPrev->setEnabled(
true);
1927 updateDanger(it,
false);
1929 updateDanger(it,
false);
1932 updateDanger(m_currentIndex,
true);
1937 const QString prefix = isDarkMode() ?
":/images/darkicons/"_L1:
":/images/lighticons/"_L1;
1938 auto getIcon = [&prefix](
const QString &name) {
1940 icon.addPixmap(QPixmap(prefix + name + QStringLiteral(
".png")), QIcon::Normal);
1941 icon.addPixmap(QPixmap(prefix + name + QStringLiteral(
"-disabled.png")), QIcon::Disabled);
1945 QIcon openIcon = getIcon(
"open-new"_L1);
1946 m_ui.actionOpen->setIcon(openIcon);
1947 m_ui.actionOpenAux->setIcon(openIcon);
1948 QIcon saveIcon = getIcon(
"save-fl-disk"_L1);
1949 m_ui.actionSave->setIcon(saveIcon);
1950 m_ui.actionSaveAll->setIcon(saveIcon);
1951 m_ui.actionPrint->setIcon(getIcon(
"print"_L1));
1952 m_ui.actionRedo->setIcon(getIcon(
"redo-arrow-right"_L1));
1953 m_ui.actionUndo->setIcon(getIcon(
"undo-arrow-left"_L1));
1954 m_ui.actionCut->setIcon(getIcon(
"cut"_L1));
1955 m_ui.actionCopy->setIcon(getIcon(
"copy-general"_L1));
1956 m_ui.actionPaste->setIcon(getIcon(
"paste-general"_L1));
1957 m_ui.actionFind->setIcon(getIcon(
"search-magnifier"_L1));
1959 m_ui.actionAccelerators->setIcon(getIcon(
"/check-ampersands"_L1));
1960 m_ui.actionOpenPhraseBook->setIcon(getIcon(
"library"_L1));
1961 m_ui.actionDone->setIcon(getIcon(
"mark-current-translation-done"_L1));
1962 m_ui.actionDoneAndNext->setIcon(getIcon(
"mark-current-translation-done-move-to-next"_L1));
1963 m_ui.actionNext->setIcon(getIcon(
"next-translation-item"_L1));
1964 m_ui.actionNextUnfinished->setIcon(getIcon(
"next-unfinished-translation-item"_L1));
1965 m_ui.actionPhraseMatches->setIcon(getIcon(
"check-phrase-suggestions"_L1));
1966 m_ui.actionSurroundingWhitespace->setIcon(getIcon(
"check-white-spaces"_L1));
1967 m_ui.actionEndingPunctuation->setIcon(getIcon(
"check-ending-pontuation"_L1));
1968 m_ui.actionPrev->setIcon(getIcon(
"previous-translation-item"_L1));
1969 m_ui.actionPrevUnfinished->setIcon(getIcon(
"previous-unfinished-translation-item"_L1));
1970 m_ui.actionPlaceMarkerMatches->setIcon(getIcon(
"check-place-markers"_L1));
1971 m_ui.actionWhatsThis->setIcon(getIcon(
"hit-help-chosen-option"_L1));
1976 m_ui.menuRecentlyOpenedFiles->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::DocumentOpenRecent));
1977 m_ui.actionCloseAll->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::WindowClose));
1978 m_ui.actionExit->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ApplicationExit));
1979 m_ui.actionSelectAll->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::EditSelectAll));
1983 connect(m_ui.menuFile, &QMenu::aboutToShow,
this, &MainWindow::fileAboutToShow);
1984 connect(m_ui.actionOpen, &QAction::triggered,
this, &MainWindow::open);
1985 connect(m_ui.actionOpenAux, &QAction::triggered,
this, &MainWindow::openAux);
1986 connect(m_ui.actionSave, &QAction::triggered,
this, &MainWindow::save);
1988 connect(m_ui.actionSaveAll, &QAction::triggered,
this, &MainWindow::saveAll);
1989 connect(m_ui.actionSaveAs, &QAction::triggered,
this, &MainWindow::saveAs);
1991 m_ui.actionSaveAs->setVisible(
false);
1992 m_ui.actionSaveAll->setVisible(
false);
1994 connect(m_ui.actionReleaseAll, &QAction::triggered,
this, &MainWindow::releaseAll);
1995 connect(m_ui.actionRelease, &QAction::triggered,
this, &MainWindow::release);
1996 connect(m_ui.actionReleaseAs, &QAction::triggered,
this, &MainWindow::releaseAs);
1997#if QT_CONFIG(printsupport)
1998 connect(m_ui.actionPrint, &QAction::triggered,
this, &MainWindow::print);
2000 m_ui.actionPrint->setEnabled(
false);
2002 connect(m_ui.actionClose, &QAction::triggered,
this, &MainWindow::closeFile);
2003 connect(m_ui.actionCloseAll, &QAction::triggered,
this, &MainWindow::closeAll);
2004 connect(m_ui.actionExit, &QAction::triggered,
this, &MainWindow::close);
2007 connect(m_ui.menuEdit, &QMenu::aboutToShow,
this, &MainWindow::editAboutToShow);
2009 connect(m_ui.actionUndo, &QAction::triggered, m_messageEditor, &MessageEditor::undo);
2010 connect(m_messageEditor, &MessageEditor::undoAvailable, m_ui.actionUndo, &QAction::setEnabled);
2012 connect(m_ui.actionRedo, &QAction::triggered, m_messageEditor, &MessageEditor::redo);
2013 connect(m_messageEditor, &MessageEditor::redoAvailable, m_ui.actionRedo, &QAction::setEnabled);
2015#ifndef QT_NO_CLIPBOARD
2016 connect(m_ui.actionCut, &QAction::triggered, m_messageEditor, &MessageEditor::cut);
2017 connect(m_messageEditor, &MessageEditor::cutAvailable, m_ui.actionCut, &QAction::setEnabled);
2019 connect(m_ui.actionCopy, &QAction::triggered, m_messageEditor, &MessageEditor::copy);
2020 connect(m_messageEditor, &MessageEditor::copyAvailable, m_ui.actionCopy, &QAction::setEnabled);
2022 connect(m_ui.actionPaste, &QAction::triggered, m_messageEditor, &MessageEditor::paste);
2023 connect(m_messageEditor, &MessageEditor::pasteAvailable, m_ui.actionPaste, &QAction::setEnabled);
2026 connect(m_ui.actionSelectAll, &QAction::triggered,
2027 m_messageEditor, &MessageEditor::selectAll);
2028 connect(m_ui.actionFind, &QAction::triggered,
2029 m_findDialog, &FindDialog::find);
2030 connect(m_ui.actionFindNext, &QAction::triggered,
2031 this, [
this] {findAgain(FindNext);});
2032 connect(m_ui.actionFindPrev, &QAction::triggered,
2033 this, [
this] {findAgain(FindPrev);});
2034 connect(m_ui.actionSearchAndTranslate, &QAction::triggered,
2035 this, &MainWindow::showTranslateDialog);
2036 connect(m_ui.actionBatchTranslation, &QAction::triggered,
2037 this, &MainWindow::showBatchTranslateDialog);
2038 connect(m_ui.actionTranslationFileSettings, &QAction::triggered,
2039 this, &MainWindow::showTranslationSettings);
2041 connect(m_batchTranslateDialog, &BatchTranslationDialog::finished,
2042 this, &MainWindow::refreshItemViews);
2046 connect(m_ui.actionAuto_Translation, &QAction::triggered,
this,
2047 &MainWindow::openMachineTranslateDialog);
2048 connect(m_ui.actionPrevUnfinished, &QAction::triggered,
this, &MainWindow::prevUnfinished);
2049 connect(m_ui.actionNextUnfinished, &QAction::triggered,
this, &MainWindow::nextUnfinished);
2050 connect(m_ui.actionNext, &QAction::triggered,
this, &MainWindow::next);
2051 connect(m_ui.actionPrev, &QAction::triggered,
this, &MainWindow::prev);
2052 connect(m_ui.actionDone, &QAction::triggered,
this, &MainWindow::done);
2053 connect(m_ui.actionDoneAndNext, &QAction::triggered,
this, &MainWindow::doneAndNext);
2054 connect(m_ui.actionBeginFromSource, &QAction::triggered, m_messageEditor,
2055 &MessageEditor::beginFromSource);
2058 connect(m_ui.actionNewPhraseBook, &QAction::triggered,
this, &MainWindow::newPhraseBook);
2059 connect(m_ui.actionOpenPhraseBook, &QAction::triggered,
this, &MainWindow::openPhraseBook);
2060 connect(m_ui.menuClosePhraseBook, &QMenu::triggered,
2061 this, &MainWindow::closePhraseBook);
2062 connect(m_ui.menuEditPhraseBook, &QMenu::triggered,
2063 this, &MainWindow::editPhraseBook);
2064#if QT_CONFIG(printsupport)
2065 connect(m_ui.menuPrintPhraseBook, &QMenu::triggered,
2066 this, &MainWindow::printPhraseBook);
2068 m_ui.menuPrintPhraseBook->setEnabled(
false);
2070 connect(m_ui.actionAddToPhraseBook, &QAction::triggered,
2071 this, &MainWindow::addToPhraseBook);
2074 connect(m_ui.actionAccelerators, &QAction::triggered,
this, &MainWindow::revalidate);
2075 connect(m_ui.actionSurroundingWhitespace, &QAction::triggered,
this, &MainWindow::revalidate);
2076 connect(m_ui.actionEndingPunctuation, &QAction::triggered,
this, &MainWindow::revalidate);
2077 connect(m_ui.actionPhraseMatches, &QAction::triggered,
this, &MainWindow::revalidate);
2078 connect(m_ui.actionPlaceMarkerMatches, &QAction::triggered,
this, &MainWindow::revalidate);
2081 connect(m_ui.actionResetSorting, &QAction::triggered,
2082 this, &MainWindow::resetSorting);
2083 connect(m_ui.actionDisplayGuesses, &QAction::triggered,
2084 m_phraseView, &PhraseView::toggleGuessing);
2085 connect(m_ui.actionStatistics, &QAction::triggered,
this, &MainWindow::showStatistics);
2086 connect(m_ui.actionQmlPreview, &QAction::triggered,
this, &MainWindow::toggleQmlPreview);
2087 connect(m_ui.actionVisualizeWhitespace, &QAction::triggered,
2088 this, &MainWindow::toggleVisualizeWhitespace);
2089 connect(m_ui.actionIncreaseZoom, &QAction::triggered,
2090 m_messageEditor, &MessageEditor::increaseFontSize);
2091 connect(m_ui.actionDecreaseZoom, &QAction::triggered,
2092 m_messageEditor, &MessageEditor::decreaseFontSize);
2093 connect(m_ui.actionResetZoomToDefault, &QAction::triggered,
2094 m_messageEditor, &MessageEditor::resetFontSize);
2095 connect(m_ui.actionShowMoreGuesses, &QAction::triggered,
2096 m_phraseView, &PhraseView::moreGuesses);
2097 connect(m_ui.actionShowFewerGuesses, &QAction::triggered,
2098 m_phraseView, &PhraseView::fewerGuesses);
2099 connect(m_phraseView, &PhraseView::showFewerGuessesAvailable,
2100 m_ui.actionShowFewerGuesses, &QAction::setEnabled);
2101 connect(m_ui.actionResetGuessesToDefault, &QAction::triggered,
2102 m_phraseView, &PhraseView::resetNumGuesses);
2103 m_ui.menuViewViews->addAction(m_contextAndLabelDock->toggleViewAction());
2104 m_ui.menuViewViews->addAction(m_messagesDock->toggleViewAction());
2105 m_ui.menuViewViews->addAction(m_phrasesDock->toggleViewAction());
2106 m_ui.menuViewViews->addAction(m_sourceAndFormDock->toggleViewAction());
2107 m_ui.menuViewViews->addAction(m_errorsDock->toggleViewAction());
2109#if defined(Q_OS_MAC)
2111 QMenu *windowMenu =
new QMenu(tr(
"&Window"),
this);
2112 menuBar()->insertMenu(m_ui.menuHelp->menuAction(), windowMenu);
2113 windowMenu->addAction(tr(
"Minimize"), QKeySequence(tr(
"Ctrl+M")),
2114 this, &QWidget::showMinimized);
2118 connect(m_ui.actionManual, &QAction::triggered,
this, &MainWindow::manual);
2119 connect(m_ui.actionAbout, &QAction::triggered,
this, &MainWindow::about);
2120 connect(m_ui.actionAboutQt, &QAction::triggered,
this, &MainWindow::aboutQt);
2121 connect(m_ui.actionWhatsThis, &QAction::triggered,
this, &MainWindow::onWhatsThis);
2123 connect(m_ui.menuRecentlyOpenedFiles, &QMenu::triggered,
2124 this, &MainWindow::recentFileActivated);
2126 m_ui.actionManual->setToolTip(tr(
"Displays the manual for %1.").arg(tr(
"Qt Linguist")));
2127 m_ui.actionAbout->setToolTip(tr(
"Displays information about %1.").arg(tr(
"Qt Linguist")));
2128 m_ui.actionDone->setShortcuts(
2129 { Qt::AltModifier | Qt::Key_Return, Qt::AltModifier | Qt::Key_Enter });
2130 m_ui.actionDoneAndNext->setShortcuts({
2131 Qt::ControlModifier | Qt::Key_Return,
2132 Qt::ControlModifier | Qt::Key_Enter,
2136 connect(m_ui.menuPhrases, &QMenu::aboutToShow,
this, &MainWindow::setupPhrase);
2138 connect(m_ui.menuRecentlyOpenedFiles, &QMenu::aboutToShow,
2139 this, &MainWindow::setupRecentFilesMenu);
2145 doUpdateLatestModel(model);
2149void MainWindow::updateLatestModel(
const QModelIndex &index)
2151 if (index.column() && (index.column() - 1 < m_dataModel
->modelCount()))
2152 doUpdateLatestModel(index.column() - 1);
2155void MainWindow::doUpdateLatestModel(
int model)
2159 bool enable =
false;
2160 bool enableRw =
false;
2169 m_phraseView->setSourceText(model, item->text());
2171 m_phraseView->setSourceText(-1, QString());
2173 m_phraseView->setSourceText(-1, QString());
2177 updateSourceView(model, item);
2178 m_ui.actionSave->setEnabled(enableRw);
2179 m_ui.actionSaveAs->setEnabled(enableRw);
2180 m_ui.actionRelease->setEnabled(enableRw);
2181 m_ui.actionReleaseAs->setEnabled(enableRw);
2182 m_ui.actionClose->setEnabled(enable);
2183 m_ui.actionTranslationFileSettings->setEnabled(enableRw);
2184 m_ui.actionSearchAndTranslate->setEnabled(enableRw);
2186 updatePhraseBookActions();
2192 if (item && !item->fileName().isEmpty()) {
2193 if (hasUiFormPreview(item->fileName())) {
2194 m_sourceAndFormView->setCurrentWidget(m_uiFormPreviewView);
2196 }
else if (hasQmlFormPreview(item->fileName(), m_ui.actionQmlPreview->isChecked())
2197 && m_qmlFormPreviewView->setSourceContext(model, item)) {
2198 m_sourceAndFormView->setCurrentWidget(m_qmlFormPreviewView);
2200 m_ui.actionQmlPreview->setChecked(
false);
2201 m_sourceAndFormView->setCurrentWidget(m_sourceCodeView);
2202 QDir dir = QFileInfo(m_dataModel->srcFileName(model)).dir();
2203 QString fileName = QDir::cleanPath(dir.absoluteFilePath(item->fileName()));
2204 m_sourceCodeView->setSourceContext(fileName, item
->lineNumber());
2207 m_sourceAndFormView->setCurrentWidget(m_sourceCodeView);
2208 m_sourceCodeView->setSourceContext(QString(), 0);
2217 if (m_fileActiveModel != m_currentIndex
.model()) {
2222 QString fn = QFileInfo(m_dataModel->srcFileName(m_currentIndex
.model())).baseName();
2224 m_ui.actionSave->setText(tr(
"&Save '%1'").arg(fn));
2225 m_ui.actionSaveAs->setText(tr(
"Save '%1' &As...").arg(fn));
2227 m_ui.actionSave->setText(tr(
"&Download '%1'").arg(fn));
2229 m_ui.actionRelease->setText(tr(
"Release '%1'").arg(fn));
2230 m_ui.actionReleaseAs->setText(tr(
"Release '%1' As...").arg(fn));
2231 m_ui.actionClose->setText(tr(
"&Close '%1'").arg(fn));
2234 m_ui.actionSave->setText(tr(
"&Save"));
2235 m_ui.actionSaveAs->setText(tr(
"Save &As..."));
2237 m_ui.actionSave->setText(tr(
"&Download"));
2239 m_ui.actionRelease->setText(tr(
"Release"));
2240 m_ui.actionReleaseAs->setText(tr(
"Release As..."));
2241 m_ui.actionClose->setText(tr(
"&Close"));
2245 m_ui.actionSaveAll->setText(tr(
"Save All"));
2247 m_ui.actionReleaseAll->setText(tr(
"&Release All"));
2248 m_ui.actionCloseAll->setText(tr(
"Close All"));
2252 m_ui.actionSaveAs->setText(tr(
"Save &As..."));
2253 m_ui.actionSaveAll->setText(tr(
"&Save"));
2255 m_ui.actionSave->setText(tr(
"&Download"));
2257 m_ui.actionReleaseAs->setText(tr(
"Release As..."));
2258 m_ui.actionReleaseAll->setText(tr(
"&Release"));
2259 m_ui.actionCloseAll->setText(tr(
"&Close"));
2263 m_ui.actionSave->setVisible(en);
2265 m_ui.actionRelease->setVisible(en);
2266 m_ui.actionClose->setVisible(en);
2267 m_fileActiveModel = m_currentIndex
.model();
2273 if (m_editActiveModel != m_currentIndex
.model()) {
2275 QString fn = QFileInfo(m_dataModel->srcFileName(m_currentIndex
.model())).baseName();
2276 m_ui.actionTranslationFileSettings->setText(tr(
"Translation File &Settings for '%1'...").arg(fn));
2277 m_ui.actionBatchTranslation->setText(tr(
"&Batch Translation of '%1'...").arg(fn));
2278 m_ui.actionSearchAndTranslate->setText(tr(
"Search And &Translate in '%1'...").arg(fn));
2280 m_ui.actionTranslationFileSettings->setText(tr(
"Translation File &Settings..."));
2281 m_ui.actionBatchTranslation->setText(tr(
"&Batch Translation..."));
2282 m_ui.actionSearchAndTranslate->setText(tr(
"Search And &Translate..."));
2284 m_editActiveModel = m_currentIndex
.model();
2290 m_contextAndLabelDock->show();
2291 m_contextAndLabelDock->raise();
2296 m_messagesDock->show();
2297 m_messagesDock->raise();
2302 m_phrasesDock->show();
2303 m_phrasesDock->raise();
2308 m_sourceAndFormDock->show();
2309 m_sourceAndFormDock->raise();
2314 m_errorsDock->show();
2315 m_errorsDock->raise();
2320 QWhatsThis::enterWhatsThisMode();
2325 QToolBar *filet =
new QToolBar(
this);
2326 filet->setObjectName(
"FileToolbar");
2327 filet->setWindowTitle(tr(
"File"));
2328 this->addToolBar(filet);
2329 m_ui.menuToolbars->addAction(filet->toggleViewAction());
2331 QToolBar *editt =
new QToolBar(
this);
2332 editt->setVisible(
false);
2333 editt->setObjectName(
"EditToolbar");
2334 editt->setWindowTitle(tr(
"Edit"));
2335 this->addToolBar(editt);
2336 m_ui.menuToolbars->addAction(editt->toggleViewAction());
2338 QToolBar *translationst =
new QToolBar(
this);
2339 translationst->setObjectName(
"TranslationToolbar");
2340 translationst->setWindowTitle(tr(
"Translation"));
2341 this->addToolBar(translationst);
2342 m_ui.menuToolbars->addAction(translationst->toggleViewAction());
2344 QToolBar *validationt =
new QToolBar(
this);
2345 validationt->setObjectName(
"ValidationToolbar");
2346 validationt->setWindowTitle(tr(
"Validation"));
2347 this->addToolBar(validationt);
2348 m_ui.menuToolbars->addAction(validationt->toggleViewAction());
2350 QToolBar *helpt =
new QToolBar(
this);
2351 helpt->setVisible(
false);
2352 helpt->setObjectName(
"HelpToolbar");
2353 helpt->setWindowTitle(tr(
"Help"));
2354 this->addToolBar(helpt);
2355 m_ui.menuToolbars->addAction(helpt->toggleViewAction());
2358 filet->addAction(m_ui.actionOpen);
2359 filet->addAction(m_ui.actionSaveAll);
2360 filet->addAction(m_ui.actionPrint);
2361 filet->addSeparator();
2362 filet->addAction(m_ui.actionOpenPhraseBook);
2364 editt->addAction(m_ui.actionUndo);
2365 editt->addAction(m_ui.actionRedo);
2366 editt->addSeparator();
2367 editt->addAction(m_ui.actionCut);
2368 editt->addAction(m_ui.actionCopy);
2369 editt->addAction(m_ui.actionPaste);
2370 editt->addSeparator();
2371 editt->addAction(m_ui.actionFind);
2373 translationst->addAction(m_ui.actionPrev);
2374 translationst->addAction(m_ui.actionNext);
2375 translationst->addAction(m_ui.actionPrevUnfinished);
2376 translationst->addAction(m_ui.actionNextUnfinished);
2377 translationst->addAction(m_ui.actionDone);
2378 translationst->addAction(m_ui.actionDoneAndNext);
2380 validationt->addAction(m_ui.actionAccelerators);
2381 validationt->addAction(m_ui.actionSurroundingWhitespace);
2382 validationt->addAction(m_ui.actionEndingPunctuation);
2383 validationt->addAction(m_ui.actionPhraseMatches);
2384 validationt->addAction(m_ui.actionPlaceMarkerMatches);
2386 helpt->addAction(m_ui.actionWhatsThis);
2389QModelIndex
MainWindow::setMessageViewRoot(
const QModelIndex &index)
2391 const QModelIndex &sortedGroupIndex = m_activeSortedMessagesModel->mapFromSource(index);
2392 const QModelIndex &trueGroupIndex =
2393 m_activeSortedMessagesModel->index(sortedGroupIndex.row(), 0);
2394 if (m_messageView->rootIndex() != trueGroupIndex)
2395 m_messageView->setRootIndex(trueGroupIndex);
2396 return trueGroupIndex;
2400
2401
2402void MainWindow::setCurrentMessage(
const QModelIndex &index)
2404 const QModelIndex &groupIndex = m_activeMessageModel->parent(index);
2405 if (!groupIndex.isValid())
2408 const QModelIndex &trueIndex =
2409 m_activeMessageModel->index(groupIndex.row(), index.column(), QModelIndex());
2410 m_settingCurrentMessage =
true;
2411 QTreeView *view = *m_activeTranslationType == IDBASED ? m_labelView : m_contextView;
2412 view->setCurrentIndex(m_activeSortedGroupsModel->mapFromSource(trueIndex));
2413 m_settingCurrentMessage =
false;
2414 setMessageViewRoot(groupIndex);
2415 m_messageView->setCurrentIndex(m_activeSortedMessagesModel->mapFromSource(index));
2418void MainWindow::setCurrentMessage(
const QModelIndex &index,
int model)
2420 const QModelIndex &theIndex =
2421 m_activeMessageModel->index(index.row(), model + 1, index.parent());
2422 setCurrentMessage(theIndex);
2428 if (cand.context.isEmpty()) {
2429 int labelIndex = m_dataModel->findGroupIndex(cand.label,
IDBASED);
2432 setCurrentMessage(m_activeMessageModel->modelIndex(
2433 MultiDataIndex(IDBASED, modelIndex, labelIndex, messageIndex)));
2435 int contextIndex = m_dataModel->findGroupIndex(cand.context,
TEXTBASED);
2437 ->findMessage(cand.source, cand.disambiguation);
2438 setCurrentMessage(m_activeMessageModel->modelIndex(
2439 MultiDataIndex(TEXTBASED, modelIndex, contextIndex, messageIndex)));
2445 auto refreshMessageView = [
this](QTreeView *view) {
2446 m_messageView->reset();
2447 m_messageView->setModel(m_activeSortedMessagesModel);
2448 view->setCurrentIndex(m_activeSortedGroupsModel->index(0, 0));
2449 connect(m_messageView->selectionModel(), &QItemSelectionModel::currentRowChanged,
this,
2450 &MainWindow::selectedMessageChanged);
2451 connect(m_messageView->selectionModel(), &QItemSelectionModel::currentColumnChanged,
this,
2452 &MainWindow::updateLatestModel);
2453 m_messageView->update();
2454 if (m_activeMessageModel->rowCount())
2455 setCurrentMessage(m_activeMessageModel->modelIndex(
2456 MultiDataIndex(*m_activeTranslationType, 0, 0, 0)));
2457 selectedMessageChanged(m_messageView->currentIndex(), QModelIndex{});
2458 updateVisibleColumns();
2461 if (m_contextAndLabelView->currentWidget() == m_labelView
2462 && (!m_activeTranslationType || *m_activeTranslationType != IDBASED)) {
2463 m_activeTranslationType.emplace(IDBASED);
2464 m_activeSortedMessagesModel = m_sortedIdBasedMessagesModel;
2465 m_activeSortedGroupsModel = m_sortedLabelsModel;
2466 m_activeMessageModel = m_idBasedMessageModel;
2467 refreshMessageView(m_labelView);
2468 }
else if (m_contextAndLabelView->currentWidget() == m_contextView
2469 && (!m_activeTranslationType || *m_activeTranslationType != TEXTBASED)) {
2470 m_activeTranslationType.emplace(TEXTBASED);
2471 m_activeSortedMessagesModel = m_sortedTextBasedMessagesModel;
2472 m_activeSortedGroupsModel = m_sortedContextsModel;
2473 m_activeMessageModel = m_textBasedMessageModel;
2474 refreshMessageView(m_contextView);
2481 if (*m_activeTranslationType == IDBASED)
2483 for (
int i = 1; i < cols; i++)
2484 m_messageView->setColumnHidden(i,
false);
2485 for (
int i = cols; i < m_messageView->header()->count(); i++)
2486 m_messageView->setColumnHidden(i,
true);
2487 m_messageView->header()->setStretchLastSection(
true);
2490QModelIndex
MainWindow::currentMessageIndex()
const
2492 return m_activeSortedMessagesModel->mapToSource(m_messageView->currentIndex());
2499 if (!pb->load(name, &langGuessed)) {
2500 QMessageBox::warning(
this, tr(
"Qt Linguist"),
2501 tr(
"Cannot read from phrase book '%1'.").arg(name));
2506 if (!m_translationSettingsDialog)
2507 m_translationSettingsDialog =
new TranslationSettingsDialog(
this);
2509 m_translationSettingsDialog->exec();
2512 m_phraseBooks.append(pb);
2514 QAction *a = m_ui.menuClosePhraseBook->addAction(pb->friendlyPhraseBookName());
2515 m_phraseBookMenu[PhraseCloseMenu].insert(a, pb);
2516 a->setToolTip(tr(
"Close this phrase book."));
2518 a = m_ui.menuEditPhraseBook->addAction(pb->friendlyPhraseBookName());
2519 m_phraseBookMenu[PhraseEditMenu].insert(a, pb);
2520 a->setToolTip(tr(
"Enables you to add, modify, or delete"
2521 " entries in this phrase book."));
2523 a = m_ui.menuPrintPhraseBook->addAction(pb->friendlyPhraseBookName());
2524 m_phraseBookMenu[PhrasePrintMenu].insert(a, pb);
2525 a->setToolTip(tr(
"Print the entries in this phrase book."));
2528 updatePhraseDicts();
2529 updatePhraseBookActions();
2536 if (!name->contains(u'.'))
2539 if (!pb.save(*name)) {
2540 QMessageBox::warning(
this, tr(
"Qt Linguist"),
2541 tr(
"Cannot create phrase book '%1'.").arg(*name));
2550 switch (QMessageBox::information(
this, tr(
"Qt Linguist"),
2551 tr(
"Do you want to save phrase book '%1'?").arg(pb->friendlyPhraseBookName()),
2552 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
2554 case QMessageBox::Cancel:
2556 case QMessageBox::Yes:
2557 if (!pb->save(pb->fileName()))
2568 for (PhraseBook *phraseBook : std::as_const(m_phraseBooks))
2569 if (!maybeSavePhraseBook(phraseBook))
2579 m_progressLabel->setText(QString(
" "_L1));
2580 m_progressLabel->setToolTip(QString());
2582 m_progressLabel->setText(QStringLiteral(
" %1/%2 ").arg(numFinished).arg(numEditable));
2583 m_progressLabel->setToolTip(tr(
"%n unfinished message(s) left.", 0,
2584 numEditable - numFinished));
2586 bool enable = numFinished != numEditable;
2587 m_ui.actionPrevUnfinished->setEnabled(enable);
2588 m_ui.actionNextUnfinished->setEnabled(enable);
2589 m_ui.actionDone->setEnabled(enable);
2590 m_ui.actionDoneAndNext->setEnabled(enable);
2592 m_ui.actionPrev->setEnabled(m_dataModel->contextCount() > 0 || m_dataModel->labelCount() > 0);
2593 m_ui.actionNext->setEnabled(m_dataModel->contextCount() > 0 || m_dataModel->labelCount() > 0);
2598 bool phraseBookLoaded = (m_currentIndex.model() >= 0) && !m_phraseBooks.isEmpty();
2599 m_ui.actionBatchTranslation->setEnabled(m_dataModel->contextCount() > 0 && phraseBookLoaded
2600 && m_dataModel->isModelWritable(m_currentIndex.model()));
2601 m_ui.actionAddToPhraseBook->setEnabled(currentMessageIndex().isValid() && phraseBookLoaded);
2604void MainWindow::updatePhraseDictInternal(
int model)
2606 QHash<QString, QList<Phrase *> > &pd = m_phraseDict[model];
2609 for (PhraseBook *pb : std::as_const(m_phraseBooks)) {
2611 if (pb->language() != QLocale::C && m_dataModel->language(model) != QLocale::C) {
2612 if (pb->language() != m_dataModel->language(model))
2614 before = (pb->territory() == m_dataModel->model(model)->territory());
2618 const auto phrases = pb->phrases();
2619 for (Phrase *p : phrases) {
2620 QString f = friendlyString(p->source());
2622 f = f.split(u' ').first();
2623 if (!pd.contains(f)) {
2624 pd.insert(f, QList<Phrase *>());
2637 updatePhraseDictInternal(model);
2643 for (
int i = 0; i < m_phraseDict.size(); ++i)
2645 m_phraseDict[i].clear();
2647 updatePhraseDictInternal(i);
2659 Validator::Checks checks{ m_ui.actionAccelerators->isChecked(),
2660 m_ui.actionEndingPunctuation->isChecked(),
2661 m_ui.actionPlaceMarkerMatches->isChecked(),
2662 m_ui.actionSurroundingWhitespace->isChecked(),
2663 m_ui.actionPhraseMatches->isChecked() };
2673 bool danger =
false;
2675 if (source.isEmpty()) {
2676 source = m->pluralText();
2677 if (source.isEmpty())
2681 Validator validator = Validator::fromSource(
2682 source, checks, m_dataModel->sourceLanguage(mi), m_phraseDict[mi]);
2684 validator.validate(m->translations(), m
->message(), m_dataModel->language(mi),
2685 m_dataModel
->model(mi
)->countRefNeeds());
2687 for (
const auto &[error, message] : errors.asKeyValueRange())
2688 m_errorsView->addError(mi, error, message);
2696 statusBar()->showMessage(m_errorsView->firstError());
2703 restoreGeometry(config.value(settingPath(
"Geometry/WindowGeometry")).toByteArray());
2704 restoreState(config.value(settingPath(
"MainWindowState")).toByteArray());
2706 m_ui.actionAccelerators->setChecked(
2707 config.value(settingPath(
"Validators/Accelerator"),
true).toBool());
2708 m_ui.actionSurroundingWhitespace->setChecked(
2709 config.value(settingPath(
"Validators/SurroundingWhitespace"),
true).toBool());
2710 m_ui.actionEndingPunctuation->setChecked(
2711 config.value(settingPath(
"Validators/EndingPunctuation"),
true).toBool());
2712 m_ui.actionPhraseMatches->setChecked(
2713 config.value(settingPath(
"Validators/PhraseMatch"),
true).toBool());
2714 m_ui.actionPlaceMarkerMatches->setChecked(
2715 config.value(settingPath(
"Validators/PlaceMarkers"),
true).toBool());
2716 m_ui.actionLengthVariants->setChecked(
2717 config.value(settingPath(
"Options/LengthVariants"),
false).toBool());
2718 m_ui.actionVisualizeWhitespace->setChecked(
2719 config.value(settingPath(
"Options/VisualizeWhitespace"),
true).toBool());
2721 m_messageEditor->setFontSize(
2722 config.value(settingPath(
"Options/EditorFontsize"), font().pointSize()).toReal());
2723 m_phraseView->setMaxCandidates(config.value(settingPath(
"Options/NumberOfGuesses"),
2724 PhraseView::getDefaultMaxCandidates()).toInt());
2726 m_recentFiles.readConfig();
2728 int size = config.beginReadArray(settingPath(
"OpenedPhraseBooks"));
2729 for (
int i = 0; i < size; ++i) {
2730 config.setArrayIndex(i);
2731 doOpenPhraseBook(config.value(
"FileName"_L1).toString());
2739 config.setValue(settingPath(
"Geometry/WindowGeometry"),
2741 config.setValue(settingPath(
"Validators/Accelerator"),
2742 m_ui.actionAccelerators->isChecked());
2743 config.setValue(settingPath(
"Validators/SurroundingWhitespace"),
2744 m_ui.actionSurroundingWhitespace->isChecked());
2745 config.setValue(settingPath(
"Validators/EndingPunctuation"),
2746 m_ui.actionEndingPunctuation->isChecked());
2747 config.setValue(settingPath(
"Validators/PhraseMatch"),
2748 m_ui.actionPhraseMatches->isChecked());
2749 config.setValue(settingPath(
"Validators/PlaceMarkers"),
2750 m_ui.actionPlaceMarkerMatches->isChecked());
2751 config.setValue(settingPath(
"Options/LengthVariants"),
2752 m_ui.actionLengthVariants->isChecked());
2753 config.setValue(settingPath(
"Options/VisualizeWhitespace"),
2754 m_ui.actionVisualizeWhitespace->isChecked());
2755 config.setValue(settingPath(
"MainWindowState"),
2757 m_recentFiles.writeConfig();
2759 config.setValue(settingPath(
"Options/EditorFontsize"), m_messageEditor->fontSize());
2760 config.setValue(settingPath(
"Options/NumberOfGuesses"), m_phraseView->getMaxCandidates());
2762 config.beginWriteArray(settingPath(
"OpenedPhraseBooks"),
2763 m_phraseBooks.size());
2764 for (
int i = 0; i < m_phraseBooks.size(); ++i) {
2765 config.setArrayIndex(i);
2766 config.setValue(
"FileName"_L1, m_phraseBooks.at(i)->fileName());
2773 m_ui.menuRecentlyOpenedFiles->clear();
2774 for (
const QStringList &strList : m_recentFiles.filesLists())
2775 if (strList.size() == 1) {
2776 const QString &str = strList.first();
2777 m_ui.menuRecentlyOpenedFiles->addAction(
2778 DataModel::prettifyFileName(str))->setData(str);
2780 QMenu *menu = m_ui.menuRecentlyOpenedFiles->addMenu(
2781 MultiDataModel::condenseFileNames(
2782 MultiDataModel::prettifyFileNames(strList)));
2783 menu->addAction(tr(
"All"))->setData(strList);
2784 for (
const QString &str : strList)
2785 menu->addAction(DataModel::prettifyFileName(str))->setData(str);
2789void MainWindow::recentFileActivated(QAction *action)
2791 openFiles(action->data().toStringList());
2796 if (!m_statistics) {
2797 m_statistics =
new Statistics(
this);
2798 connect(m_dataModel, &MultiDataModel::statsChanged, m_statistics, &Statistics::updateStats);
2800 m_statistics->show();
2806 if (m_ui.actionQmlPreview->isChecked())
2807 m_sourceAndFormView->setCurrentWidget(m_qmlFormPreviewView);
2809 m_sourceAndFormView->setCurrentWidget(m_sourceCodeView);
2814 m_messageEditor->setVisualizeWhitespace(m_ui.actionVisualizeWhitespace->isChecked());
2827 if (!m_statistics || !m_statistics->isVisible() || m_currentIndex
.model() < 0)
2833void MainWindow::doShowTranslationSettings(
int model)
2835 if (!m_translationSettingsDialog)
2836 m_translationSettingsDialog =
new TranslationSettingsDialog(
this);
2838 m_translationSettingsDialog->exec();
2843 doShowTranslationSettings(m_currentIndex
.model());
2848 if (event->type() == QEvent::DragEnter) {
2849 QDragEnterEvent *e =
static_cast<QDragEnterEvent*>(event);
2850 if (e->mimeData()->hasFormat(
"text/uri-list"_L1)) {
2851 e->acceptProposedAction();
2854 }
else if (event->type() == QEvent::Drop) {
2855 QDropEvent *e =
static_cast<QDropEvent*>(event);
2856 if (!e->mimeData()->hasFormat(
"text/uri-list"_L1))
2859 for (
const QUrl &url : e->mimeData()->urls())
2860 if (!url.toLocalFile().isEmpty())
2861 urls << url.toLocalFile();
2862 if (!urls.isEmpty())
2864 e->acceptProposedAction();
2866 }
else if (event->type() == QEvent::KeyPress) {
2867 QKeyEvent *ke =
static_cast<QKeyEvent *>(event);
2868 if (ke->key() == Qt::Key_Escape) {
2869 if (object == m_messageEditor)
2870 m_messageView->setFocus();
2871 else if (object == m_messagesDock)
2872 m_contextAndLabelView->currentWidget()->setFocus();
2873 }
else if ((ke->key() == Qt::Key_Plus || ke->key() == Qt::Key_Equal)
2874 && (ke->modifiers() & Qt::ControlModifier)) {
2876 }
else if (ke->key() == Qt::Key_Minus
2877 && (ke->modifiers() & Qt::ControlModifier)) {
2880 }
else if (event->type() == QEvent::Wheel) {
2881 QWheelEvent *we =
static_cast<QWheelEvent *>(event);
2882 if (we->modifiers() & Qt::ControlModifier) {
2883 if (we->angleDelta().y() > 0)
2888 }
else if (event->type() == QEvent::ApplicationPaletteChange) {
2892 return QMainWindow::eventFilter(object, event);
TranslationType translationType() const
FocusWatcher(MessageEditor *msgedit, QObject *parent)
bool eventFilter(QObject *object, QEvent *event) override
Filters events if this object has been installed as an event filter for the watched object.
void setDataModel(MultiDataModel *dm)
void closeEvent(QCloseEvent *e) override
This event handler is called with the given event when Qt receives a window close request for a top-l...
bool openFiles(const QStringList &names)
bool eventFilter(QObject *obj, QEvent *ev) override
Filters events if this object has been installed as an event filter for the watched object.
void setEditorFocusForModel(int model)
void setUnfinishedEditorFocus()
bool focusNextUnfinished()
void activeModelChanged(int model)
void showMessage(const MultiDataIndex &index)
const TranslatorMessage & message() const
TranslatorMessage::Type type() const
MultiDataIndex(TranslationType type=TEXTBASED, int model=-1, int group=-1, int message=-1)
MultiDataModelIterator(TranslationType type, MultiDataModel *model=0, int modelNo=-1, int groupNo=0, int messageNo=0)
MessageItem * current() const
bool isModified(int model) const
MessageItem * messageItem(const MultiDataIndex &index, int model) const
void setModified(int model, bool dirty)
MultiGroupItem * multiGroupItem(const MultiDataIndex &index) const
MultiGroupItem * multiGroupItem(int idx, TranslationType type) const
void languageChanged(int model)
int getNumFinished() const
void setDanger(const MultiDataIndex &index, bool danger)
bool isModelWritable(int model) const
MultiMessageItem * multiMessageItem(const MultiDataIndex &index) const
int getNumEditable() const
void translationChanged(const MultiDataIndex &index)
void multiGroupDataChanged(const MultiDataIndex &index)
MessageItem * messageItem(const MultiDataIndex &index) const
void setFinished(const MultiDataIndex &index, bool finished)
void messageDataChanged(const MultiDataIndex &index)
void setCurrentMessageFromGuess(int modelIndex, const Candidate &cand)
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
\reimp
SortedGroupsModel(QObject *parent, MultiDataModel *model, TranslationType translationType)
SortedMessagesModel(QObject *parent, MultiDataModel *model, TranslationType translationType)
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
\reimp
void setDataModel(DataModel *model)
void setPhraseBook(PhraseBook *phraseBook)
bool isTranslated() const
static QString fileFilters(bool allFirst)
static const QVariant & pxObsolete()
int firstNonobsoleteMessageIndex(int msgIdx) const
bool isUnfinished() const
OpenedFile(DataModel *_dataModel, bool _readWrite, bool _langGuessed)