5
6
7
15#include <helpclient.h>
34#include <QApplication>
35#include <QtWidgets/private/qdialog_p.h>
40#include <QDesktopServices>
48#include <QInputDialog>
49#include <QItemDelegate>
52#include <QLibraryInfo>
58#include <QRegularExpression>
62#include <QSortFilterProxyModel>
63#include <QStackedWidget>
71#if QT_CONFIG(printsupport)
72#include <QPrintDialog>
76using namespace Qt::Literals::StringLiterals;
82class GroupItemDelegate :
public QItemDelegate
85 GroupItemDelegate(QObject *parent, MultiDataModel *model)
86 : QItemDelegate(parent), m_dataModel(model)
90 void paint(QPainter *painter,
const QStyleOptionViewItem &option,
91 const QModelIndex &index)
const override
93 const QAbstractItemModel *model = index.model();
96 if (!model->parent(index).isValid()) {
97 if (index.column() - 1 == m_dataModel->modelCount()) {
98 QStyleOptionViewItem opt = option;
99 opt.font.setBold(
true);
100 QItemDelegate::paint(painter, opt, index);
104 QItemDelegate::paint(painter, option, index);
108 MultiDataModel *m_dataModel;
113 static const QVariant v = createMarkIcon(TranslationMarks::ObsoleteMark);
128 if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
134 return MainWindow::tr(
"Source text");
138 case 0:
return QString();
140 return MainWindow::tr(
"ID");
142 return MainWindow::tr(
"Source text");
146 if (role == Qt::DecorationRole && orientation == Qt::Horizontal && section - 1 < m_dataModel->modelCount())
167 if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
169 case 0:
return QString();
171 return m_translationType == TEXTBASED ? MainWindow::tr(
"Context")
172 : MainWindow::tr(
"Label");
174 return MainWindow::tr(
"Items");
177 if (role == Qt::DecorationRole && orientation == Qt::Horizontal && section - 1 < m_dataModel->modelCount())
202 if (event->type() == QEvent::FocusIn)
210#if QT_CONFIG(printsupport)
214 m_translationSettingsDialog(0),
215 m_settingCurrentMessage(
false),
216 m_fileActiveModel(-1),
217 m_editActiveModel(-1),
221 setUnifiedTitleAndToolBarOnMac(
true);
224#if !defined(Q_OS_MACOS) && !defined(Q_OS_WIN)
225 setWindowIcon(QPixmap(
":/images/appicon.png"_L1));
228 m_dataModel =
new MultiDataModel(
this);
229 m_idBasedMessageModel =
new MessageModel(IDBASED,
this, m_dataModel);
230 m_textBasedMessageModel =
new MessageModel(TEXTBASED,
this, m_dataModel);
233 m_contextAndLabelDock =
new QDockWidget(
this);
234 m_contextAndLabelDock->setObjectName(
"ContextLabelDockWidget");
235 m_contextAndLabelDock->setAllowedAreas(Qt::AllDockWidgetAreas);
236 m_contextAndLabelDock->setWindowTitle(tr(
"Context/Label"));
237 m_contextAndLabelDock->setAcceptDrops(
true);
238 m_contextAndLabelDock->installEventFilter(
this);
240 m_contextAndLabelView =
new QTabWidget(
this);
242 QWidget* dockContent =
new QWidget(
this);
243 QBoxLayout* layout =
new QBoxLayout(QBoxLayout::LeftToRight, dockContent);
244 layout->addWidget(m_contextAndLabelView);
246 m_contextAndLabelDock->setWidget(dockContent);
249 m_sortedContextsModel =
new SortedGroupsModel(
this, m_dataModel, TEXTBASED);
250 m_sortedContextsModel->setSortRole(MessageModel::SortRole);
251 m_sortedContextsModel->setSortCaseSensitivity(Qt::CaseInsensitive);
252 m_sortedContextsModel->setSourceModel(m_textBasedMessageModel);
254 m_contextView =
new QTreeView(
this);
255 m_contextView->setRootIsDecorated(
false);
256 m_contextView->setItemsExpandable(
false);
257 m_contextView->setUniformRowHeights(
true);
258 m_contextView->setAlternatingRowColors(
true);
259 m_contextView->setAllColumnsShowFocus(
true);
260 m_contextView->setItemDelegate(
new GroupItemDelegate(
this, m_dataModel));
261 m_contextView->setSortingEnabled(
true);
262 m_contextView->setWhatsThis(tr(
"This panel lists the source contexts."));
263 m_contextView->setModel(m_sortedContextsModel);
264 m_contextView->header()->setSectionsMovable(
false);
265 m_contextView->setColumnHidden(0,
true);
266 m_contextView->header()->setStretchLastSection(
false);
268 m_contextAndLabelView->addTab(m_contextView,
"Text Based"_L1);
271 m_sortedLabelsModel =
new SortedGroupsModel(
this, m_dataModel, IDBASED);
272 m_sortedLabelsModel->setSortRole(MessageModel::SortRole);
273 m_sortedLabelsModel->setSortCaseSensitivity(Qt::CaseInsensitive);
274 m_sortedLabelsModel->setSourceModel(m_idBasedMessageModel);
276 m_labelView =
new QTreeView(
this);
277 m_labelView->setRootIsDecorated(
false);
278 m_labelView->setItemsExpandable(
false);
279 m_labelView->setUniformRowHeights(
true);
280 m_labelView->setAlternatingRowColors(
true);
281 m_labelView->setAllColumnsShowFocus(
true);
282 m_labelView->setItemDelegate(
new GroupItemDelegate(
this, m_dataModel));
283 m_labelView->setSortingEnabled(
true);
284 m_labelView->setWhatsThis(tr(
"This panel lists the source labels."));
285 m_labelView->setModel(m_sortedLabelsModel);
286 m_labelView->header()->setSectionsMovable(
false);
287 m_labelView->setColumnHidden(0,
true);
288 m_labelView->header()->setStretchLastSection(
false);
290 m_contextAndLabelView->addTab(m_labelView,
"ID Based"_L1);
293 m_messagesDock =
new QDockWidget(
this);
294 m_messagesDock->setObjectName(
"StringsDockWidget");
295 m_messagesDock->setAllowedAreas(Qt::AllDockWidgetAreas);
296 m_messagesDock->setWindowTitle(tr(
"Strings"));
297 m_messagesDock->setAcceptDrops(
true);
298 m_messagesDock->installEventFilter(
this);
300 m_sortedTextBasedMessagesModel =
new SortedMessagesModel(
this, m_dataModel, TEXTBASED);
301 m_sortedTextBasedMessagesModel->setSortRole(MessageModel::SortRole);
302 m_sortedTextBasedMessagesModel->setSortCaseSensitivity(Qt::CaseInsensitive);
303 m_sortedTextBasedMessagesModel->setSortLocaleAware(
true);
304 m_sortedTextBasedMessagesModel->setSourceModel(m_textBasedMessageModel);
306 m_sortedIdBasedMessagesModel =
new SortedMessagesModel(
this, m_dataModel, IDBASED);
307 m_sortedIdBasedMessagesModel->setSortRole(MessageModel::SortRole);
308 m_sortedIdBasedMessagesModel->setSortCaseSensitivity(Qt::CaseInsensitive);
309 m_sortedIdBasedMessagesModel->setSortLocaleAware(
true);
310 m_sortedIdBasedMessagesModel->setSourceModel(m_idBasedMessageModel);
312 m_messageView =
new QTreeView(m_messagesDock);
313 m_messageView->setSortingEnabled(
true);
314 m_messageView->setRootIsDecorated(
false);
315 m_messageView->setUniformRowHeights(
true);
316 m_messageView->setAllColumnsShowFocus(
true);
317 m_messageView->setItemsExpandable(
false);
318 m_messageView->setModel(m_sortedTextBasedMessagesModel);
319 m_messageView->header()->setSectionsMovable(
false);
320 m_messageView->setColumnHidden(0,
true);
322 m_messagesDock->setWidget(m_messageView);
325 m_messageEditor =
new MessageEditor(m_dataModel,
this);
326 m_messageEditor->setAcceptDrops(
true);
327 m_messageEditor->installEventFilter(
this);
329 QBoxLayout *lout =
new QBoxLayout(QBoxLayout::TopToBottom, m_ui.centralwidget);
330 lout->addWidget(m_messageEditor);
331 lout->setContentsMargins(QMargins());
332 m_ui.centralwidget->setLayout(lout);
335 m_phrasesDock =
new QDockWidget(
this);
336 m_phrasesDock->setObjectName(
"PhrasesDockwidget");
337 m_phrasesDock->setAllowedAreas(Qt::AllDockWidgetAreas);
338 m_phrasesDock->setWindowTitle(tr(
"Phrases and guesses"));
340 m_phraseView =
new PhraseView(m_dataModel, &m_phraseDict,
this);
341 m_phrasesDock->setWidget(m_phraseView);
344 m_sourceAndFormDock =
new QDockWidget(
this);
345 m_sourceAndFormDock->setObjectName(
"SourceAndFormDock");
346 m_sourceAndFormDock->setAllowedAreas(Qt::AllDockWidgetAreas);
347 m_sourceAndFormDock->setWindowTitle(tr(
"Sources and Forms"));
348 m_sourceAndFormView =
new QStackedWidget(
this);
349 m_sourceAndFormDock->setWidget(m_sourceAndFormView);
350 m_uiFormPreviewView =
new UiFormPreviewView(0, m_dataModel);
351 m_sourceCodeView =
new SourceCodeView(0);
355 m_sourceCodeContainer =
new QWidget;
356 QVBoxLayout *sourceCodeLayout =
new QVBoxLayout(m_sourceCodeContainer);
357 sourceCodeLayout->setContentsMargins(0, 0, 0, 0);
358 sourceCodeLayout->setSpacing(0);
359 m_previewBanner =
new QFrame;
360 m_previewBanner->setFrameShape(QFrame::StyledPanel);
361 QHBoxLayout *bannerLayout =
new QHBoxLayout(m_previewBanner);
362 m_previewBannerLabel =
new QLabel;
363 QPushButton *showPreviewButton =
new QPushButton(tr(
"Show preview"));
364 bannerLayout->addWidget(m_previewBannerLabel, 1);
365 bannerLayout->addWidget(showPreviewButton, 0);
366 m_previewBanner->hide();
367 connect(showPreviewButton, &QPushButton::clicked,
this, &
MainWindow::enablePreviewFromBanner);
368 sourceCodeLayout->addWidget(m_previewBanner);
369 sourceCodeLayout->addWidget(m_sourceCodeView, 1);
372 m_qmlFormPreviewView =
new QmlFormPreviewView(m_dataModel);
373 m_sourceAndFormView->addWidget(m_qmlFormPreviewView);
375 m_sourceAndFormView->addWidget(m_sourceCodeContainer);
376 m_sourceAndFormView->addWidget(m_uiFormPreviewView);
379 m_errorsDock =
new QDockWidget(
this);
380 m_errorsDock->setObjectName(
"ErrorsDockWidget");
381 m_errorsDock->setAllowedAreas(Qt::AllDockWidgetAreas);
382 m_errorsDock->setWindowTitle(tr(
"Warnings"));
383 m_errorsView =
new ErrorsView(m_dataModel,
this);
384 m_errorsDock->setWidget(m_errorsView);
387 setDockNestingEnabled(
true);
388 setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
389 setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
390 setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
391 setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
392 addDockWidget(Qt::LeftDockWidgetArea, m_contextAndLabelDock);
393 addDockWidget(Qt::TopDockWidgetArea, m_messagesDock);
394 addDockWidget(Qt::BottomDockWidgetArea, m_phrasesDock);
395 addDockWidget(Qt::TopDockWidgetArea, m_sourceAndFormDock);
396 m_sourceAndFormDock->hide();
397 addDockWidget(Qt::BottomDockWidgetArea, m_errorsDock);
402 m_messageEditor->installEventFilter(m_phraseView);
405 QShortcut *contextShortcut =
new QShortcut(QKeySequence(Qt::Key_F6),
this);
406 connect(contextShortcut, &QShortcut::activated,
408 QShortcut *messagesShortcut =
new QShortcut(QKeySequence(Qt::Key_F7),
this);
409 connect(messagesShortcut, &QShortcut::activated,
411 QShortcut *errorsShortcut =
new QShortcut(QKeySequence(Qt::Key_F8),
this);
412 connect(errorsShortcut, &QShortcut::activated,
414 QShortcut *sourceCodeShortcut =
new QShortcut(QKeySequence(Qt::Key_F9),
this);
415 connect(sourceCodeShortcut, &QShortcut::activated,
417 QShortcut *phrasesShortcut =
new QShortcut(QKeySequence(Qt::Key_F10),
this);
418 connect(phrasesShortcut, &QShortcut::activated,
421 connect(m_phraseView, &PhraseView::phraseSelected,
424 this, &
MainWindow::setCurrentMessageFromGuess);
425 connect(m_contextView->selectionModel(), &QItemSelectionModel::currentRowChanged,
426 this, &MainWindow::selectedContextChanged);
427 connect(m_labelView->selectionModel(), &QItemSelectionModel::currentRowChanged,
this,
428 &MainWindow::selectedLabelChanged);
429 connect(m_contextView->selectionModel(), &QItemSelectionModel::currentColumnChanged,
430 this, &MainWindow::updateLatestModel);
431 connect(m_labelView->selectionModel(), &QItemSelectionModel::currentColumnChanged,
this,
432 &MainWindow::updateLatestModel);
435 connect(m_contextAndLabelView, &QTabWidget::currentChanged,
this,
438 m_translateDialog =
new TranslateDialog(
this);
439 m_batchTranslateDialog =
new BatchTranslationDialog(m_dataModel,
this);
440 m_findDialog =
new FindDialog(
this);
445 m_progressLabel =
new QLabel();
446 statusBar()->addPermanentWidget(m_progressLabel);
447 m_modifiedLabel =
new QLabel(tr(
" MOD ",
"status bar: file(s) modified"));
448 statusBar()->addPermanentWidget(m_modifiedLabel);
449 m_contextAndLabelView->setCurrentWidget(m_contextView);
450 contextAndLabelTabChanged();
457 this, &QWidget::setWindowModified);
459 m_modifiedLabel, &QWidget::setVisible);
471 connect(m_messageView, &QAbstractItemView::clicked,
472 this, &MainWindow::toggleFinished);
473 connect(m_messageView, &QAbstractItemView::activated,
475 connect(m_contextView, &QAbstractItemView::activated,
476 m_messageView, qOverload<>(&QWidget::setFocus));
477 connect(m_labelView, &QAbstractItemView::activated, m_messageView,
478 qOverload<>(&QWidget::setFocus));
479 connect(m_messageEditor, &MessageEditor::translationChanged,
480 this, &MainWindow::updateTranslation);
481 connect(m_messageEditor, &
MessageEditor::translatorCommentChanged,
482 this, &MainWindow::updateTranslatorComment);
483 connect(m_findDialog, &FindDialog::findNext,
484 this, &MainWindow::findNext);
485 connect(m_translateDialog, &TranslateDialog::requestMatchUpdate,
487 connect(m_translateDialog, &TranslateDialog::activated,
490 QSize as(screen()->size());
492 resize(QSize(1000, 800).boundedTo(as));
497 connect(m_ui.actionLengthVariants, &QAction::toggled,
502 m_focusWatcher =
new FocusWatcher(m_messageEditor,
this);
503 m_contextView->installEventFilter(m_focusWatcher);
504 m_labelView->installEventFilter(m_focusWatcher);
505 m_messageView->installEventFilter(m_focusWatcher);
506 m_messageEditor->installEventFilter(m_focusWatcher);
507 m_sourceAndFormView->installEventFilter(m_focusWatcher);
508 m_phraseView->installEventFilter(m_focusWatcher);
509 m_errorsView->installEventFilter(m_focusWatcher);
515 qDeleteAll(m_phraseBooks);
518#if QT_CONFIG(printsupport)
525 m_contextView->header()->setSectionResizeMode(1, QHeaderView::Stretch);
526 m_contextView->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
527 m_labelView->header()->setSectionResizeMode(1, QHeaderView::Stretch);
528 m_labelView->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
536 for (
int i = 1; i < mc + 1; ++i) {
537 m_contextView->header()->setSectionResizeMode(i, QHeaderView::Fixed);
538 m_contextView->header()->resizeSection(i, 24);
540 m_labelView->header()->setSectionResizeMode(i, QHeaderView::Fixed);
541 m_labelView->header()->resizeSection(i, 24);
543 m_messageView->header()->setSectionResizeMode(i, QHeaderView::Fixed);
544 m_messageView->header()->resizeSection(i, 24);
546 for (
int i = mc + 1; i < m_messageView->header()->count(); i++)
547 m_messageView->header()->setSectionResizeMode(i, QHeaderView::Stretch);
550 selectedMessageChanged(QModelIndex(), QModelIndex());
551 doUpdateLatestModel(-1);
553 QTreeView *view = qobject_cast<QTreeView *>(m_contextAndLabelView->currentWidget());
554 if (!view->currentIndex().isValid()) {
556 view->setCurrentIndex(m_activeSortedGroupsModel->index(0, 0));
559 view->selectionModel()->select(view->currentIndex(),
560 QItemSelectionModel::SelectCurrent
561 | QItemSelectionModel::Rows);
562 m_messageView->selectionModel()->select(m_messageView->currentIndex(),
563 QItemSelectionModel::SelectCurrent|QItemSelectionModel::Rows);
568 doUpdateLatestModel(0);
569 else if (m_currentIndex
.model() >= mc)
570 doUpdateLatestModel(mc - 1);
573 m_contextView->setUpdatesEnabled(
true);
574 m_labelView->setUpdatesEnabled(
true);
575 m_messageView->setUpdatesEnabled(
true);
580 m_ui.actionFind->setEnabled(m_dataModel->contextCount() > 0 || m_dataModel->labelCount() > 0);
581 m_ui.actionFindNext->setEnabled(
false);
582 m_ui.actionFindPrev->setEnabled(
false);
588 updateVisibleColumns();
604 bool waitCursor =
false;
605 statusBar()->showMessage(tr(
"Loading..."));
606 qApp->processEvents();
609 bool closeOld =
false;
610 for (QString name : names) {
612 QApplication::setOverrideCursor(Qt::WaitCursor);
616 bool readWrite = m_globalReadWrite;
617 if (name.startsWith(u'=')) {
623 name = fi.canonicalFilePath();
624 if (m_dataModel->isFileLoaded(name) >= 0)
628 DataModel *dm =
new DataModel(m_dataModel);
629 if (!dm->load(name, &langGuessed,
this)) {
633 if (opened.isEmpty()) {
634 if (!m_dataModel->isWellMergeable(dm)) {
635 QApplication::restoreOverrideCursor();
637 switch (QMessageBox::information(
this, tr(
"Loading File - Qt Linguist"),
638 tr(
"The file '%1' does not seem to be related to the currently open file(s) '%2'.\n\n"
639 "Close the open file(s) first?")
640 .arg(DataModel::prettifyPlainFileName(name), m_dataModel->condensedSrcFileNames(
true)),
641 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
643 case QMessageBox::Cancel:
646 case QMessageBox::Yes:
654 if (!opened.first().dataModel->isWellMergeable(dm)) {
655 QApplication::restoreOverrideCursor();
657 switch (QMessageBox::information(
this, tr(
"Loading File - Qt Linguist"),
658 tr(
"The file '%1' does not seem to be related to the file '%2'"
659 " which is being loaded as well.\n\n"
660 "Skip loading the first named file?")
661 .arg(DataModel::prettifyPlainFileName(name), opened.first().dataModel->srcFileName(
true)),
662 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
664 case QMessageBox::Cancel:
666 for (
const OpenedFile &op : std::as_const(opened))
669 case QMessageBox::Yes:
677 opened.append(OpenedFile(dm, readWrite, langGuessed));
682 QApplication::restoreOverrideCursor();
686 for (
const OpenedFile &op : std::as_const(opened))
692 for (
const OpenedFile &op : std::as_const(opened)) {
693 if (op.langGuessed) {
695 QApplication::restoreOverrideCursor();
698 if (!m_translationSettingsDialog)
699 m_translationSettingsDialog =
new TranslationSettingsDialog(
this);
700 m_translationSettingsDialog->setDataModel(op.dataModel);
701 m_translationSettingsDialog->exec();
706 QApplication::setOverrideCursor(Qt::WaitCursor);
707 m_contextView->setUpdatesEnabled(
false);
708 m_labelView->setUpdatesEnabled(
false);
709 m_messageView->setUpdatesEnabled(
false);
711 for (
const OpenedFile &op : std::as_const(opened)) {
712 m_phraseDict.append(QHash<QString, QList<Phrase *> >());
713 m_dataModel->append(op.dataModel, op.readWrite);
715 updatePhraseDictInternal(m_phraseDict.size() - 1);
716 totalCount += op.dataModel->messageCount();
718 statusBar()->showMessage(tr(
"%n translation unit(s) loaded.", 0, totalCount),
MessageMS);
720 m_recentFiles.addFiles(m_dataModel->srcFileNames());
723 QApplication::restoreOverrideCursor();
729 m_globalReadWrite =
true;
730 pickTranslationFiles();
735 m_globalReadWrite =
false;
736 pickTranslationFiles();
742 if (model >= 0 && maybeSave(model)) {
743 m_phraseDict.removeAt(model);
744 m_contextView->setUpdatesEnabled(
false);
745 m_labelView->setUpdatesEnabled(
false);
746 m_messageView->setUpdatesEnabled(
false);
754 if (maybeSaveAll()) {
755 m_phraseDict.clear();
756 m_contextView->setUpdatesEnabled(
false);
757 m_labelView->setUpdatesEnabled(
false);
758 m_messageView->setUpdatesEnabled(
false);
762 m_recentFiles.closeGroup();
770 static const QString pattern(
"%1 (*.%2);;"_L1);
771 QStringList allExtensions;
773 for (
const Translator::FileFormat &format : std::as_const(Translator::registeredFileFormats())) {
774 if (format.fileType == Translator::FileFormat::TranslationSource && format.priority >= 0) {
775 filter.append(pattern.arg(format.description(), format.extension));
776 allExtensions.append(
"*."_L1 + format.extension);
779 QString allFilter = QObject::tr(
"Translation files (%1);;").arg(allExtensions.join(u' '));
781 filter.prepend(allFilter);
783 filter.append(allFilter);
784 filter.append(QObject::tr(
"All files (*)"));
791 if (!m_recentFiles.isEmpty())
792 dir = QFileInfo(m_recentFiles.lastOpenedFile()).path();
796 QFileInfo mainFile(m_dataModel->srcFileName(0));
797 QString mainFileBase = mainFile.baseName();
798 int pos = mainFileBase.indexOf(u'_');
800 varFilt = tr(
"Related files (%1);;")
801 .arg(mainFileBase.left(pos) +
"_*."_L1 + mainFile.completeSuffix());
805 openFiles(QFileDialog::getOpenFileNames(
this, tr(
"Open Translation Files"), dir,
806 varFilt + fileFilters(
true)));
808 const auto fileOpenCompleted = [
this](
const QString &fileName,
const QByteArray &fileContent) {
809 const QString copyFileName =
810 QDir::tempPath() + QLatin1String(
"/") + QFileInfo(fileName).fileName();
811 QFile tsFile(copyFileName);
812 if (tsFile.open(QIODevice::WriteOnly)) {
813 tsFile.write(fileContent);
815 openFiles({ copyFileName });
816 m_wasmFileMap[copyFileName] = std::move(fileName);
820 QFileDialog::getOpenFileContent(varFilt + fileFilters(
true), fileOpenCompleted);
826 QApplication::setOverrideCursor(Qt::WaitCursor);
827 if (m_dataModel->save(model,
this)) {
830 QString wasmFileName = m_dataModel->model(model)->srcFileName();
831 if (
const auto itr = m_wasmFileMap.find(wasmFileName); itr != m_wasmFileMap.end()) {
832 QFile tsFile(wasmFileName);
833 if (tsFile.open(QIODevice::ReadOnly)) {
835 QByteArray content = tsFile.readAll();
836 QFileDialog::saveFileContent(content, itr.value(),
this);
840 statusBar()->showMessage(tr(
"File saved."),
MessageMS);
842 QApplication::restoreOverrideCursor();
850 m_recentFiles.closeGroup();
856 QMessageBox::warning(
this, tr(
"Qt Linguist"), tr(
"Please select a file to be saved."));
860 saveInternal(m_currentIndex
.model());
866 QMessageBox::warning(
this, tr(
"Qt Linguist"),
867 tr(
"This function is not available on WebAssembly"));
874 QString newFilename = QFileDialog::getSaveFileName(
875 this, QString(), m_dataModel->srcFileName(m_currentIndex.model()), fileFilters(
false));
876 if (!newFilename.isEmpty()) {
877 if (m_dataModel->saveAs(m_currentIndex
.model(), newFilename,
this)) {
879 statusBar()->showMessage(tr(
"File saved."),
MessageMS);
880 m_recentFiles.addFiles(m_dataModel->srcFileNames());
890 QFileInfo oldFile(m_dataModel->srcFileName(m_currentIndex
.model()));
891 QString newFilename = oldFile.path() +
"/"_L1 + oldFile.completeBaseName() +
".qm"_L1;
893 newFilename = QFileDialog::getSaveFileName(
this, tr(
"Release"), newFilename,
894 tr(
"Qt message files for released applications (*.qm)\nAll files (*)"));
895 if (!newFilename.isEmpty()) {
897 statusBar()->showMessage(tr(
"File created."),
MessageMS);
903 QFileInfo oldFile(m_dataModel->srcFileName(model));
904 QString newFilename = oldFile.path() + u'/' + oldFile.completeBaseName() +
".qm"_L1;
906 if (!newFilename.isEmpty()) {
907 if (m_dataModel->release(model, newFilename,
false,
false,
SaveEverything,
this))
908 statusBar()->showMessage(tr(
"File created."),
MessageMS);
918 releaseInternal(m_currentIndex
.model());
928#if QT_CONFIG(printsupport)
929QPrinter *MainWindow::printer()
932 m_printer =
new QPrinter;
936void MainWindow::print()
939 QAutoPointer<QPrintDialog> dlg{
new QPrintDialog(printer(),
this)};
941 QApplication::setOverrideCursor(Qt::WaitCursor);
942 printer()->setDocName(m_dataModel->condensedSrcFileNames(
true));
943 statusBar()->showMessage(tr(
"Printing..."));
944 PrintOut pout(printer());
946 auto printGroupItem = [&pout, &pageNum,
this](
int index, TranslationType type) {
947 MultiGroupItem *mg = m_dataModel->multiGroupItem(index, type);
949 pout.setRule(PrintOut::ThickRule);
950 pout.setGuide(mg->group());
952 pout.addBox(100, tr(
"Label: %1").arg(mg->group()), PrintOut::Strong);
954 pout.addBox(100, tr(
"Context: %1").arg(mg->group()), PrintOut::Strong);
957 pout.addBox(92, mg->comment(), PrintOut::Emphasis);
959 pout.setRule(PrintOut::ThickRule);
961 for (
int j = 0; j < mg->messageCount(); ++j) {
962 pout.setRule(PrintOut::ThinRule);
963 bool printedSrc =
false;
965 for (
int k = 0; k < m_dataModel->modelCount(); ++k) {
966 if (
const MessageItem *m = mg->messageItem(k, j)) {
968 pout.addBox(40, m->text());
970 comment = m->comment();
975 if (m->message().isPlural() && m_dataModel->language(k) != QLocale::C) {
976 QStringList transls = m->translations();
977 pout.addBox(40, transls.join(u'\n'));
979 pout.addBox(40, m->translation());
983 switch (m->message().type()) {
984 case TranslatorMessage::Finished:
985 type = tr(
"finished");
987 case TranslatorMessage::Unfinished:
988 type = m->danger() ? tr(
"unresolved") :
"unfinished"_L1;
990 case TranslatorMessage::Obsolete:
991 case TranslatorMessage::Vanished:
992 type = tr(
"obsolete");
995 pout.addBox(12, type, PrintOut::Normal, Qt::AlignRight);
999 if (!comment.isEmpty()) {
1001 pout.addBox(92, comment, PrintOut::Emphasis);
1002 pout.flushLine(
true);
1005 if (pout.pageNum() != pageNum) {
1006 pageNum = pout.pageNum();
1007 statusBar()->showMessage(tr(
"Printing... (page %1)").arg(pageNum));
1012 for (
int i = 0; i < m_dataModel->labelCount(); ++i)
1013 printGroupItem(i, IDBASED);
1014 for (
int i = 0; i < m_dataModel->contextCount(); ++i)
1015 printGroupItem(i, TEXTBASED);
1016 pout.flushLine(
true);
1017 QApplication::restoreOverrideCursor();
1018 statusBar()->showMessage(tr(
"Printing completed"), MessageMS);
1020 statusBar()->showMessage(tr(
"Printing aborted"), MessageMS);
1028 if ((m_findWhere & where) == 0)
1031 QString text = searchWhat;
1033 if (m_findOptions.testFlag(FindDialog::IgnoreAccelerators))
1037 if (m_findOptions.testFlag(FindDialog::UseRegExp))
1038 return m_findDialog->getRegExp().match(text).hasMatch();
1040 return text.indexOf(m_findText, 0, m_findOptions.testFlag(FindDialog::MatchCase)
1041 ? Qt::CaseSensitive : Qt::CaseInsensitive) >= 0;
1049 const QModelIndex &startIndex = m_messageView->currentIndex();
1050 QModelIndex index = (direction == FindNext
1051 ? nextMessage(startIndex)
1052 : prevMessage(startIndex));
1054 while (index.isValid()) {
1055 QModelIndex realIndex = m_activeSortedMessagesModel->mapToSource(index);
1056 MultiDataIndex dataIndex = m_activeMessageModel->dataIndex(realIndex, -1);
1057 bool hadMessage =
false;
1060 if (m_findStatusFilter != -1 && m_findStatusFilter != m
->type())
1063 if (m_findOptions.testFlag(FindDialog::SkipObsolete)
1070 if (searchItem(DataModel::SourceText, m->text()))
1072 if (searchItem(DataModel::SourceText, m->pluralText()))
1074 if (searchItem(DataModel::Comments, m->comment()))
1076 if (searchItem(DataModel::Comments, m->extraComment()))
1079 const auto translations = m->translations();
1080 for (
const QString &trans : translations)
1081 if (searchItem(DataModel::Translations, trans))
1083 if (searchItem(DataModel::Comments, m->translatorComment()))
1090 setCurrentMessage(realIndex, i);
1093 const QModelIndex &c1 =
1094 m_activeSortedGroupsModel
1096 m_activeSortedMessagesModel->mapToSource(startIndex))
1098 const QModelIndex &c2 =
1099 m_activeSortedGroupsModel->mapFromSource(realIndex).parent();
1100 const QModelIndex &m = m_activeSortedMessagesModel->mapFromSource(realIndex);
1102 if (c2.row() < c1.row() || (c1.row() == c2.row() && m.row() <= startIndex.row()))
1103 statusBar()->showMessage(tr(
"Search wrapped."),
MessageMS);
1105 m_findDialog->hide();
1113 if (index == startIndex)
1116 index = (direction == FindNext
1117 ? nextMessage(index)
1118 : prevMessage(index));
1122 QMessageBox::warning(m_findDialog, tr(
"Qt Linguist"),
1123 tr(
"Cannot find the string '%1'.").arg(m_findText));
1128 m_activeMessageModel->blockSignals(
true);
1129 m_batchTranslateDialog->setPhraseBooks(m_phraseBooks, m_currentIndex
.model());
1130 if (m_batchTranslateDialog->exec() != QDialog::Accepted)
1131 m_activeMessageModel->blockSignals(
false);
1137 m_latestCaseSensitivity = -1;
1138 QModelIndex idx = m_messageView->currentIndex();
1140 m_activeSortedMessagesModel->index(idx.row(), m_currentIndex
.model() + 1, idx.parent());
1141 m_messageView->setCurrentIndex(idx2);
1142 QString fn = QFileInfo(m_dataModel->srcFileName(m_currentIndex.model())).baseName();
1143 m_translateDialog->setWindowTitle(tr(
"Search And Translate in '%1' - Qt Linguist").arg(fn));
1144 m_translateDialog->exec();
1147void MainWindow::updateTranslateHit(
bool &hit)
1152 && m->compare(m_translateDialog->findText(),
false, m_translateDialog->caseSensitivity());
1157 QString findText = m_translateDialog->findText();
1158 QString replaceText = m_translateDialog->replaceText();
1159 bool markFinished = m_translateDialog->markFinished();
1160 Qt::CaseSensitivity caseSensitivity = m_translateDialog->caseSensitivity();
1162 int translatedCount = 0;
1164 if (mode == TranslateDialog::TranslateAll) {
1165 auto setTranslations = [
this, &translatedCount, &findText, &caseSensitivity, &replaceText,
1170 if (m && !m
->isObsolete() && m->compare(findText,
false, caseSensitivity)) {
1171 if (!translatedCount)
1172 m_activeMessageModel->blockSignals(
true);
1173 m_dataModel->setTranslation(it, replaceText);
1182 if (translatedCount) {
1184 QMessageBox::warning(m_translateDialog, tr(
"Translate - Qt Linguist"),
1185 tr(
"Translated %n entry(s)", 0, translatedCount));
1188 if (mode == TranslateDialog::Translate) {
1189 m_dataModel->setTranslation(m_currentIndex, replaceText);
1193 const QModelIndex firstIndex = firstMessage();
1194 if (findText != m_latestFindText || caseSensitivity != m_latestCaseSensitivity) {
1195 m_latestFindText = findText;
1196 m_latestCaseSensitivity = caseSensitivity;
1197 m_searchIndex = firstIndex;
1202 QModelIndex realIndex = m_activeSortedMessagesModel->mapToSource(m_searchIndex);
1203 MultiDataIndex dataIndex = m_activeMessageModel->dataIndex(realIndex, m_currentIndex.model());
1204 m_searchIndex = nextMessage(m_searchIndex);
1205 if (MessageItem *m = m_dataModel->messageItem(dataIndex)) {
1206 if (!m->isObsolete() && m->compare(findText,
false, caseSensitivity)) {
1207 setCurrentMessage(realIndex, m_currentIndex.model());
1213 if (m_searchIndex == firstIndex) {
1214 if (QMessageBox::question(
1215 m_translateDialog, tr(
"Translate - Qt Linguist"),
1216 tr(
"No more occurrences of '%1'. Start over?").arg(findText),
1217 QMessageBox::Yes | QMessageBox::No)
1218 != QMessageBox::Yes) {
1219 m_searchIndex = prevMessage(m_searchIndex);
1226 if (!translatedCount) {
1228 QMessageBox::warning(m_translateDialog, tr(
"Translate - Qt Linguist"),
1229 tr(
"Cannot find the string '%1'.").arg(findText));
1235 QString name = QFileDialog::getSaveFileName(
this, tr(
"Create New Phrase Book"),
1236 m_phraseBookDir, tr(
"Qt phrase books (*.qph)\nAll files (*)"));
1237 if (!name.isEmpty()) {
1239 if (!m_translationSettingsDialog)
1240 m_translationSettingsDialog =
new TranslationSettingsDialog(
this);
1242 if (!m_translationSettingsDialog->exec())
1244 m_phraseBookDir = QFileInfo(name).absolutePath();
1245 if (savePhraseBook(&name, pb)) {
1246 if (doOpenPhraseBook(name))
1247 statusBar()->showMessage(tr(
"Phrase book created."),
MessageMS);
1252bool MainWindow::isPhraseBookOpen(
const QString &name)
1254 for (
const PhraseBook *pb : std::as_const(m_phraseBooks)) {
1255 if (pb->fileName() == name)
1264 QString name = QFileDialog::getOpenFileName(
this, tr(
"Open Phrase Book"),
1265 m_phraseBookDir, tr(
"Qt phrase books (*.qph);;All files (*)"));
1267 if (!name.isEmpty()) {
1268 m_phraseBookDir = QFileInfo(name).absolutePath();
1269 if (!isPhraseBookOpen(name)) {
1270 if (
PhraseBook *phraseBook = doOpenPhraseBook(name)) {
1271 int n = phraseBook->phrases().size();
1272 statusBar()->showMessage(tr(
"%n phrase(s) loaded.", 0, n),
MessageMS);
1278void MainWindow::closePhraseBook(QAction *action)
1280 PhraseBook *pb = m_phraseBookMenu[PhraseCloseMenu].value(action);
1281 if (!maybeSavePhraseBook(pb))
1284 m_phraseBookMenu[PhraseCloseMenu].remove(action);
1285 m_ui.menuClosePhraseBook->removeAction(action);
1287 QAction *act = m_phraseBookMenu[PhraseEditMenu].key(pb);
1288 m_phraseBookMenu[PhraseEditMenu].remove(act);
1289 m_ui.menuEditPhraseBook->removeAction(act);
1291 act = m_phraseBookMenu[PhrasePrintMenu].key(pb);
1292 m_ui.menuPrintPhraseBook->removeAction(act);
1294 m_phraseBooks.removeOne(pb);
1297 updatePhraseDicts();
1299 updatePhraseBookActions();
1302void MainWindow::editPhraseBook(QAction *action)
1304 PhraseBook *pb = m_phraseBookMenu[PhraseEditMenu].value(action);
1305 QAutoPointer<PhraseBookBox> box{
new PhraseBookBox(pb,
this)};
1310 updatePhraseDicts();
1313#if QT_CONFIG(printsupport)
1315void MainWindow::printPhraseBook(QAction *action)
1317 PhraseBook *phraseBook = m_phraseBookMenu[PhrasePrintMenu].value(action);
1321 QAutoPointer<QPrintDialog> dlg{
new QPrintDialog(printer(),
this)};
1323 printer()->setDocName(phraseBook->fileName());
1324 statusBar()->showMessage(tr(
"Printing..."));
1325 PrintOut pout(printer());
1326 pout.setRule(PrintOut::ThinRule);
1327 const auto phrases = phraseBook->phrases();
1328 for (
const Phrase *p : phrases) {
1329 pout.setGuide(p->source());
1330 pout.addBox(29, p->source());
1332 pout.addBox(29, p->target());
1334 pout.addBox(34, p->definition(), PrintOut::Emphasis);
1336 if (pout.pageNum() != pageNum) {
1337 pageNum = pout.pageNum();
1338 statusBar()->showMessage(tr(
"Printing... (page %1)")
1341 pout.setRule(PrintOut::NoRule);
1342 pout.flushLine(
true);
1344 pout.flushLine(
true);
1345 statusBar()->showMessage(tr(
"Printing completed"), MessageMS);
1347 statusBar()->showMessage(tr(
"Printing aborted"), MessageMS);
1355 QStringList phraseBookList;
1356 QHash<QString, PhraseBook *> phraseBookHash;
1357 for (PhraseBook *pb : std::as_const(m_phraseBooks)) {
1358 if (pb->language() != QLocale::C && m_dataModel->language(m_currentIndex.model()) != QLocale::C) {
1359 if (pb->language() != m_dataModel->language(m_currentIndex.model()))
1361 if (pb->territory() == m_dataModel->model(m_currentIndex.model())->territory())
1362 phraseBookList.prepend(pb->friendlyPhraseBookName());
1364 phraseBookList.append(pb->friendlyPhraseBookName());
1366 phraseBookList.append(pb->friendlyPhraseBookName());
1368 phraseBookHash.insert(pb->friendlyPhraseBookName(), pb);
1370 if (phraseBookList.isEmpty()) {
1371 QMessageBox::warning(
this, tr(
"Add to phrase book"),
1372 tr(
"No appropriate phrasebook found."));
1376 QString selectedPhraseBook;
1377 if (phraseBookList.size() == 1) {
1378 selectedPhraseBook = phraseBookList.at(0);
1379 if (QMessageBox::information(
this, tr(
"Add to phrase book"),
1380 tr(
"Adding entry to phrasebook %1").arg(selectedPhraseBook),
1381 QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok)
1385 bool okPressed =
false;
1386 selectedPhraseBook = QInputDialog::getItem(
this, tr(
"Add to phrase book"),
1387 tr(
"Select phrase book to add to"),
1388 phraseBookList, 0,
false, &okPressed);
1394 Phrase *phrase =
new Phrase(currentMessage->text(), currentMessage->translation(),
1395 QString(),
nullptr);
1397 phraseBookHash.value(selectedPhraseBook)->append(phrase);
1402 m_contextView->sortByColumn(-1, Qt::AscendingOrder);
1403 m_labelView->sortByColumn(-1, Qt::AscendingOrder);
1404 m_messageView->sortByColumn(-1, Qt::AscendingOrder);
1409 QString errorMessage;
1410 const QString page = m_helpClient->documentUrl(u"linguist"_s) +
"/qtlinguist-index.html"_L1;
1411 if (!m_helpClient->showPage(page, &errorMessage))
1412 qWarning(
"%s", qPrintable(errorMessage));
1417 return tr(
"Qt Linguist is a tool for adding translations to Qt applications.");
1422 QAutoPointer<QMessageBox> box{
new QMessageBox(
this)};
1423 box->setTextFormat(Qt::RichText);
1424 QString version = tr(
"Version %1");
1425 version = version.arg(QLatin1String(QT_VERSION_STR));
1427 box->setText(
"<center><img src=\":/images/icons/linguist-128-32.png\"/></img><p>%1</p></center>"
1429 "<p>Copyright (C) The Qt Company Ltd.</p>"_L1.arg(version, description()));
1431 box->setWindowTitle(QApplication::translate(
"AboutDialog",
"Qt Linguist"));
1432 box->setIcon(QMessageBox::NoIcon);
1438 QMessageBox::aboutQt(
this, tr(
"Qt Linguist"));
1443 bool enabled = !m_phraseBooks.isEmpty();
1444 m_ui.menuClosePhraseBook->setEnabled(enabled);
1445 m_ui.menuEditPhraseBook->setEnabled(enabled);
1446#if QT_CONFIG(printsupport)
1447 m_ui.menuPrintPhraseBook->setEnabled(enabled);
1453 if (maybeSaveAll() && maybeSavePhraseBooks())
1464 switch (QMessageBox::information(
this, tr(
"Qt Linguist"),
1465 tr(
"Do you want to save the modified files?"),
1466 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
1468 case QMessageBox::Cancel:
1470 case QMessageBox::Yes:
1484 switch (QMessageBox::information(
this, tr(
"Qt Linguist"),
1485 tr(
"Do you want to save '%1'?").arg(m_dataModel->srcFileName(model,
true)),
1486 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
1488 case QMessageBox::Cancel:
1490 case QMessageBox::Yes:
1491 saveInternal(model);
1502 bool enable =
false;
1503 bool enableRw =
false;
1511 m_ui.actionSaveAll->setEnabled(enableRw);
1512 m_ui.actionReleaseAll->setEnabled(enableRw);
1513 m_ui.actionCloseAll->setEnabled(enable);
1514#if QT_CONFIG(printsupport)
1515 m_ui.actionPrint->setEnabled(enable);
1517 m_ui.actionAccelerators->setEnabled(enable);
1518 m_ui.actionSurroundingWhitespace->setEnabled(enable);
1519 m_ui.actionEndingPunctuation->setEnabled(enable);
1520 m_ui.actionPhraseMatches->setEnabled(enable);
1521 m_ui.actionPlaceMarkerMatches->setEnabled(enable);
1522 m_ui.actionResetSorting->setEnabled(enable);
1526 m_fileActiveModel = m_editActiveModel = -2;
1529 cap = tr(
"Qt Linguist[*]");
1531 cap = tr(
"%1[*] - Qt Linguist").arg(m_dataModel->condensedSrcFileNames(
true));
1532 setWindowTitle(cap);
1535void MainWindow::selectedContextChanged(
const QModelIndex &sortedIndex,
const QModelIndex &oldIndex)
1537 if (sortedIndex.isValid()) {
1538 if (m_settingCurrentMessage)
1541 if (!m_activeTranslationType || *m_activeTranslationType == IDBASED)
1542 contextAndLabelTabChanged();
1543 QModelIndex sourceIndex = m_sortedContextsModel->mapToSource(sortedIndex);
1544 if (m_activeMessageModel->parent(currentMessageIndex()).row() == sourceIndex.row())
1546 QModelIndex contextIndex = setMessageViewRoot(sourceIndex);
1547 const QModelIndex &firstChild =
1548 m_activeSortedMessagesModel->index(0, sourceIndex.column(), contextIndex);
1549 m_messageView->setCurrentIndex(firstChild);
1550 }
else if (oldIndex.isValid()) {
1551 m_contextView->setCurrentIndex(oldIndex);
1555void MainWindow::selectedLabelChanged(
const QModelIndex &sortedIndex,
const QModelIndex &oldIndex)
1557 if (sortedIndex.isValid()) {
1558 if (m_settingCurrentMessage)
1561 if (!m_activeTranslationType || *m_activeTranslationType != IDBASED)
1562 contextAndLabelTabChanged();
1563 QModelIndex sourceIndex = m_sortedLabelsModel->mapToSource(sortedIndex);
1564 if (m_activeMessageModel->parent(currentMessageIndex()).row() == sourceIndex.row())
1566 QModelIndex labelIndex = setMessageViewRoot(sourceIndex);
1567 const QModelIndex &firstChild =
1568 m_activeSortedMessagesModel->index(0, sourceIndex.column(), labelIndex);
1569 m_messageView->setCurrentIndex(firstChild);
1570 }
else if (oldIndex.isValid()) {
1571 m_labelView->setCurrentIndex(oldIndex);
1576
1577
1578void MainWindow::selectedMessageChanged(
const QModelIndex &sortedIndex,
const QModelIndex &oldIndex)
1581 if (!sortedIndex.isValid() && oldIndex.isValid()) {
1582 m_messageView->setCurrentIndex(oldIndex);
1588 QModelIndex index = m_activeSortedMessagesModel->mapToSource(sortedIndex);
1589 if (index.isValid()) {
1590 model = (index.column() && (index.column() - 1 < m_dataModel
->modelCount()))
1591 ? index.column() - 1
1593 m_currentIndex = m_activeMessageModel->dataIndex(index, model);
1597 m_phraseView->setSourceText(m_currentIndex
.model(), m->text());
1599 m_phraseView->setSourceText(-1, QString());
1607 m_phraseView->setSourceText(-1, QString());
1609 m_errorsView->setEnabled(m != 0);
1610 updateDanger(m_currentIndex,
true);
1614 m_phraseView->setSourceText(-1, QString());
1616 updateSourceView(model, m);
1618 updatePhraseBookActions();
1619 m_ui.actionSelectAll->setEnabled(index.isValid());
1625 updateDanger(index,
true);
1628 if (hasUiFormPreview(index.model(), m->fileName()))
1631 else if (hasQmlFormPreview(index.model(), m->fileName()))
1632 if (!m_qmlFormPreviewView->setSourceContext(index.model(), m))
1633 m_ui.actionQmlPreview->setChecked(
false);
1639void MainWindow::updateTranslation(
const QStringList &translations)
1644 if (translations == m->translations())
1647 m->setTranslations(translations);
1648 if (hasUiFormPreview(m_currentIndex.model(), m->fileName()))
1651 else if (hasQmlFormPreview(m_currentIndex.model(), m->fileName()))
1652 if (!m_qmlFormPreviewView->setSourceContext(m_currentIndex.model(), m))
1653 m_ui.actionQmlPreview->setChecked(
false);
1655 updateDanger(m_currentIndex,
true);
1663void MainWindow::updateTranslatorComment(
const QString &comment)
1668 if (comment == m->translatorComment())
1671 m->setTranslatorComment(comment);
1678 m_activeMessageModel->blockSignals(
false);
1679 m_contextView->update();
1680 m_labelView->update();
1681 m_messageView->update();
1701void MainWindow::toggleFinished(
const QModelIndex &index)
1703 if (!index.isValid() || index.column() - 1 >= m_dataModel
->modelCount()
1707 QModelIndex item = m_activeSortedMessagesModel->mapToSource(index);
1708 MultiDataIndex dataIndex = m_activeMessageModel->dataIndex(item);
1720 if (!m_machineTranslationDialog)
1721 m_machineTranslationDialog =
new MachineTranslationDialog(
this);
1723 m_machineTranslationDialog->open();
1727
1728
1729
1730
1731QModelIndex
MainWindow::nextGroup(
const QModelIndex &index)
const
1733 QModelIndex sortedGroupIndex = m_activeSortedGroupsModel->mapFromSource(
1734 m_activeSortedMessagesModel->mapToSource(index));
1736 int nextRow = sortedGroupIndex.row() + 1;
1737 if (nextRow >= m_activeSortedGroupsModel->rowCount()) {
1738 const QSortFilterProxyModel *inactiveModel =
1739 m_activeSortedGroupsModel == m_sortedLabelsModel ? m_sortedContextsModel
1740 : m_sortedLabelsModel;
1741 if (inactiveModel->rowCount())
1742 m_contextAndLabelView->setCurrentIndex(1 - m_contextAndLabelView->currentIndex());
1745 sortedGroupIndex = m_activeSortedGroupsModel->index(nextRow, index.column());
1747 return m_activeSortedMessagesModel->mapFromSource(
1748 m_activeSortedGroupsModel->mapToSource(sortedGroupIndex));
1752
1753
1754QModelIndex
MainWindow::prevGroup(
const QModelIndex &index)
const
1756 QModelIndex sortedGroupIndex = m_activeSortedGroupsModel->mapFromSource(
1757 m_activeSortedMessagesModel->mapToSource(index));
1759 int prevRow = sortedGroupIndex.row() - 1;
1761 const QSortFilterProxyModel *inactiveModel =
1762 m_activeSortedGroupsModel == m_sortedLabelsModel ? m_sortedContextsModel
1763 : m_sortedLabelsModel;
1764 if (inactiveModel->rowCount())
1765 m_contextAndLabelView->setCurrentIndex(1 - m_contextAndLabelView->currentIndex());
1766 prevRow = m_activeSortedGroupsModel->rowCount() - 1;
1768 sortedGroupIndex = m_activeSortedGroupsModel->index(prevRow, index.column());
1770 return m_activeSortedMessagesModel->mapFromSource(
1771 m_activeSortedGroupsModel->mapToSource(sortedGroupIndex));
1776 QModelIndex id = m_activeSortedMessagesModel->index(0, 0);
1777 QModelIndex firstId;
1778 if (id.isValid() && m_activeSortedMessagesModel->hasChildren(id))
1779 firstId = m_activeSortedMessagesModel->index(0, 0, id);
1780 else if (id.isValid())
1785QModelIndex
MainWindow::nextMessage(
const QModelIndex ¤tIndex,
bool checkUnfinished)
const
1788 currentIndex.isValid() ? currentIndex : m_activeSortedMessagesModel->index(0, 0);
1791 QModelIndex par = idx.parent();
1792 if (par.isValid()) {
1793 row = idx.row() + 1;
1798 if (row >= m_activeSortedMessagesModel->rowCount(par)) {
1799 par = nextGroup(par);
1802 idx = m_activeSortedMessagesModel->index(row, idx.column(), par);
1804 if (!checkUnfinished)
1807 QModelIndex item = m_activeSortedMessagesModel->mapToSource(idx);
1808 MultiDataIndex index = m_activeMessageModel->dataIndex(item, -1);
1811 }
while (idx != currentIndex);
1812 return QModelIndex();
1815QModelIndex
MainWindow::prevMessage(
const QModelIndex ¤tIndex,
bool checkUnfinished)
const
1818 currentIndex.isValid() ? currentIndex : m_activeSortedMessagesModel->index(0, 0);
1820 int row = idx.row() - 1;
1821 QModelIndex par = idx.parent();
1822 if (!par.isValid()) {
1828 par = prevGroup(par);
1829 row = m_activeSortedMessagesModel->rowCount(par) - 1;
1831 idx = m_activeSortedMessagesModel->index(row, idx.column(), par);
1833 if (!checkUnfinished)
1836 QModelIndex item = m_activeSortedMessagesModel->mapToSource(idx);
1837 MultiDataIndex index = m_activeMessageModel->dataIndex(item, -1);
1840 }
while (idx != currentIndex);
1841 return QModelIndex();
1846 if (m_ui.actionNextUnfinished->isEnabled()) {
1847 if (!doNext(
true)) {
1850 statusBar()->showMessage(tr(
"No untranslated translation units left."),
MessageMS);
1858 if (m_ui.actionNextUnfinished->isEnabled()) {
1859 if (!doPrev(
true)) {
1862 statusBar()->showMessage(tr(
"No untranslated translation units left."),
MessageMS);
1880 QModelIndex index = prevMessage(m_messageView->currentIndex(), checkUnfinished);
1881 if (index.isValid())
1882 setCurrentMessage(m_activeSortedMessagesModel->mapToSource(index));
1883 if (checkUnfinished)
1887 return index.isValid();
1892 QModelIndex index = nextMessage(m_messageView->currentIndex(), checkUnfinished);
1893 if (index.isValid())
1894 setCurrentMessage(m_activeSortedMessagesModel->mapToSource(index));
1895 if (checkUnfinished)
1899 return index.isValid();
1903 FindDialog::FindOptions options,
int statusFilter)
1908 m_findWhere = where;
1909 m_findOptions = options;
1910 m_findStatusFilter = statusFilter;
1911 if (options.testFlag(FindDialog::UseRegExp)) {
1912 m_findDialog->getRegExp().setPatternOptions(options.testFlag(FindDialog::MatchCase)
1913 ? QRegularExpression::NoPatternOption
1914 : QRegularExpression::CaseInsensitiveOption);
1916 m_ui.actionFindNext->setEnabled(
true);
1917 m_ui.actionFindPrev->setEnabled(
true);
1924 updateDanger(it,
false);
1926 updateDanger(it,
false);
1929 updateDanger(m_currentIndex,
true);
1934 const QString prefix = isDarkMode() ?
":/images/darkicons/"_L1:
":/images/lighticons/"_L1;
1935 auto getIcon = [&prefix](
const QString &name) {
1937 icon.addPixmap(QPixmap(prefix + name + QStringLiteral(
".png")), QIcon::Normal);
1938 icon.addPixmap(QPixmap(prefix + name + QStringLiteral(
"-disabled.png")), QIcon::Disabled);
1942 QIcon openIcon = getIcon(
"open-new"_L1);
1943 m_ui.actionOpen->setIcon(openIcon);
1944 m_ui.actionOpenAux->setIcon(openIcon);
1945 QIcon saveIcon = getIcon(
"save-fl-disk"_L1);
1946 m_ui.actionSave->setIcon(saveIcon);
1947 m_ui.actionSaveAll->setIcon(saveIcon);
1948 m_ui.actionPrint->setIcon(getIcon(
"print"_L1));
1949 m_ui.actionRedo->setIcon(getIcon(
"redo-arrow-right"_L1));
1950 m_ui.actionUndo->setIcon(getIcon(
"undo-arrow-left"_L1));
1951 m_ui.actionCut->setIcon(getIcon(
"cut"_L1));
1952 m_ui.actionCopy->setIcon(getIcon(
"copy-general"_L1));
1953 m_ui.actionPaste->setIcon(getIcon(
"paste-general"_L1));
1954 m_ui.actionFind->setIcon(getIcon(
"search-magnifier"_L1));
1956 m_ui.actionAccelerators->setIcon(getIcon(
"/check-ampersands"_L1));
1957 m_ui.actionOpenPhraseBook->setIcon(getIcon(
"library"_L1));
1958 m_ui.actionDone->setIcon(getIcon(
"mark-current-translation-done"_L1));
1959 m_ui.actionDoneAndNext->setIcon(getIcon(
"mark-current-translation-done-move-to-next"_L1));
1960 m_ui.actionNext->setIcon(getIcon(
"next-translation-item"_L1));
1961 m_ui.actionNextUnfinished->setIcon(getIcon(
"next-unfinished-translation-item"_L1));
1962 m_ui.actionPhraseMatches->setIcon(getIcon(
"check-phrase-suggestions"_L1));
1963 m_ui.actionSurroundingWhitespace->setIcon(getIcon(
"check-white-spaces"_L1));
1964 m_ui.actionEndingPunctuation->setIcon(getIcon(
"check-ending-pontuation"_L1));
1965 m_ui.actionPrev->setIcon(getIcon(
"previous-translation-item"_L1));
1966 m_ui.actionPrevUnfinished->setIcon(getIcon(
"previous-unfinished-translation-item"_L1));
1967 m_ui.actionPlaceMarkerMatches->setIcon(getIcon(
"check-place-markers"_L1));
1968 m_ui.actionWhatsThis->setIcon(getIcon(
"hit-help-chosen-option"_L1));
1973 m_ui.menuRecentlyOpenedFiles->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::DocumentOpenRecent));
1974 m_ui.actionCloseAll->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::WindowClose));
1975 m_ui.actionExit->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ApplicationExit));
1976 m_ui.actionSelectAll->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::EditSelectAll));
1980 connect(m_ui.menuFile, &QMenu::aboutToShow,
this, &
MainWindow::fileAboutToShow);
1981 connect(m_ui.actionOpen, &QAction::triggered,
this, &
MainWindow::open);
1982 connect(m_ui.actionOpenAux, &QAction::triggered,
this, &
MainWindow::openAux);
1983 connect(m_ui.actionSave, &QAction::triggered,
this, &
MainWindow::save);
1985 connect(m_ui.actionSaveAll, &QAction::triggered,
this, &
MainWindow::saveAll);
1986 connect(m_ui.actionSaveAs, &QAction::triggered,
this, &
MainWindow::saveAs);
1988 m_ui.actionSaveAs->setVisible(
false);
1989 m_ui.actionSaveAll->setVisible(
false);
1991 connect(m_ui.actionReleaseAll, &QAction::triggered,
this, &
MainWindow::releaseAll);
1992 connect(m_ui.actionRelease, &QAction::triggered,
this, &
MainWindow::release);
1993 connect(m_ui.actionReleaseAs, &QAction::triggered,
this, &
MainWindow::releaseAs);
1994#if QT_CONFIG(printsupport)
1995 connect(m_ui.actionPrint, &QAction::triggered,
this, &MainWindow::print);
1997 m_ui.actionPrint->setEnabled(
false);
1999 connect(m_ui.actionClose, &QAction::triggered,
this, &
MainWindow::closeFile);
2000 connect(m_ui.actionCloseAll, &QAction::triggered,
this, &
MainWindow::closeAll);
2001 connect(m_ui.actionExit, &QAction::triggered,
this, &MainWindow::close);
2004 connect(m_ui.menuEdit, &QMenu::aboutToShow,
this, &
MainWindow::editAboutToShow);
2006 connect(m_ui.actionUndo, &QAction::triggered, m_messageEditor, &MessageEditor::undo);
2012#ifndef QT_NO_CLIPBOARD
2023 connect(m_ui.actionSelectAll, &QAction::triggered,
2025 connect(m_ui.actionFind, &QAction::triggered,
2026 m_findDialog, &FindDialog::find);
2027 connect(m_ui.actionFindNext, &QAction::triggered,
2028 this, [
this] {findAgain(
FindNext);});
2029 connect(m_ui.actionFindPrev, &QAction::triggered,
2030 this, [
this] {findAgain(
FindPrev);});
2031 connect(m_ui.actionSearchAndTranslate, &QAction::triggered,
2033 connect(m_ui.actionBatchTranslation, &QAction::triggered,
2034 this, &
MainWindow::showBatchTranslateDialog);
2035 connect(m_ui.actionTranslationFileSettings, &QAction::triggered,
2038 connect(m_batchTranslateDialog, &BatchTranslationDialog::finished,
2043 connect(m_ui.actionAuto_Translation, &QAction::triggered,
this,
2045 connect(m_ui.actionPrevUnfinished, &QAction::triggered,
this, &
MainWindow::prevUnfinished);
2046 connect(m_ui.actionNextUnfinished, &QAction::triggered,
this, &
MainWindow::nextUnfinished);
2047 connect(m_ui.actionNext, &QAction::triggered,
this, &
MainWindow::next);
2048 connect(m_ui.actionPrev, &QAction::triggered,
this, &
MainWindow::prev);
2049 connect(m_ui.actionDone, &QAction::triggered,
this, &MainWindow::done);
2050 connect(m_ui.actionDoneAndNext, &QAction::triggered,
this, &
MainWindow::doneAndNext);
2051 connect(m_ui.actionBeginFromSource, &QAction::triggered, m_messageEditor,
2055 connect(m_ui.actionNewPhraseBook, &QAction::triggered,
this, &
MainWindow::newPhraseBook);
2056 connect(m_ui.actionOpenPhraseBook, &QAction::triggered,
this, &
MainWindow::openPhraseBook);
2057 connect(m_ui.menuClosePhraseBook, &QMenu::triggered,
2059 connect(m_ui.menuEditPhraseBook, &QMenu::triggered,
2061#if QT_CONFIG(printsupport)
2062 connect(m_ui.menuPrintPhraseBook, &QMenu::triggered,
2063 this, &MainWindow::printPhraseBook);
2065 m_ui.menuPrintPhraseBook->setEnabled(
false);
2067 connect(m_ui.actionAddToPhraseBook, &QAction::triggered,
2071 connect(m_ui.actionAccelerators, &QAction::triggered,
this, &
MainWindow::revalidate);
2072 connect(m_ui.actionSurroundingWhitespace, &QAction::triggered,
this, &
MainWindow::revalidate);
2073 connect(m_ui.actionEndingPunctuation, &QAction::triggered,
this, &
MainWindow::revalidate);
2074 connect(m_ui.actionPhraseMatches, &QAction::triggered,
this, &
MainWindow::revalidate);
2075 connect(m_ui.actionPlaceMarkerMatches, &QAction::triggered,
this, &
MainWindow::revalidate);
2078 connect(m_ui.actionResetSorting, &QAction::triggered,
2080 connect(m_ui.actionDisplayGuesses, &QAction::triggered,
2081 m_phraseView, &PhraseView::toggleGuessing);
2082 connect(m_ui.actionStatistics, &QAction::triggered,
this, &
MainWindow::showStatistics);
2084 connect(m_ui.actionQmlPreview, &QAction::triggered,
this, &
MainWindow::toggleQmlPreview);
2086 m_ui.actionQmlPreview->setVisible(
false);
2088 connect(m_ui.actionFormPreview, &QAction::triggered,
this, &
MainWindow::toggleFormPreview);
2089 connect(m_ui.actionVisualizeWhitespace, &QAction::triggered,
2090 this, &
MainWindow::toggleVisualizeWhitespace);
2091 connect(m_ui.actionIncreaseZoom, &QAction::triggered,
2093 connect(m_ui.actionDecreaseZoom, &QAction::triggered,
2095 connect(m_ui.actionResetZoomToDefault, &QAction::triggered,
2097 connect(m_ui.actionShowMoreGuesses, &QAction::triggered,
2099 connect(m_ui.actionShowFewerGuesses, &QAction::triggered,
2102 m_ui.actionShowFewerGuesses, &QAction::setEnabled);
2103 connect(m_ui.actionResetGuessesToDefault, &QAction::triggered,
2105 m_ui.menuViewViews->addAction(m_contextAndLabelDock->toggleViewAction());
2106 m_ui.menuViewViews->addAction(m_messagesDock->toggleViewAction());
2107 m_ui.menuViewViews->addAction(m_phrasesDock->toggleViewAction());
2108 m_ui.menuViewViews->addAction(m_sourceAndFormDock->toggleViewAction());
2109 m_ui.menuViewViews->addAction(m_errorsDock->toggleViewAction());
2111#if defined(Q_OS_MACOS)
2113 QMenu *windowMenu =
new QMenu(tr(
"&Window"),
this);
2114 menuBar()->insertMenu(m_ui.menuHelp->menuAction(), windowMenu);
2115 windowMenu->addAction(tr(
"Minimize"), QKeySequence(tr(
"Ctrl+M")),
2116 this, &QWidget::showMinimized);
2120 connect(m_ui.actionManual, &QAction::triggered,
this, &
MainWindow::manual);
2121 connect(m_ui.actionAbout, &QAction::triggered,
this, &
MainWindow::about);
2122 connect(m_ui.actionAboutQt, &QAction::triggered,
this, &
MainWindow::aboutQt);
2123 connect(m_ui.actionWhatsThis, &QAction::triggered,
this, &
MainWindow::onWhatsThis);
2125 connect(m_ui.menuRecentlyOpenedFiles, &QMenu::triggered,
2128 m_ui.actionManual->setToolTip(tr(
"Displays the manual for %1.").arg(tr(
"Qt Linguist")));
2129 m_ui.actionAbout->setToolTip(tr(
"Displays information about %1.").arg(tr(
"Qt Linguist")));
2130 m_ui.actionDone->setShortcuts(
2131 { Qt::AltModifier | Qt::Key_Return, Qt::AltModifier | Qt::Key_Enter });
2132 m_ui.actionDoneAndNext->setShortcuts({
2133 Qt::ControlModifier | Qt::Key_Return,
2134 Qt::ControlModifier | Qt::Key_Enter,
2138 connect(m_ui.menuPhrases, &QMenu::aboutToShow,
this, &
MainWindow::setupPhrase);
2140 connect(m_ui.menuRecentlyOpenedFiles, &QMenu::aboutToShow,
2147 doUpdateLatestModel(model);
2151void MainWindow::updateLatestModel(
const QModelIndex &index)
2153 if (index.column() && (index.column() - 1 < m_dataModel
->modelCount()))
2154 doUpdateLatestModel(index.column() - 1);
2157void MainWindow::doUpdateLatestModel(
int model)
2161 bool enable =
false;
2162 bool enableRw =
false;
2171 m_phraseView->setSourceText(model, item->text());
2173 m_phraseView->setSourceText(-1, QString());
2175 m_phraseView->setSourceText(-1, QString());
2179 updateSourceView(model, item);
2180 m_ui.actionSave->setEnabled(enableRw);
2181 m_ui.actionSaveAs->setEnabled(enableRw);
2182 m_ui.actionRelease->setEnabled(enableRw);
2183 m_ui.actionReleaseAs->setEnabled(enableRw);
2184 m_ui.actionClose->setEnabled(enable);
2185 m_ui.actionTranslationFileSettings->setEnabled(enableRw);
2186 m_ui.actionSearchAndTranslate->setEnabled(enableRw);
2188 updatePhraseBookActions();
2196 const QString shownPath = item ? previewFilePath(model, item->fileName()) : QString();
2197 if (shownPath != m_uiPreviewOnceFile)
2198 m_uiPreviewOnceFile.clear();
2199 if (shownPath != m_qmlPreviewOnceFile)
2200 m_qmlPreviewOnceFile.clear();
2202 if (item && !item->fileName().isEmpty()) {
2203 if (hasUiFormPreview(model, item->fileName())) {
2204 m_sourceAndFormView->setCurrentWidget(m_uiFormPreviewView);
2207 }
else if (hasQmlFormPreview(model, item->fileName())
2208 && m_qmlFormPreviewView->setSourceContext(model, item)) {
2209 m_sourceAndFormView->setCurrentWidget(m_qmlFormPreviewView);
2215 if (item->fileName().endsWith(
".qml"_L1)) {
2216 m_ui.actionQmlPreview->setChecked(
false);
2217 m_qmlPreviewOnceFile.clear();
2220 m_sourceAndFormView->setCurrentWidget(m_sourceCodeContainer);
2221 updatePreviewBanner(item->fileName());
2222 QDir dir = QFileInfo(m_dataModel->srcFileName(model)).dir();
2223 QString fileName = QDir::cleanPath(dir.absoluteFilePath(item->fileName()));
2224 m_sourceCodeView->setSourceContext(fileName, item
->lineNumber());
2227 m_sourceAndFormView->setCurrentWidget(m_sourceCodeContainer);
2228 updatePreviewBanner(QString());
2229 m_sourceCodeView->setSourceContext(QString(), 0);
2236void MainWindow::updatePreviewBanner(
const QString &fileName)
2238 if (fileName.endsWith(
".ui"_L1) || fileName.endsWith(
".jui"_L1)) {
2239 m_previewBannerLabel->setText(tr(
"This is a Qt Widgets Designer form."));
2240 m_previewBanner->show();
2242 }
else if (fileName.endsWith(
".qml"_L1)) {
2243 m_previewBannerLabel->setText(tr(
"This is a QML file."));
2244 m_previewBanner->show();
2247 m_previewBanner->hide();
2260 const QString fileName = item->fileName();
2262 if (fileName.endsWith(
".qml"_L1)) {
2263 m_ui.actionQmlPreview->trigger();
2267 if (fileName.endsWith(
".ui"_L1) || fileName.endsWith(
".jui"_L1))
2268 m_ui.actionFormPreview->trigger();
2276 if (m_fileActiveModel != m_currentIndex
.model()) {
2281 QString fn = QFileInfo(m_dataModel->srcFileName(m_currentIndex.model())).baseName();
2283 m_ui.actionSave->setText(tr(
"&Save '%1'").arg(fn));
2284 m_ui.actionSaveAs->setText(tr(
"Save '%1' &As...").arg(fn));
2286 m_ui.actionSave->setText(tr(
"&Download '%1'").arg(fn));
2288 m_ui.actionRelease->setText(tr(
"Release '%1'").arg(fn));
2289 m_ui.actionReleaseAs->setText(tr(
"Release '%1' As...").arg(fn));
2290 m_ui.actionClose->setText(tr(
"&Close '%1'").arg(fn));
2293 m_ui.actionSave->setText(tr(
"&Save"));
2294 m_ui.actionSaveAs->setText(tr(
"Save &As..."));
2296 m_ui.actionSave->setText(tr(
"&Download"));
2298 m_ui.actionRelease->setText(tr(
"Release"));
2299 m_ui.actionReleaseAs->setText(tr(
"Release As..."));
2300 m_ui.actionClose->setText(tr(
"&Close"));
2304 m_ui.actionSaveAll->setText(tr(
"Save All"));
2306 m_ui.actionReleaseAll->setText(tr(
"&Release All"));
2307 m_ui.actionCloseAll->setText(tr(
"Close All"));
2311 m_ui.actionSaveAs->setText(tr(
"Save &As..."));
2312 m_ui.actionSaveAll->setText(tr(
"&Save"));
2314 m_ui.actionSave->setText(tr(
"&Download"));
2316 m_ui.actionReleaseAs->setText(tr(
"Release As..."));
2317 m_ui.actionReleaseAll->setText(tr(
"&Release"));
2318 m_ui.actionCloseAll->setText(tr(
"&Close"));
2322 m_ui.actionSave->setVisible(en);
2324 m_ui.actionRelease->setVisible(en);
2325 m_ui.actionClose->setVisible(en);
2326 m_fileActiveModel = m_currentIndex
.model();
2332 if (m_editActiveModel != m_currentIndex
.model()) {
2334 QString fn = QFileInfo(m_dataModel->srcFileName(m_currentIndex.model())).baseName();
2335 m_ui.actionTranslationFileSettings->setText(tr(
"Translation File &Settings for '%1'...").arg(fn));
2336 m_ui.actionBatchTranslation->setText(tr(
"&Batch Translation of '%1'...").arg(fn));
2337 m_ui.actionSearchAndTranslate->setText(tr(
"Search And &Translate in '%1'...").arg(fn));
2339 m_ui.actionTranslationFileSettings->setText(tr(
"Translation File &Settings..."));
2340 m_ui.actionBatchTranslation->setText(tr(
"&Batch Translation..."));
2341 m_ui.actionSearchAndTranslate->setText(tr(
"Search And &Translate..."));
2343 m_editActiveModel = m_currentIndex
.model();
2349 m_contextAndLabelDock->show();
2350 m_contextAndLabelDock->raise();
2355 m_messagesDock->show();
2356 m_messagesDock->raise();
2361 m_phrasesDock->show();
2362 m_phrasesDock->raise();
2367 m_sourceAndFormDock->show();
2368 m_sourceAndFormDock->raise();
2373 m_errorsDock->show();
2374 m_errorsDock->raise();
2379 QWhatsThis::enterWhatsThisMode();
2384 QToolBar *filet =
new QToolBar(
this);
2385 filet->setObjectName(
"FileToolbar");
2386 filet->setWindowTitle(tr(
"File"));
2387 this->addToolBar(filet);
2388 m_ui.menuToolbars->addAction(filet->toggleViewAction());
2390 QToolBar *editt =
new QToolBar(
this);
2391 editt->setVisible(
false);
2392 editt->setObjectName(
"EditToolbar");
2393 editt->setWindowTitle(tr(
"Edit"));
2394 this->addToolBar(editt);
2395 m_ui.menuToolbars->addAction(editt->toggleViewAction());
2397 QToolBar *translationst =
new QToolBar(
this);
2398 translationst->setObjectName(
"TranslationToolbar");
2399 translationst->setWindowTitle(tr(
"Translation"));
2400 this->addToolBar(translationst);
2401 m_ui.menuToolbars->addAction(translationst->toggleViewAction());
2403 QToolBar *validationt =
new QToolBar(
this);
2404 validationt->setObjectName(
"ValidationToolbar");
2405 validationt->setWindowTitle(tr(
"Validation"));
2406 this->addToolBar(validationt);
2407 m_ui.menuToolbars->addAction(validationt->toggleViewAction());
2409 QToolBar *helpt =
new QToolBar(
this);
2410 helpt->setVisible(
false);
2411 helpt->setObjectName(
"HelpToolbar");
2412 helpt->setWindowTitle(tr(
"Help"));
2413 this->addToolBar(helpt);
2414 m_ui.menuToolbars->addAction(helpt->toggleViewAction());
2417 filet->addAction(m_ui.actionOpen);
2418 filet->addAction(m_ui.actionSaveAll);
2419 filet->addAction(m_ui.actionPrint);
2420 filet->addSeparator();
2421 filet->addAction(m_ui.actionOpenPhraseBook);
2423 editt->addAction(m_ui.actionUndo);
2424 editt->addAction(m_ui.actionRedo);
2425 editt->addSeparator();
2426 editt->addAction(m_ui.actionCut);
2427 editt->addAction(m_ui.actionCopy);
2428 editt->addAction(m_ui.actionPaste);
2429 editt->addSeparator();
2430 editt->addAction(m_ui.actionFind);
2432 translationst->addAction(m_ui.actionPrev);
2433 translationst->addAction(m_ui.actionNext);
2434 translationst->addAction(m_ui.actionPrevUnfinished);
2435 translationst->addAction(m_ui.actionNextUnfinished);
2436 translationst->addAction(m_ui.actionDone);
2437 translationst->addAction(m_ui.actionDoneAndNext);
2439 validationt->addAction(m_ui.actionAccelerators);
2440 validationt->addAction(m_ui.actionSurroundingWhitespace);
2441 validationt->addAction(m_ui.actionEndingPunctuation);
2442 validationt->addAction(m_ui.actionPhraseMatches);
2443 validationt->addAction(m_ui.actionPlaceMarkerMatches);
2445 helpt->addAction(m_ui.actionWhatsThis);
2448QModelIndex
MainWindow::setMessageViewRoot(
const QModelIndex &index)
2450 const QModelIndex &sortedGroupIndex = m_activeSortedMessagesModel->mapFromSource(index);
2451 const QModelIndex &trueGroupIndex =
2452 m_activeSortedMessagesModel->index(sortedGroupIndex.row(), 0);
2453 if (m_messageView->rootIndex() != trueGroupIndex)
2454 m_messageView->setRootIndex(trueGroupIndex);
2455 return trueGroupIndex;
2459
2460
2461void MainWindow::setCurrentMessage(
const QModelIndex &index)
2463 const QModelIndex &groupIndex = m_activeMessageModel->parent(index);
2464 if (!groupIndex.isValid())
2467 const QModelIndex &trueIndex =
2468 m_activeMessageModel->index(groupIndex.row(), index.column(), QModelIndex());
2469 m_settingCurrentMessage =
true;
2470 QTreeView *view = *m_activeTranslationType == IDBASED ? m_labelView : m_contextView;
2471 view->setCurrentIndex(m_activeSortedGroupsModel->mapFromSource(trueIndex));
2472 m_settingCurrentMessage =
false;
2473 setMessageViewRoot(groupIndex);
2474 m_messageView->setCurrentIndex(m_activeSortedMessagesModel->mapFromSource(index));
2477void MainWindow::setCurrentMessage(
const QModelIndex &index,
int model)
2479 const QModelIndex &theIndex =
2480 m_activeMessageModel->index(index.row(), model + 1, index.parent());
2481 setCurrentMessage(theIndex);
2487 if (cand.context.isEmpty()) {
2488 int labelIndex = m_dataModel->findGroupIndex(cand.label,
IDBASED);
2491 setCurrentMessage(m_activeMessageModel->modelIndex(
2492 MultiDataIndex(IDBASED, modelIndex, labelIndex, messageIndex)));
2494 int contextIndex = m_dataModel->findGroupIndex(cand.context,
TEXTBASED);
2496 ->findMessage(cand.source, cand.disambiguation);
2497 setCurrentMessage(m_activeMessageModel->modelIndex(
2498 MultiDataIndex(TEXTBASED, modelIndex, contextIndex, messageIndex)));
2504 auto refreshMessageView = [
this](QTreeView *view) {
2505 m_messageView->reset();
2506 m_messageView->setModel(m_activeSortedMessagesModel);
2507 view->setCurrentIndex(m_activeSortedGroupsModel->index(0, 0));
2508 connect(m_messageView->selectionModel(), &QItemSelectionModel::currentRowChanged,
this,
2509 &MainWindow::selectedMessageChanged);
2510 connect(m_messageView->selectionModel(), &QItemSelectionModel::currentColumnChanged,
this,
2511 &MainWindow::updateLatestModel);
2512 m_messageView->update();
2513 if (m_activeMessageModel->rowCount())
2514 setCurrentMessage(m_activeMessageModel->modelIndex(
2515 MultiDataIndex(*m_activeTranslationType, 0, 0, 0)));
2516 selectedMessageChanged(m_messageView->currentIndex(), QModelIndex{});
2517 updateVisibleColumns();
2520 if (m_contextAndLabelView->currentWidget() == m_labelView
2521 && (!m_activeTranslationType || *m_activeTranslationType != IDBASED)) {
2522 m_activeTranslationType.emplace(IDBASED);
2523 m_activeSortedMessagesModel = m_sortedIdBasedMessagesModel;
2524 m_activeSortedGroupsModel = m_sortedLabelsModel;
2525 m_activeMessageModel = m_idBasedMessageModel;
2526 refreshMessageView(m_labelView);
2527 }
else if (m_contextAndLabelView->currentWidget() == m_contextView
2528 && (!m_activeTranslationType || *m_activeTranslationType != TEXTBASED)) {
2529 m_activeTranslationType.emplace(TEXTBASED);
2530 m_activeSortedMessagesModel = m_sortedTextBasedMessagesModel;
2531 m_activeSortedGroupsModel = m_sortedContextsModel;
2532 m_activeMessageModel = m_textBasedMessageModel;
2533 refreshMessageView(m_contextView);
2540 if (*m_activeTranslationType == IDBASED)
2542 for (
int i = 1; i < cols; i++)
2543 m_messageView->setColumnHidden(i,
false);
2544 for (
int i = cols; i < m_messageView->header()->count(); i++)
2545 m_messageView->setColumnHidden(i,
true);
2546 m_messageView->header()->setStretchLastSection(
true);
2549QModelIndex
MainWindow::currentMessageIndex()
const
2551 return m_activeSortedMessagesModel->mapToSource(m_messageView->currentIndex());
2558 if (!pb->load(name, &langGuessed)) {
2559 QMessageBox::warning(
this, tr(
"Qt Linguist"),
2560 tr(
"Cannot read from phrase book '%1'.").arg(name));
2565 if (!m_translationSettingsDialog)
2566 m_translationSettingsDialog =
new TranslationSettingsDialog(
this);
2568 m_translationSettingsDialog->exec();
2571 m_phraseBooks.append(pb);
2573 QAction *a = m_ui.menuClosePhraseBook->addAction(pb->friendlyPhraseBookName());
2574 m_phraseBookMenu[PhraseCloseMenu].insert(a, pb);
2575 a->setToolTip(tr(
"Close this phrase book."));
2577 a = m_ui.menuEditPhraseBook->addAction(pb->friendlyPhraseBookName());
2578 m_phraseBookMenu[PhraseEditMenu].insert(a, pb);
2579 a->setToolTip(tr(
"Enables you to add, modify, or delete"
2580 " entries in this phrase book."));
2582 a = m_ui.menuPrintPhraseBook->addAction(pb->friendlyPhraseBookName());
2583 m_phraseBookMenu[PhrasePrintMenu].insert(a, pb);
2584 a->setToolTip(tr(
"Print the entries in this phrase book."));
2587 updatePhraseDicts();
2588 updatePhraseBookActions();
2595 if (!name->contains(u'.'))
2598 if (!pb.save(*name)) {
2599 QMessageBox::warning(
this, tr(
"Qt Linguist"),
2600 tr(
"Cannot create phrase book '%1'.").arg(*name));
2609 switch (QMessageBox::information(
this, tr(
"Qt Linguist"),
2610 tr(
"Do you want to save phrase book '%1'?").arg(pb->friendlyPhraseBookName()),
2611 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
2613 case QMessageBox::Cancel:
2615 case QMessageBox::Yes:
2616 if (!pb->save(pb->fileName()))
2627 for (PhraseBook *phraseBook : std::as_const(m_phraseBooks))
2628 if (!maybeSavePhraseBook(phraseBook))
2638 m_progressLabel->setText(QString(
" "_L1));
2639 m_progressLabel->setToolTip(QString());
2641 m_progressLabel->setText(QStringLiteral(
" %1/%2 ").arg(numFinished).arg(numEditable));
2642 m_progressLabel->setToolTip(tr(
"%n unfinished message(s) left.", 0,
2643 numEditable - numFinished));
2645 bool enable = numFinished != numEditable;
2646 m_ui.actionPrevUnfinished->setEnabled(enable);
2647 m_ui.actionNextUnfinished->setEnabled(enable);
2648 m_ui.actionDone->setEnabled(enable);
2649 m_ui.actionDoneAndNext->setEnabled(enable);
2651 m_ui.actionPrev->setEnabled(m_dataModel->contextCount() > 0 || m_dataModel->labelCount() > 0);
2652 m_ui.actionNext->setEnabled(m_dataModel->contextCount() > 0 || m_dataModel->labelCount() > 0);
2657 bool phraseBookLoaded = (m_currentIndex.model() >= 0) && !m_phraseBooks.isEmpty();
2658 m_ui.actionBatchTranslation->setEnabled(m_dataModel->contextCount() > 0 && phraseBookLoaded
2659 && m_dataModel->isModelWritable(m_currentIndex.model()));
2660 m_ui.actionAddToPhraseBook->setEnabled(currentMessageIndex().isValid() && phraseBookLoaded);
2663void MainWindow::updatePhraseDictInternal(
int model)
2665 QHash<QString, QList<Phrase *> > &pd = m_phraseDict[model];
2668 for (PhraseBook *pb : std::as_const(m_phraseBooks)) {
2670 if (pb->language() != QLocale::C && m_dataModel->language(model) != QLocale::C) {
2671 if (pb->language() != m_dataModel->language(model))
2673 before = (pb->territory() == m_dataModel->model(model)->territory());
2677 const auto phrases = pb->phrases();
2678 for (Phrase *p : phrases) {
2679 QString f = friendlyString(p->source());
2681 f = f.split(u' ').first();
2682 if (!pd.contains(f)) {
2683 pd.insert(f, QList<Phrase *>());
2696 updatePhraseDictInternal(model);
2702 for (
int i = 0; i < m_phraseDict.size(); ++i)
2704 m_phraseDict[i].clear();
2706 updatePhraseDictInternal(i);
2718 Validator::Checks checks{ m_ui.actionAccelerators->isChecked(),
2719 m_ui.actionEndingPunctuation->isChecked(),
2720 m_ui.actionPlaceMarkerMatches->isChecked(),
2721 m_ui.actionSurroundingWhitespace->isChecked(),
2722 m_ui.actionPhraseMatches->isChecked() };
2732 bool danger =
false;
2734 if (source.isEmpty()) {
2735 source = m->pluralText();
2736 if (source.isEmpty())
2740 Validator validator = Validator::fromSource(
2741 source, checks, m_dataModel->sourceLanguage(mi), m_phraseDict[mi]);
2743 validator.validate(m->translations(), m
->message(), m_dataModel->language(mi),
2744 m_dataModel
->model(mi
)->countRefNeeds());
2745 danger = !errors.isEmpty();
2747 for (
const auto &[error, message] : errors)
2748 m_errorsView->addError(mi, error, message);
2756 statusBar()->showMessage(m_errorsView->firstError());
2763 restoreGeometry(config.value(settingPath(
"Geometry/WindowGeometry")).toByteArray());
2764 restoreState(config.value(settingPath(
"MainWindowState")).toByteArray());
2766 m_ui.actionAccelerators->setChecked(
2767 config.value(settingPath(
"Validators/Accelerator"),
true).toBool());
2768 m_ui.actionSurroundingWhitespace->setChecked(
2769 config.value(settingPath(
"Validators/SurroundingWhitespace"),
true).toBool());
2770 m_ui.actionEndingPunctuation->setChecked(
2771 config.value(settingPath(
"Validators/EndingPunctuation"),
true).toBool());
2772 m_ui.actionPhraseMatches->setChecked(
2773 config.value(settingPath(
"Validators/PhraseMatch"),
true).toBool());
2774 m_ui.actionPlaceMarkerMatches->setChecked(
2775 config.value(settingPath(
"Validators/PlaceMarkers"),
true).toBool());
2776 m_ui.actionLengthVariants->setChecked(
2777 config.value(settingPath(
"Options/LengthVariants"),
false).toBool());
2778 m_ui.actionVisualizeWhitespace->setChecked(
2779 config.value(settingPath(
"Options/VisualizeWhitespace"),
true).toBool());
2782 config.value(settingPath(
"Options/EditorFontsize"), font().pointSize()).toReal()
);
2786 m_recentFiles.readConfig();
2788 int size = config.beginReadArray(settingPath(
"OpenedPhraseBooks"));
2789 for (
int i = 0; i < size; ++i) {
2790 config.setArrayIndex(i);
2791 doOpenPhraseBook(config.value(
"FileName"_L1).toString());
2799 config.setValue(settingPath(
"Geometry/WindowGeometry"),
2801 config.setValue(settingPath(
"Validators/Accelerator"),
2802 m_ui.actionAccelerators->isChecked());
2803 config.setValue(settingPath(
"Validators/SurroundingWhitespace"),
2804 m_ui.actionSurroundingWhitespace->isChecked());
2805 config.setValue(settingPath(
"Validators/EndingPunctuation"),
2806 m_ui.actionEndingPunctuation->isChecked());
2807 config.setValue(settingPath(
"Validators/PhraseMatch"),
2808 m_ui.actionPhraseMatches->isChecked());
2809 config.setValue(settingPath(
"Validators/PlaceMarkers"),
2810 m_ui.actionPlaceMarkerMatches->isChecked());
2811 config.setValue(settingPath(
"Options/LengthVariants"),
2812 m_ui.actionLengthVariants->isChecked());
2813 config.setValue(settingPath(
"Options/VisualizeWhitespace"),
2814 m_ui.actionVisualizeWhitespace->isChecked());
2815 config.setValue(settingPath(
"MainWindowState"),
2817 m_recentFiles.writeConfig();
2819 config.setValue(settingPath(
"Options/EditorFontsize"), m_messageEditor
->fontSize());
2822 config.beginWriteArray(settingPath(
"OpenedPhraseBooks"),
2823 m_phraseBooks.size());
2824 for (
int i = 0; i < m_phraseBooks.size(); ++i) {
2825 config.setArrayIndex(i);
2826 config.setValue(
"FileName"_L1, m_phraseBooks.at(i)->fileName());
2833 m_ui.menuRecentlyOpenedFiles->clear();
2834 for (
const QStringList &strList : m_recentFiles.filesLists())
2835 if (strList.size() == 1) {
2836 const QString &str = strList.first();
2837 m_ui.menuRecentlyOpenedFiles->addAction(
2838 DataModel::prettifyFileName(str))->setData(str);
2840 QMenu *menu = m_ui.menuRecentlyOpenedFiles->addMenu(
2841 MultiDataModel::condenseFileNames(
2842 MultiDataModel::prettifyFileNames(strList)));
2843 menu->addAction(tr(
"All"))->setData(strList);
2844 for (
const QString &str : strList)
2845 menu->addAction(DataModel::prettifyFileName(str))->setData(str);
2849void MainWindow::recentFileActivated(QAction *action)
2851 openFiles(action->data().toStringList());
2856 if (!m_statistics) {
2857 m_statistics =
new Statistics(
this);
2860 m_statistics->show();
2869 updateSourceView(m_currentIndex
.model(), item);
2875QString
MainWindow::previewFilePath(
int model,
const QString &fileName)
const
2877 if (model < 0 || fileName.isEmpty())
2879 const QDir dir = QFileInfo(m_dataModel->srcFileName(model)).dir();
2880 return QDir::cleanPath(dir.absoluteFilePath(fileName));
2883bool MainWindow::hasUiFormPreview(
int model,
const QString &fileName)
const
2885 if (!fileName.endsWith(
".ui"_L1) && !fileName.endsWith(
".jui"_L1))
2887 if (m_ui.actionFormPreview->isChecked())
2889 const QString path = previewFilePath(model, fileName);
2890 return !path.isEmpty() && path == m_uiPreviewOnceFile;
2894bool MainWindow::hasQmlFormPreview(
int model,
const QString &fileName)
const
2896 if (!fileName.endsWith(
".qml"_L1))
2898 if (m_ui.actionQmlPreview->isChecked())
2900 const QString path = previewFilePath(model, fileName);
2901 return !path.isEmpty() && path == m_qmlPreviewOnceFile;
2909bool MainWindow::askPreviewActivation(
bool isQml)
2911 QAction *action = isQml ? m_ui.actionQmlPreview : m_ui.actionFormPreview;
2915 const QString fileName = item ? item->fileName() : QString();
2917 const bool canPreviewOnce = isQml
2918 ? fileName.endsWith(
".qml"_L1)
2919 : (fileName.endsWith(
".ui"_L1) || fileName.endsWith(
".jui"_L1));
2921 const QString text = isQml
2922 ? tr(
"The QML preview loads and renders QML files where the current message "
2923 "is used. This can execute code embedded in the QML, and may cause "
2924 "crashes or other unexpected behavior.\n\n"
2925 "Activate the preview only for sources you trust.\n\n"
2926 "Do you want to activate the preview?")
2927 : tr(
"The UI form preview loads and renders Qt Widgets Designer forms where "
2928 "the current message is used. This can execute code embedded in the UI "
2929 "file, and may cause crashes or other unexpected behavior.\n\n"
2930 "Activate the preview only for sources you trust.\n\n"
2931 "Do you want to activate the preview?");
2933 QAutoPointer<QMessageBox> box{
new QMessageBox(QMessageBox::Warning,
2934 tr(
"Qt Linguist"), text,
2935 QMessageBox::NoButton,
this)};
2936 QPushButton *dontPreview = box->addButton(tr(
"Do Not Preview"), QMessageBox::RejectRole);
2937 QPushButton *forSession = box->addButton(tr(
"Preview for Session"), QMessageBox::AcceptRole);
2939 canPreviewOnce ? box->addButton(tr(
"Preview Once"), QMessageBox::AcceptRole) :
nullptr;
2940 box->setDefaultButton(once ? once : dontPreview);
2945 if (box->clickedButton() == forSession)
2947 action->setChecked(
false);
2948 QString &onceFile = isQml ? m_qmlPreviewOnceFile : m_uiPreviewOnceFile;
2949 if (once && box->clickedButton() == once) {
2950 onceFile = previewFilePath(m_currentIndex.model(), fileName);
2960 if (m_ui.actionQmlPreview->isChecked()) {
2961 if (askPreviewActivation(
true))
2962 showSourceCodeDock();
2964 m_qmlPreviewOnceFile.clear();
2966 refreshFormPreview();
2972 if (m_ui.actionFormPreview->isChecked()) {
2973 if (askPreviewActivation(
false))
2974 showSourceCodeDock();
2976 m_uiPreviewOnceFile.clear();
2978 refreshFormPreview();
2996 if (!m_statistics || !m_statistics->isVisible() || m_currentIndex
.model() < 0)
3002void MainWindow::doShowTranslationSettings(
int model)
3004 if (!m_translationSettingsDialog)
3005 m_translationSettingsDialog =
new TranslationSettingsDialog(
this);
3007 m_translationSettingsDialog->exec();
3012 doShowTranslationSettings(m_currentIndex
.model());
3017 if (event->type() == QEvent::DragEnter) {
3018 QDragEnterEvent *e =
static_cast<QDragEnterEvent*>(event);
3019 if (e->mimeData()->hasFormat(
"text/uri-list"_L1)) {
3020 e->acceptProposedAction();
3023 }
else if (event->type() == QEvent::Drop) {
3024 QDropEvent *e =
static_cast<QDropEvent*>(event);
3025 if (!e->mimeData()->hasFormat(
"text/uri-list"_L1))
3028 const auto &qurls = e->mimeData()->urls();
3029 for (
const QUrl &url : qurls)
3030 if (!url.toLocalFile().isEmpty())
3031 urls << url.toLocalFile();
3032 if (!urls.isEmpty())
3034 e->acceptProposedAction();
3036 }
else if (event->type() == QEvent::KeyPress) {
3037 QKeyEvent *ke =
static_cast<QKeyEvent *>(event);
3038 if (ke->key() == Qt::Key_Escape) {
3039 if (object == m_messageEditor)
3040 m_messageView->setFocus();
3041 else if (object == m_messagesDock)
3042 m_contextAndLabelView->currentWidget()->setFocus();
3043 }
else if ((ke->key() == Qt::Key_Plus || ke->key() == Qt::Key_Equal)
3044 && (ke->modifiers() & Qt::ControlModifier)) {
3046 }
else if (ke->key() == Qt::Key_Minus
3047 && (ke->modifiers() & Qt::ControlModifier)) {
3050 }
else if (event->type() == QEvent::Wheel) {
3051 QWheelEvent *we =
static_cast<QWheelEvent *>(event);
3052 if (we->modifiers() & Qt::ControlModifier) {
3053 if (we->angleDelta().y() > 0)
3058 }
else if (event->type() == QEvent::ApplicationPaletteChange) {
3062 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)
MainWindow(HelpClientType helpClientType)
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 setLengthVariants(bool on)
void redoAvailable(bool avail)
void cutAvailable(bool avail)
void undoAvailable(bool avail)
void setVisualizeWhitespace(bool value)
void setEditorFocusForModel(int model)
void pasteAvailable(bool avail)
void setFontSize(const float fontSize)
void setUnfinishedEditorFocus()
bool focusNextUnfinished()
void activeModelChanged(int model)
void copyAvailable(bool avail)
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 modifiedChanged(bool)
void translationChanged(const MultiDataIndex &index)
void statsChanged(const StatisticalData &newStats)
void multiGroupDataChanged(const MultiDataIndex &index)
MessageItem * messageItem(const MultiDataIndex &index) const
void setFinished(const MultiDataIndex &index, bool finished)
void messageDataChanged(const MultiDataIndex &index)
void setMaxCandidates(const int max)
int getMaxCandidates() const
static int getDefaultMaxCandidates()
void showFewerGuessesAvailable(bool canShow)
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 const int MessageMS
static QString fileFilters(bool allFirst)
static const QVariant & pxObsolete()
int firstNonobsoleteMessageIndex(int msgIdx) const
bool isUnfinished() const
OpenedFile(DataModel *_dataModel, bool _readWrite, bool _langGuessed)