5
6
7
15#include <helpclient.h>
33#include <QApplication>
37#include <QDesktopServices>
45#include <QInputDialog>
46#include <QItemDelegate>
49#include <QLibraryInfo>
55#include <QRegularExpression>
59#include <QSortFilterProxyModel>
60#include <QStackedWidget>
68#if QT_CONFIG(printsupport)
69#include <QPrintDialog>
73using namespace Qt::Literals::StringLiterals;
79class GroupItemDelegate :
public QItemDelegate
82 GroupItemDelegate(QObject *parent, MultiDataModel *model)
83 : QItemDelegate(parent), m_dataModel(model)
87 void paint(QPainter *painter,
const QStyleOptionViewItem &option,
88 const QModelIndex &index)
const override
90 const QAbstractItemModel *model = index.model();
93 if (!model->parent(index).isValid()) {
94 if (index.column() - 1 == m_dataModel->modelCount()) {
95 QStyleOptionViewItem opt = option;
96 opt.font.setBold(
true);
97 QItemDelegate::paint(painter, opt, index);
101 QItemDelegate::paint(painter, option, index);
105 MultiDataModel *m_dataModel;
110 static const QVariant v = createMarkIcon(TranslationMarks::ObsoleteMark);
125 if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
131 return MainWindow::tr(
"Source text");
135 case 0:
return QString();
137 return MainWindow::tr(
"ID");
139 return MainWindow::tr(
"Source text");
143 if (role == Qt::DecorationRole && orientation == Qt::Horizontal && section - 1 < m_dataModel->modelCount())
164 if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
166 case 0:
return QString();
168 return m_translationType == TEXTBASED ? MainWindow::tr(
"Context")
169 : MainWindow::tr(
"Label");
171 return MainWindow::tr(
"Items");
174 if (role == Qt::DecorationRole && orientation == Qt::Horizontal && section - 1 < m_dataModel->modelCount())
199 if (event->type() == QEvent::FocusIn)
207#if QT_CONFIG(printsupport)
211 m_translationSettingsDialog(0),
212 m_settingCurrentMessage(
false),
213 m_fileActiveModel(-1),
214 m_editActiveModel(-1),
218 setUnifiedTitleAndToolBarOnMac(
true);
221#if !defined(Q_OS_MACOS) && !defined(Q_OS_WIN)
222 setWindowIcon(QPixmap(
":/images/appicon.png"_L1));
225 m_dataModel =
new MultiDataModel(
this);
226 m_idBasedMessageModel =
new MessageModel(IDBASED,
this, m_dataModel);
227 m_textBasedMessageModel =
new MessageModel(TEXTBASED,
this, m_dataModel);
230 m_contextAndLabelDock =
new QDockWidget(
this);
231 m_contextAndLabelDock->setObjectName(
"ContextLabelDockWidget");
232 m_contextAndLabelDock->setAllowedAreas(Qt::AllDockWidgetAreas);
233 m_contextAndLabelDock->setWindowTitle(tr(
"Context/Label"));
234 m_contextAndLabelDock->setAcceptDrops(
true);
235 m_contextAndLabelDock->installEventFilter(
this);
237 m_contextAndLabelView =
new QTabWidget(
this);
239 QWidget* dockContent =
new QWidget(
this);
240 QBoxLayout* layout =
new QBoxLayout(QBoxLayout::LeftToRight, dockContent);
241 layout->addWidget(m_contextAndLabelView);
243 m_contextAndLabelDock->setWidget(dockContent);
246 m_sortedContextsModel =
new SortedGroupsModel(
this, m_dataModel, TEXTBASED);
247 m_sortedContextsModel->setSortRole(MessageModel::SortRole);
248 m_sortedContextsModel->setSortCaseSensitivity(Qt::CaseInsensitive);
249 m_sortedContextsModel->setSourceModel(m_textBasedMessageModel);
251 m_contextView =
new QTreeView(
this);
252 m_contextView->setRootIsDecorated(
false);
253 m_contextView->setItemsExpandable(
false);
254 m_contextView->setUniformRowHeights(
true);
255 m_contextView->setAlternatingRowColors(
true);
256 m_contextView->setAllColumnsShowFocus(
true);
257 m_contextView->setItemDelegate(
new GroupItemDelegate(
this, m_dataModel));
258 m_contextView->setSortingEnabled(
true);
259 m_contextView->setWhatsThis(tr(
"This panel lists the source contexts."));
260 m_contextView->setModel(m_sortedContextsModel);
261 m_contextView->header()->setSectionsMovable(
false);
262 m_contextView->setColumnHidden(0,
true);
263 m_contextView->header()->setStretchLastSection(
false);
265 m_contextAndLabelView->addTab(m_contextView,
"Text Based"_L1);
268 m_sortedLabelsModel =
new SortedGroupsModel(
this, m_dataModel, IDBASED);
269 m_sortedLabelsModel->setSortRole(MessageModel::SortRole);
270 m_sortedLabelsModel->setSortCaseSensitivity(Qt::CaseInsensitive);
271 m_sortedLabelsModel->setSourceModel(m_idBasedMessageModel);
273 m_labelView =
new QTreeView(
this);
274 m_labelView->setRootIsDecorated(
false);
275 m_labelView->setItemsExpandable(
false);
276 m_labelView->setUniformRowHeights(
true);
277 m_labelView->setAlternatingRowColors(
true);
278 m_labelView->setAllColumnsShowFocus(
true);
279 m_labelView->setItemDelegate(
new GroupItemDelegate(
this, m_dataModel));
280 m_labelView->setSortingEnabled(
true);
281 m_labelView->setWhatsThis(tr(
"This panel lists the source labels."));
282 m_labelView->setModel(m_sortedLabelsModel);
283 m_labelView->header()->setSectionsMovable(
false);
284 m_labelView->setColumnHidden(0,
true);
285 m_labelView->header()->setStretchLastSection(
false);
287 m_contextAndLabelView->addTab(m_labelView,
"ID Based"_L1);
290 m_messagesDock =
new QDockWidget(
this);
291 m_messagesDock->setObjectName(
"StringsDockWidget");
292 m_messagesDock->setAllowedAreas(Qt::AllDockWidgetAreas);
293 m_messagesDock->setWindowTitle(tr(
"Strings"));
294 m_messagesDock->setAcceptDrops(
true);
295 m_messagesDock->installEventFilter(
this);
297 m_sortedTextBasedMessagesModel =
new SortedMessagesModel(
this, m_dataModel, TEXTBASED);
298 m_sortedTextBasedMessagesModel->setSortRole(MessageModel::SortRole);
299 m_sortedTextBasedMessagesModel->setSortCaseSensitivity(Qt::CaseInsensitive);
300 m_sortedTextBasedMessagesModel->setSortLocaleAware(
true);
301 m_sortedTextBasedMessagesModel->setSourceModel(m_textBasedMessageModel);
303 m_sortedIdBasedMessagesModel =
new SortedMessagesModel(
this, m_dataModel, IDBASED);
304 m_sortedIdBasedMessagesModel->setSortRole(MessageModel::SortRole);
305 m_sortedIdBasedMessagesModel->setSortCaseSensitivity(Qt::CaseInsensitive);
306 m_sortedIdBasedMessagesModel->setSortLocaleAware(
true);
307 m_sortedIdBasedMessagesModel->setSourceModel(m_idBasedMessageModel);
309 m_messageView =
new QTreeView(m_messagesDock);
310 m_messageView->setSortingEnabled(
true);
311 m_messageView->setRootIsDecorated(
false);
312 m_messageView->setUniformRowHeights(
true);
313 m_messageView->setAllColumnsShowFocus(
true);
314 m_messageView->setItemsExpandable(
false);
315 m_messageView->setModel(m_sortedTextBasedMessagesModel);
316 m_messageView->header()->setSectionsMovable(
false);
317 m_messageView->setColumnHidden(0,
true);
319 m_messagesDock->setWidget(m_messageView);
322 m_messageEditor =
new MessageEditor(m_dataModel,
this);
323 m_messageEditor->setAcceptDrops(
true);
324 m_messageEditor->installEventFilter(
this);
326 QBoxLayout *lout =
new QBoxLayout(QBoxLayout::TopToBottom, m_ui.centralwidget);
327 lout->addWidget(m_messageEditor);
328 lout->setContentsMargins(QMargins());
329 m_ui.centralwidget->setLayout(lout);
332 m_phrasesDock =
new QDockWidget(
this);
333 m_phrasesDock->setObjectName(
"PhrasesDockwidget");
334 m_phrasesDock->setAllowedAreas(Qt::AllDockWidgetAreas);
335 m_phrasesDock->setWindowTitle(tr(
"Phrases and guesses"));
337 m_phraseView =
new PhraseView(m_dataModel, &m_phraseDict,
this);
338 m_phrasesDock->setWidget(m_phraseView);
341 m_sourceAndFormDock =
new QDockWidget(
this);
342 m_sourceAndFormDock->setObjectName(
"SourceAndFormDock");
343 m_sourceAndFormDock->setAllowedAreas(Qt::AllDockWidgetAreas);
344 m_sourceAndFormDock->setWindowTitle(tr(
"Sources and Forms"));
345 m_sourceAndFormView =
new QStackedWidget(
this);
346 m_sourceAndFormDock->setWidget(m_sourceAndFormView);
347 m_uiFormPreviewView =
new UiFormPreviewView(0, m_dataModel);
348 m_sourceCodeView =
new SourceCodeView(0);
352 m_sourceCodeContainer =
new QWidget;
353 QVBoxLayout *sourceCodeLayout =
new QVBoxLayout(m_sourceCodeContainer);
354 sourceCodeLayout->setContentsMargins(0, 0, 0, 0);
355 sourceCodeLayout->setSpacing(0);
356 m_previewBanner =
new QFrame;
357 m_previewBanner->setFrameShape(QFrame::StyledPanel);
358 QHBoxLayout *bannerLayout =
new QHBoxLayout(m_previewBanner);
359 m_previewBannerLabel =
new QLabel;
360 QPushButton *showPreviewButton =
new QPushButton(tr(
"Show preview"));
361 bannerLayout->addWidget(m_previewBannerLabel, 1);
362 bannerLayout->addWidget(showPreviewButton, 0);
363 m_previewBanner->hide();
364 connect(showPreviewButton, &QPushButton::clicked,
this, &
MainWindow::enablePreviewFromBanner);
365 sourceCodeLayout->addWidget(m_previewBanner);
366 sourceCodeLayout->addWidget(m_sourceCodeView, 1);
369 m_qmlFormPreviewView =
new QmlFormPreviewView(m_dataModel);
370 m_sourceAndFormView->addWidget(m_qmlFormPreviewView);
372 m_sourceAndFormView->addWidget(m_sourceCodeContainer);
373 m_sourceAndFormView->addWidget(m_uiFormPreviewView);
376 m_errorsDock =
new QDockWidget(
this);
377 m_errorsDock->setObjectName(
"ErrorsDockWidget");
378 m_errorsDock->setAllowedAreas(Qt::AllDockWidgetAreas);
379 m_errorsDock->setWindowTitle(tr(
"Warnings"));
380 m_errorsView =
new ErrorsView(m_dataModel,
this);
381 m_errorsDock->setWidget(m_errorsView);
384 setDockNestingEnabled(
true);
385 setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
386 setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
387 setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
388 setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
389 addDockWidget(Qt::LeftDockWidgetArea, m_contextAndLabelDock);
390 addDockWidget(Qt::TopDockWidgetArea, m_messagesDock);
391 addDockWidget(Qt::BottomDockWidgetArea, m_phrasesDock);
392 addDockWidget(Qt::TopDockWidgetArea, m_sourceAndFormDock);
393 m_sourceAndFormDock->hide();
394 addDockWidget(Qt::BottomDockWidgetArea, m_errorsDock);
399 m_messageEditor->installEventFilter(m_phraseView);
402 QShortcut *contextShortcut =
new QShortcut(QKeySequence(Qt::Key_F6),
this);
403 connect(contextShortcut, &QShortcut::activated,
405 QShortcut *messagesShortcut =
new QShortcut(QKeySequence(Qt::Key_F7),
this);
406 connect(messagesShortcut, &QShortcut::activated,
408 QShortcut *errorsShortcut =
new QShortcut(QKeySequence(Qt::Key_F8),
this);
409 connect(errorsShortcut, &QShortcut::activated,
411 QShortcut *sourceCodeShortcut =
new QShortcut(QKeySequence(Qt::Key_F9),
this);
412 connect(sourceCodeShortcut, &QShortcut::activated,
414 QShortcut *phrasesShortcut =
new QShortcut(QKeySequence(Qt::Key_F10),
this);
415 connect(phrasesShortcut, &QShortcut::activated,
418 connect(m_phraseView, &PhraseView::phraseSelected,
421 this, &
MainWindow::setCurrentMessageFromGuess);
422 connect(m_contextView->selectionModel(), &QItemSelectionModel::currentRowChanged,
423 this, &MainWindow::selectedContextChanged);
424 connect(m_labelView->selectionModel(), &QItemSelectionModel::currentRowChanged,
this,
425 &MainWindow::selectedLabelChanged);
426 connect(m_contextView->selectionModel(), &QItemSelectionModel::currentColumnChanged,
427 this, &MainWindow::updateLatestModel);
428 connect(m_labelView->selectionModel(), &QItemSelectionModel::currentColumnChanged,
this,
429 &MainWindow::updateLatestModel);
432 connect(m_contextAndLabelView, &QTabWidget::currentChanged,
this,
435 m_translateDialog =
new TranslateDialog(
this);
436 m_batchTranslateDialog =
new BatchTranslationDialog(m_dataModel,
this);
437 m_findDialog =
new FindDialog(
this);
442 m_progressLabel =
new QLabel();
443 statusBar()->addPermanentWidget(m_progressLabel);
444 m_modifiedLabel =
new QLabel(tr(
" MOD ",
"status bar: file(s) modified"));
445 statusBar()->addPermanentWidget(m_modifiedLabel);
446 m_contextAndLabelView->setCurrentWidget(m_contextView);
447 contextAndLabelTabChanged();
454 this, &QWidget::setWindowModified);
456 m_modifiedLabel, &QWidget::setVisible);
468 connect(m_messageView, &QAbstractItemView::clicked,
469 this, &MainWindow::toggleFinished);
470 connect(m_messageView, &QAbstractItemView::activated,
472 connect(m_contextView, &QAbstractItemView::activated,
473 m_messageView, qOverload<>(&QWidget::setFocus));
474 connect(m_labelView, &QAbstractItemView::activated, m_messageView,
475 qOverload<>(&QWidget::setFocus));
476 connect(m_messageEditor, &MessageEditor::translationChanged,
477 this, &MainWindow::updateTranslation);
478 connect(m_messageEditor, &
MessageEditor::translatorCommentChanged,
479 this, &MainWindow::updateTranslatorComment);
480 connect(m_findDialog, &FindDialog::findNext,
481 this, &MainWindow::findNext);
482 connect(m_translateDialog, &TranslateDialog::requestMatchUpdate,
484 connect(m_translateDialog, &TranslateDialog::activated,
487 QSize as(screen()->size());
489 resize(QSize(1000, 800).boundedTo(as));
494 connect(m_ui.actionLengthVariants, &QAction::toggled,
499 m_focusWatcher =
new FocusWatcher(m_messageEditor,
this);
500 m_contextView->installEventFilter(m_focusWatcher);
501 m_labelView->installEventFilter(m_focusWatcher);
502 m_messageView->installEventFilter(m_focusWatcher);
503 m_messageEditor->installEventFilter(m_focusWatcher);
504 m_sourceAndFormView->installEventFilter(m_focusWatcher);
505 m_phraseView->installEventFilter(m_focusWatcher);
506 m_errorsView->installEventFilter(m_focusWatcher);
512 qDeleteAll(m_phraseBooks);
515#if QT_CONFIG(printsupport)
522 m_contextView->header()->setSectionResizeMode(1, QHeaderView::Stretch);
523 m_contextView->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
524 m_labelView->header()->setSectionResizeMode(1, QHeaderView::Stretch);
525 m_labelView->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
533 for (
int i = 1; i < mc + 1; ++i) {
534 m_contextView->header()->setSectionResizeMode(i, QHeaderView::Fixed);
535 m_contextView->header()->resizeSection(i, 24);
537 m_labelView->header()->setSectionResizeMode(i, QHeaderView::Fixed);
538 m_labelView->header()->resizeSection(i, 24);
540 m_messageView->header()->setSectionResizeMode(i, QHeaderView::Fixed);
541 m_messageView->header()->resizeSection(i, 24);
543 for (
int i = mc + 1; i < m_messageView->header()->count(); i++)
544 m_messageView->header()->setSectionResizeMode(i, QHeaderView::Stretch);
547 selectedMessageChanged(QModelIndex(), QModelIndex());
548 doUpdateLatestModel(-1);
550 QTreeView *view = qobject_cast<QTreeView *>(m_contextAndLabelView->currentWidget());
551 if (!view->currentIndex().isValid()) {
553 view->setCurrentIndex(m_activeSortedGroupsModel->index(0, 0));
556 view->selectionModel()->select(view->currentIndex(),
557 QItemSelectionModel::SelectCurrent
558 | QItemSelectionModel::Rows);
559 m_messageView->selectionModel()->select(m_messageView->currentIndex(),
560 QItemSelectionModel::SelectCurrent|QItemSelectionModel::Rows);
565 doUpdateLatestModel(0);
566 else if (m_currentIndex
.model() >= mc)
567 doUpdateLatestModel(mc - 1);
570 m_contextView->setUpdatesEnabled(
true);
571 m_labelView->setUpdatesEnabled(
true);
572 m_messageView->setUpdatesEnabled(
true);
577 m_ui.actionFind->setEnabled(m_dataModel->contextCount() > 0 || m_dataModel->labelCount() > 0);
578 m_ui.actionFindNext->setEnabled(
false);
579 m_ui.actionFindPrev->setEnabled(
false);
585 updateVisibleColumns();
601 bool waitCursor =
false;
602 statusBar()->showMessage(tr(
"Loading..."));
603 qApp->processEvents();
606 bool closeOld =
false;
607 for (QString name : names) {
609 QApplication::setOverrideCursor(Qt::WaitCursor);
613 bool readWrite = m_globalReadWrite;
614 if (name.startsWith(u'=')) {
620 name = fi.canonicalFilePath();
621 if (m_dataModel->isFileLoaded(name) >= 0)
625 DataModel *dm =
new DataModel(m_dataModel);
626 if (!dm->load(name, &langGuessed,
this)) {
630 if (opened.isEmpty()) {
631 if (!m_dataModel->isWellMergeable(dm)) {
632 QApplication::restoreOverrideCursor();
634 switch (QMessageBox::information(
this, tr(
"Loading File - Qt Linguist"),
635 tr(
"The file '%1' does not seem to be related to the currently open file(s) '%2'.\n\n"
636 "Close the open file(s) first?")
637 .arg(DataModel::prettifyPlainFileName(name), m_dataModel->condensedSrcFileNames(
true)),
638 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
640 case QMessageBox::Cancel:
643 case QMessageBox::Yes:
651 if (!opened.first().dataModel->isWellMergeable(dm)) {
652 QApplication::restoreOverrideCursor();
654 switch (QMessageBox::information(
this, tr(
"Loading File - Qt Linguist"),
655 tr(
"The file '%1' does not seem to be related to the file '%2'"
656 " which is being loaded as well.\n\n"
657 "Skip loading the first named file?")
658 .arg(DataModel::prettifyPlainFileName(name), opened.first().dataModel->srcFileName(
true)),
659 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
661 case QMessageBox::Cancel:
663 for (
const OpenedFile &op : std::as_const(opened))
666 case QMessageBox::Yes:
674 opened.append(OpenedFile(dm, readWrite, langGuessed));
679 QApplication::restoreOverrideCursor();
683 for (
const OpenedFile &op : std::as_const(opened))
689 for (
const OpenedFile &op : std::as_const(opened)) {
690 if (op.langGuessed) {
692 QApplication::restoreOverrideCursor();
695 if (!m_translationSettingsDialog)
696 m_translationSettingsDialog =
new TranslationSettingsDialog(
this);
697 m_translationSettingsDialog->setDataModel(op.dataModel);
698 m_translationSettingsDialog->exec();
703 QApplication::setOverrideCursor(Qt::WaitCursor);
704 m_contextView->setUpdatesEnabled(
false);
705 m_labelView->setUpdatesEnabled(
false);
706 m_messageView->setUpdatesEnabled(
false);
708 for (
const OpenedFile &op : std::as_const(opened)) {
709 m_phraseDict.append(QHash<QString, QList<Phrase *> >());
710 m_dataModel->append(op.dataModel, op.readWrite);
712 updatePhraseDictInternal(m_phraseDict.size() - 1);
713 totalCount += op.dataModel->messageCount();
715 statusBar()->showMessage(tr(
"%n translation unit(s) loaded.", 0, totalCount),
MessageMS);
717 m_recentFiles.addFiles(m_dataModel->srcFileNames());
720 QApplication::restoreOverrideCursor();
726 m_globalReadWrite =
true;
727 pickTranslationFiles();
732 m_globalReadWrite =
false;
733 pickTranslationFiles();
739 if (model >= 0 && maybeSave(model)) {
740 m_phraseDict.removeAt(model);
741 m_contextView->setUpdatesEnabled(
false);
742 m_labelView->setUpdatesEnabled(
false);
743 m_messageView->setUpdatesEnabled(
false);
751 if (maybeSaveAll()) {
752 m_phraseDict.clear();
753 m_contextView->setUpdatesEnabled(
false);
754 m_labelView->setUpdatesEnabled(
false);
755 m_messageView->setUpdatesEnabled(
false);
759 m_recentFiles.closeGroup();
767 static const QString pattern(
"%1 (*.%2);;"_L1);
768 QStringList allExtensions;
770 for (
const Translator::FileFormat &format : std::as_const(Translator::registeredFileFormats())) {
771 if (format.fileType == Translator::FileFormat::TranslationSource && format.priority >= 0) {
772 filter.append(pattern.arg(format.description(), format.extension));
773 allExtensions.append(
"*."_L1 + format.extension);
776 QString allFilter = QObject::tr(
"Translation files (%1);;").arg(allExtensions.join(u' '));
778 filter.prepend(allFilter);
780 filter.append(allFilter);
781 filter.append(QObject::tr(
"All files (*)"));
788 if (!m_recentFiles.isEmpty())
789 dir = QFileInfo(m_recentFiles.lastOpenedFile()).path();
793 QFileInfo mainFile(m_dataModel->srcFileName(0));
794 QString mainFileBase = mainFile.baseName();
795 int pos = mainFileBase.indexOf(u'_');
797 varFilt = tr(
"Related files (%1);;")
798 .arg(mainFileBase.left(pos) +
"_*."_L1 + mainFile.completeSuffix());
802 openFiles(QFileDialog::getOpenFileNames(
this, tr(
"Open Translation Files"), dir,
803 varFilt + fileFilters(
true)));
805 const auto fileOpenCompleted = [
this](
const QString &fileName,
const QByteArray &fileContent) {
806 const QString copyFileName =
807 QDir::tempPath() + QLatin1String(
"/") + QFileInfo(fileName).fileName();
808 QFile tsFile(copyFileName);
809 if (tsFile.open(QIODevice::WriteOnly)) {
810 tsFile.write(fileContent);
812 openFiles({ copyFileName });
813 m_wasmFileMap[copyFileName] = std::move(fileName);
817 QFileDialog::getOpenFileContent(varFilt + fileFilters(
true), fileOpenCompleted);
823 QApplication::setOverrideCursor(Qt::WaitCursor);
824 if (m_dataModel->save(model,
this)) {
827 QString wasmFileName = m_dataModel->model(model)->srcFileName();
828 if (
const auto itr = m_wasmFileMap.find(wasmFileName); itr != m_wasmFileMap.end()) {
829 QFile tsFile(wasmFileName);
830 if (tsFile.open(QIODevice::ReadOnly)) {
832 QByteArray content = tsFile.readAll();
833 QFileDialog::saveFileContent(content, itr.value(),
this);
837 statusBar()->showMessage(tr(
"File saved."),
MessageMS);
839 QApplication::restoreOverrideCursor();
847 m_recentFiles.closeGroup();
853 QMessageBox::warning(
this, tr(
"Qt Linguist"), tr(
"Please select a file to be saved."));
857 saveInternal(m_currentIndex
.model());
863 QMessageBox::warning(
this, tr(
"Qt Linguist"),
864 tr(
"This function is not available on WebAssembly"));
871 QString newFilename = QFileDialog::getSaveFileName(
872 this, QString(), m_dataModel->srcFileName(m_currentIndex.model()), fileFilters(
false));
873 if (!newFilename.isEmpty()) {
874 if (m_dataModel->saveAs(m_currentIndex
.model(), newFilename,
this)) {
876 statusBar()->showMessage(tr(
"File saved."),
MessageMS);
877 m_recentFiles.addFiles(m_dataModel->srcFileNames());
887 QFileInfo oldFile(m_dataModel->srcFileName(m_currentIndex
.model()));
888 QString newFilename = oldFile.path() +
"/"_L1 + oldFile.completeBaseName() +
".qm"_L1;
890 newFilename = QFileDialog::getSaveFileName(
this, tr(
"Release"), newFilename,
891 tr(
"Qt message files for released applications (*.qm)\nAll files (*)"));
892 if (!newFilename.isEmpty()) {
894 statusBar()->showMessage(tr(
"File created."),
MessageMS);
900 QFileInfo oldFile(m_dataModel->srcFileName(model));
901 QString newFilename = oldFile.path() + u'/' + oldFile.completeBaseName() +
".qm"_L1;
903 if (!newFilename.isEmpty()) {
904 if (m_dataModel->release(model, newFilename,
false,
false,
SaveEverything,
this))
905 statusBar()->showMessage(tr(
"File created."),
MessageMS);
915 releaseInternal(m_currentIndex
.model());
925#if QT_CONFIG(printsupport)
926QPrinter *MainWindow::printer()
929 m_printer =
new QPrinter;
933void MainWindow::print()
936 QPrintDialog dlg(printer(),
this);
938 QApplication::setOverrideCursor(Qt::WaitCursor);
939 printer()->setDocName(m_dataModel->condensedSrcFileNames(
true));
940 statusBar()->showMessage(tr(
"Printing..."));
941 PrintOut pout(printer());
943 auto printGroupItem = [&pout, &pageNum,
this](
int index, TranslationType type) {
944 MultiGroupItem *mg = m_dataModel->multiGroupItem(index, type);
946 pout.setRule(PrintOut::ThickRule);
947 pout.setGuide(mg->group());
949 pout.addBox(100, tr(
"Label: %1").arg(mg->group()), PrintOut::Strong);
951 pout.addBox(100, tr(
"Context: %1").arg(mg->group()), PrintOut::Strong);
954 pout.addBox(92, mg->comment(), PrintOut::Emphasis);
956 pout.setRule(PrintOut::ThickRule);
958 for (
int j = 0; j < mg->messageCount(); ++j) {
959 pout.setRule(PrintOut::ThinRule);
960 bool printedSrc =
false;
962 for (
int k = 0; k < m_dataModel->modelCount(); ++k) {
963 if (
const MessageItem *m = mg->messageItem(k, j)) {
965 pout.addBox(40, m->text());
967 comment = m->comment();
972 if (m->message().isPlural() && m_dataModel->language(k) != QLocale::C) {
973 QStringList transls = m->translations();
974 pout.addBox(40, transls.join(u'\n'));
976 pout.addBox(40, m->translation());
980 switch (m->message().type()) {
981 case TranslatorMessage::Finished:
982 type = tr(
"finished");
984 case TranslatorMessage::Unfinished:
985 type = m->danger() ? tr(
"unresolved") :
"unfinished"_L1;
987 case TranslatorMessage::Obsolete:
988 case TranslatorMessage::Vanished:
989 type = tr(
"obsolete");
992 pout.addBox(12, type, PrintOut::Normal, Qt::AlignRight);
996 if (!comment.isEmpty()) {
998 pout.addBox(92, comment, PrintOut::Emphasis);
999 pout.flushLine(
true);
1002 if (pout.pageNum() != pageNum) {
1003 pageNum = pout.pageNum();
1004 statusBar()->showMessage(tr(
"Printing... (page %1)").arg(pageNum));
1009 for (
int i = 0; i < m_dataModel->labelCount(); ++i)
1010 printGroupItem(i, IDBASED);
1011 for (
int i = 0; i < m_dataModel->contextCount(); ++i)
1012 printGroupItem(i, TEXTBASED);
1013 pout.flushLine(
true);
1014 QApplication::restoreOverrideCursor();
1015 statusBar()->showMessage(tr(
"Printing completed"), MessageMS);
1017 statusBar()->showMessage(tr(
"Printing aborted"), MessageMS);
1025 if ((m_findWhere & where) == 0)
1028 QString text = searchWhat;
1030 if (m_findOptions.testFlag(FindDialog::IgnoreAccelerators))
1034 if (m_findOptions.testFlag(FindDialog::UseRegExp))
1035 return m_findDialog->getRegExp().match(text).hasMatch();
1037 return text.indexOf(m_findText, 0, m_findOptions.testFlag(FindDialog::MatchCase)
1038 ? Qt::CaseSensitive : Qt::CaseInsensitive) >= 0;
1046 const QModelIndex &startIndex = m_messageView->currentIndex();
1047 QModelIndex index = (direction == FindNext
1048 ? nextMessage(startIndex)
1049 : prevMessage(startIndex));
1051 while (index.isValid()) {
1052 QModelIndex realIndex = m_activeSortedMessagesModel->mapToSource(index);
1053 MultiDataIndex dataIndex = m_activeMessageModel->dataIndex(realIndex, -1);
1054 bool hadMessage =
false;
1057 if (m_findStatusFilter != -1 && m_findStatusFilter != m
->type())
1060 if (m_findOptions.testFlag(FindDialog::SkipObsolete)
1067 if (searchItem(DataModel::SourceText, m->text()))
1069 if (searchItem(DataModel::SourceText, m->pluralText()))
1071 if (searchItem(DataModel::Comments, m->comment()))
1073 if (searchItem(DataModel::Comments, m->extraComment()))
1076 const auto translations = m->translations();
1077 for (
const QString &trans : translations)
1078 if (searchItem(DataModel::Translations, trans))
1080 if (searchItem(DataModel::Comments, m->translatorComment()))
1087 setCurrentMessage(realIndex, i);
1090 const QModelIndex &c1 =
1091 m_activeSortedGroupsModel
1093 m_activeSortedMessagesModel->mapToSource(startIndex))
1095 const QModelIndex &c2 =
1096 m_activeSortedGroupsModel->mapFromSource(realIndex).parent();
1097 const QModelIndex &m = m_activeSortedMessagesModel->mapFromSource(realIndex);
1099 if (c2.row() < c1.row() || (c1.row() == c2.row() && m.row() <= startIndex.row()))
1100 statusBar()->showMessage(tr(
"Search wrapped."),
MessageMS);
1102 m_findDialog->hide();
1110 if (index == startIndex)
1113 index = (direction == FindNext
1114 ? nextMessage(index)
1115 : prevMessage(index));
1119 QMessageBox::warning(m_findDialog, tr(
"Qt Linguist"),
1120 tr(
"Cannot find the string '%1'.").arg(m_findText));
1125 m_activeMessageModel->blockSignals(
true);
1126 m_batchTranslateDialog->setPhraseBooks(m_phraseBooks, m_currentIndex
.model());
1127 if (m_batchTranslateDialog->exec() != QDialog::Accepted)
1128 m_activeMessageModel->blockSignals(
false);
1134 m_latestCaseSensitivity = -1;
1135 QModelIndex idx = m_messageView->currentIndex();
1137 m_activeSortedMessagesModel->index(idx.row(), m_currentIndex
.model() + 1, idx.parent());
1138 m_messageView->setCurrentIndex(idx2);
1139 QString fn = QFileInfo(m_dataModel->srcFileName(m_currentIndex.model())).baseName();
1140 m_translateDialog->setWindowTitle(tr(
"Search And Translate in '%1' - Qt Linguist").arg(fn));
1141 m_translateDialog->exec();
1144void MainWindow::updateTranslateHit(
bool &hit)
1149 && m->compare(m_translateDialog->findText(),
false, m_translateDialog->caseSensitivity());
1154 QString findText = m_translateDialog->findText();
1155 QString replaceText = m_translateDialog->replaceText();
1156 bool markFinished = m_translateDialog->markFinished();
1157 Qt::CaseSensitivity caseSensitivity = m_translateDialog->caseSensitivity();
1159 int translatedCount = 0;
1161 if (mode == TranslateDialog::TranslateAll) {
1162 auto setTranslations = [
this, &translatedCount, &findText, &caseSensitivity, &replaceText,
1167 if (m && !m
->isObsolete() && m->compare(findText,
false, caseSensitivity)) {
1168 if (!translatedCount)
1169 m_activeMessageModel->blockSignals(
true);
1170 m_dataModel->setTranslation(it, replaceText);
1179 if (translatedCount) {
1181 QMessageBox::warning(m_translateDialog, tr(
"Translate - Qt Linguist"),
1182 tr(
"Translated %n entry(s)", 0, translatedCount));
1185 if (mode == TranslateDialog::Translate) {
1186 m_dataModel->setTranslation(m_currentIndex, replaceText);
1190 const QModelIndex firstIndex = firstMessage();
1191 if (findText != m_latestFindText || caseSensitivity != m_latestCaseSensitivity) {
1192 m_latestFindText = findText;
1193 m_latestCaseSensitivity = caseSensitivity;
1194 m_searchIndex = firstIndex;
1199 QModelIndex realIndex = m_activeSortedMessagesModel->mapToSource(m_searchIndex);
1200 MultiDataIndex dataIndex = m_activeMessageModel->dataIndex(realIndex, m_currentIndex.model());
1201 m_searchIndex = nextMessage(m_searchIndex);
1202 if (MessageItem *m = m_dataModel->messageItem(dataIndex)) {
1203 if (!m->isObsolete() && m->compare(findText,
false, caseSensitivity)) {
1204 setCurrentMessage(realIndex, m_currentIndex.model());
1210 if (m_searchIndex == firstIndex) {
1211 if (QMessageBox::question(
1212 m_translateDialog, tr(
"Translate - Qt Linguist"),
1213 tr(
"No more occurrences of '%1'. Start over?").arg(findText),
1214 QMessageBox::Yes | QMessageBox::No)
1215 != QMessageBox::Yes) {
1216 m_searchIndex = prevMessage(m_searchIndex);
1223 if (!translatedCount) {
1225 QMessageBox::warning(m_translateDialog, tr(
"Translate - Qt Linguist"),
1226 tr(
"Cannot find the string '%1'.").arg(findText));
1232 QString name = QFileDialog::getSaveFileName(
this, tr(
"Create New Phrase Book"),
1233 m_phraseBookDir, tr(
"Qt phrase books (*.qph)\nAll files (*)"));
1234 if (!name.isEmpty()) {
1236 if (!m_translationSettingsDialog)
1237 m_translationSettingsDialog =
new TranslationSettingsDialog(
this);
1239 if (!m_translationSettingsDialog->exec())
1241 m_phraseBookDir = QFileInfo(name).absolutePath();
1242 if (savePhraseBook(&name, pb)) {
1243 if (doOpenPhraseBook(name))
1244 statusBar()->showMessage(tr(
"Phrase book created."),
MessageMS);
1249bool MainWindow::isPhraseBookOpen(
const QString &name)
1251 for (
const PhraseBook *pb : std::as_const(m_phraseBooks)) {
1252 if (pb->fileName() == name)
1261 QString name = QFileDialog::getOpenFileName(
this, tr(
"Open Phrase Book"),
1262 m_phraseBookDir, tr(
"Qt phrase books (*.qph);;All files (*)"));
1264 if (!name.isEmpty()) {
1265 m_phraseBookDir = QFileInfo(name).absolutePath();
1266 if (!isPhraseBookOpen(name)) {
1267 if (
PhraseBook *phraseBook = doOpenPhraseBook(name)) {
1268 int n = phraseBook->phrases().size();
1269 statusBar()->showMessage(tr(
"%n phrase(s) loaded.", 0, n),
MessageMS);
1275void MainWindow::closePhraseBook(QAction *action)
1277 PhraseBook *pb = m_phraseBookMenu[PhraseCloseMenu].value(action);
1278 if (!maybeSavePhraseBook(pb))
1281 m_phraseBookMenu[PhraseCloseMenu].remove(action);
1282 m_ui.menuClosePhraseBook->removeAction(action);
1284 QAction *act = m_phraseBookMenu[PhraseEditMenu].key(pb);
1285 m_phraseBookMenu[PhraseEditMenu].remove(act);
1286 m_ui.menuEditPhraseBook->removeAction(act);
1288 act = m_phraseBookMenu[PhrasePrintMenu].key(pb);
1289 m_ui.menuPrintPhraseBook->removeAction(act);
1291 m_phraseBooks.removeOne(pb);
1294 updatePhraseDicts();
1296 updatePhraseBookActions();
1299void MainWindow::editPhraseBook(QAction *action)
1301 PhraseBook *pb = m_phraseBookMenu[PhraseEditMenu].value(action);
1305 updatePhraseDicts();
1308#if QT_CONFIG(printsupport)
1310void MainWindow::printPhraseBook(QAction *action)
1312 PhraseBook *phraseBook = m_phraseBookMenu[PhrasePrintMenu].value(action);
1316 QPrintDialog dlg(printer(),
this);
1318 printer()->setDocName(phraseBook->fileName());
1319 statusBar()->showMessage(tr(
"Printing..."));
1320 PrintOut pout(printer());
1321 pout.setRule(PrintOut::ThinRule);
1322 const auto phrases = phraseBook->phrases();
1323 for (
const Phrase *p : phrases) {
1324 pout.setGuide(p->source());
1325 pout.addBox(29, p->source());
1327 pout.addBox(29, p->target());
1329 pout.addBox(34, p->definition(), PrintOut::Emphasis);
1331 if (pout.pageNum() != pageNum) {
1332 pageNum = pout.pageNum();
1333 statusBar()->showMessage(tr(
"Printing... (page %1)")
1336 pout.setRule(PrintOut::NoRule);
1337 pout.flushLine(
true);
1339 pout.flushLine(
true);
1340 statusBar()->showMessage(tr(
"Printing completed"), MessageMS);
1342 statusBar()->showMessage(tr(
"Printing aborted"), MessageMS);
1350 QStringList phraseBookList;
1351 QHash<QString, PhraseBook *> phraseBookHash;
1352 for (PhraseBook *pb : std::as_const(m_phraseBooks)) {
1353 if (pb->language() != QLocale::C && m_dataModel->language(m_currentIndex.model()) != QLocale::C) {
1354 if (pb->language() != m_dataModel->language(m_currentIndex.model()))
1356 if (pb->territory() == m_dataModel->model(m_currentIndex.model())->territory())
1357 phraseBookList.prepend(pb->friendlyPhraseBookName());
1359 phraseBookList.append(pb->friendlyPhraseBookName());
1361 phraseBookList.append(pb->friendlyPhraseBookName());
1363 phraseBookHash.insert(pb->friendlyPhraseBookName(), pb);
1365 if (phraseBookList.isEmpty()) {
1366 QMessageBox::warning(
this, tr(
"Add to phrase book"),
1367 tr(
"No appropriate phrasebook found."));
1371 QString selectedPhraseBook;
1372 if (phraseBookList.size() == 1) {
1373 selectedPhraseBook = phraseBookList.at(0);
1374 if (QMessageBox::information(
this, tr(
"Add to phrase book"),
1375 tr(
"Adding entry to phrasebook %1").arg(selectedPhraseBook),
1376 QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok)
1380 bool okPressed =
false;
1381 selectedPhraseBook = QInputDialog::getItem(
this, tr(
"Add to phrase book"),
1382 tr(
"Select phrase book to add to"),
1383 phraseBookList, 0,
false, &okPressed);
1389 Phrase *phrase =
new Phrase(currentMessage->text(), currentMessage->translation(),
1390 QString(),
nullptr);
1392 phraseBookHash.value(selectedPhraseBook)->append(phrase);
1397 m_contextView->sortByColumn(-1, Qt::AscendingOrder);
1398 m_labelView->sortByColumn(-1, Qt::AscendingOrder);
1399 m_messageView->sortByColumn(-1, Qt::AscendingOrder);
1404 QString errorMessage;
1405 const QString page = m_helpClient->documentUrl(u"linguist"_s) +
"/qtlinguist-index.html"_L1;
1406 if (!m_helpClient->showPage(page, &errorMessage))
1407 qWarning(
"%s", qPrintable(errorMessage));
1412 return tr(
"Qt Linguist is a tool for adding translations to Qt applications.");
1417 QMessageBox box(
this);
1418 box.setTextFormat(Qt::RichText);
1419 QString version = tr(
"Version %1");
1420 version = version.arg(QLatin1String(QT_VERSION_STR));
1422 box.setText(QStringLiteral(
"<center><img src=\":/images/icons/linguist-128-32.png\"/></img><p>%1</p></center>"
1424 "<p>Copyright (C) The Qt Company Ltd.</p>").arg(version, description()));
1426 box.setWindowTitle(QApplication::translate(
"AboutDialog",
"Qt Linguist"));
1427 box.setIcon(QMessageBox::NoIcon);
1433 QMessageBox::aboutQt(
this, tr(
"Qt Linguist"));
1438 bool enabled = !m_phraseBooks.isEmpty();
1439 m_ui.menuClosePhraseBook->setEnabled(enabled);
1440 m_ui.menuEditPhraseBook->setEnabled(enabled);
1441#if QT_CONFIG(printsupport)
1442 m_ui.menuPrintPhraseBook->setEnabled(enabled);
1448 if (maybeSaveAll() && maybeSavePhraseBooks())
1459 switch (QMessageBox::information(
this, tr(
"Qt Linguist"),
1460 tr(
"Do you want to save the modified files?"),
1461 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
1463 case QMessageBox::Cancel:
1465 case QMessageBox::Yes:
1479 switch (QMessageBox::information(
this, tr(
"Qt Linguist"),
1480 tr(
"Do you want to save '%1'?").arg(m_dataModel->srcFileName(model,
true)),
1481 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
1483 case QMessageBox::Cancel:
1485 case QMessageBox::Yes:
1486 saveInternal(model);
1497 bool enable =
false;
1498 bool enableRw =
false;
1506 m_ui.actionSaveAll->setEnabled(enableRw);
1507 m_ui.actionReleaseAll->setEnabled(enableRw);
1508 m_ui.actionCloseAll->setEnabled(enable);
1509#if QT_CONFIG(printsupport)
1510 m_ui.actionPrint->setEnabled(enable);
1512 m_ui.actionAccelerators->setEnabled(enable);
1513 m_ui.actionSurroundingWhitespace->setEnabled(enable);
1514 m_ui.actionEndingPunctuation->setEnabled(enable);
1515 m_ui.actionPhraseMatches->setEnabled(enable);
1516 m_ui.actionPlaceMarkerMatches->setEnabled(enable);
1517 m_ui.actionResetSorting->setEnabled(enable);
1521 m_fileActiveModel = m_editActiveModel = -2;
1524 cap = tr(
"Qt Linguist[*]");
1526 cap = tr(
"%1[*] - Qt Linguist").arg(m_dataModel->condensedSrcFileNames(
true));
1527 setWindowTitle(cap);
1530void MainWindow::selectedContextChanged(
const QModelIndex &sortedIndex,
const QModelIndex &oldIndex)
1532 if (sortedIndex.isValid()) {
1533 if (m_settingCurrentMessage)
1536 if (!m_activeTranslationType || *m_activeTranslationType == IDBASED)
1537 contextAndLabelTabChanged();
1538 QModelIndex sourceIndex = m_sortedContextsModel->mapToSource(sortedIndex);
1539 if (m_activeMessageModel->parent(currentMessageIndex()).row() == sourceIndex.row())
1541 QModelIndex contextIndex = setMessageViewRoot(sourceIndex);
1542 const QModelIndex &firstChild =
1543 m_activeSortedMessagesModel->index(0, sourceIndex.column(), contextIndex);
1544 m_messageView->setCurrentIndex(firstChild);
1545 }
else if (oldIndex.isValid()) {
1546 m_contextView->setCurrentIndex(oldIndex);
1550void MainWindow::selectedLabelChanged(
const QModelIndex &sortedIndex,
const QModelIndex &oldIndex)
1552 if (sortedIndex.isValid()) {
1553 if (m_settingCurrentMessage)
1556 if (!m_activeTranslationType || *m_activeTranslationType != IDBASED)
1557 contextAndLabelTabChanged();
1558 QModelIndex sourceIndex = m_sortedLabelsModel->mapToSource(sortedIndex);
1559 if (m_activeMessageModel->parent(currentMessageIndex()).row() == sourceIndex.row())
1561 QModelIndex labelIndex = setMessageViewRoot(sourceIndex);
1562 const QModelIndex &firstChild =
1563 m_activeSortedMessagesModel->index(0, sourceIndex.column(), labelIndex);
1564 m_messageView->setCurrentIndex(firstChild);
1565 }
else if (oldIndex.isValid()) {
1566 m_labelView->setCurrentIndex(oldIndex);
1571
1572
1573void MainWindow::selectedMessageChanged(
const QModelIndex &sortedIndex,
const QModelIndex &oldIndex)
1576 if (!sortedIndex.isValid() && oldIndex.isValid()) {
1577 m_messageView->setCurrentIndex(oldIndex);
1583 QModelIndex index = m_activeSortedMessagesModel->mapToSource(sortedIndex);
1584 if (index.isValid()) {
1585 model = (index.column() && (index.column() - 1 < m_dataModel
->modelCount()))
1586 ? index.column() - 1
1588 m_currentIndex = m_activeMessageModel->dataIndex(index, model);
1592 m_phraseView->setSourceText(m_currentIndex
.model(), m->text());
1594 m_phraseView->setSourceText(-1, QString());
1602 m_phraseView->setSourceText(-1, QString());
1604 m_errorsView->setEnabled(m != 0);
1605 updateDanger(m_currentIndex,
true);
1609 m_phraseView->setSourceText(-1, QString());
1611 updateSourceView(model, m);
1613 updatePhraseBookActions();
1614 m_ui.actionSelectAll->setEnabled(index.isValid());
1620 updateDanger(index,
true);
1623 if (hasUiFormPreview(index.model(), m->fileName()))
1626 else if (hasQmlFormPreview(index.model(), m->fileName()))
1627 if (!m_qmlFormPreviewView->setSourceContext(index.model(), m))
1628 m_ui.actionQmlPreview->setChecked(
false);
1634void MainWindow::updateTranslation(
const QStringList &translations)
1639 if (translations == m->translations())
1642 m->setTranslations(translations);
1643 if (hasUiFormPreview(m_currentIndex.model(), m->fileName()))
1646 else if (hasQmlFormPreview(m_currentIndex.model(), m->fileName()))
1647 if (!m_qmlFormPreviewView->setSourceContext(m_currentIndex.model(), m))
1648 m_ui.actionQmlPreview->setChecked(
false);
1650 updateDanger(m_currentIndex,
true);
1658void MainWindow::updateTranslatorComment(
const QString &comment)
1663 if (comment == m->translatorComment())
1666 m->setTranslatorComment(comment);
1673 m_activeMessageModel->blockSignals(
false);
1674 m_contextView->update();
1675 m_labelView->update();
1676 m_messageView->update();
1696void MainWindow::toggleFinished(
const QModelIndex &index)
1698 if (!index.isValid() || index.column() - 1 >= m_dataModel
->modelCount()
1702 QModelIndex item = m_activeSortedMessagesModel->mapToSource(index);
1703 MultiDataIndex dataIndex = m_activeMessageModel->dataIndex(item);
1715 if (!m_machineTranslationDialog)
1716 m_machineTranslationDialog =
new MachineTranslationDialog(
this);
1718 m_machineTranslationDialog->open();
1722
1723
1724
1725
1726QModelIndex
MainWindow::nextGroup(
const QModelIndex &index)
const
1728 QModelIndex sortedGroupIndex = m_activeSortedGroupsModel->mapFromSource(
1729 m_activeSortedMessagesModel->mapToSource(index));
1731 int nextRow = sortedGroupIndex.row() + 1;
1732 if (nextRow >= m_activeSortedGroupsModel->rowCount()) {
1733 const QSortFilterProxyModel *inactiveModel =
1734 m_activeSortedGroupsModel == m_sortedLabelsModel ? m_sortedContextsModel
1735 : m_sortedLabelsModel;
1736 if (inactiveModel->rowCount())
1737 m_contextAndLabelView->setCurrentIndex(1 - m_contextAndLabelView->currentIndex());
1740 sortedGroupIndex = m_activeSortedGroupsModel->index(nextRow, index.column());
1742 return m_activeSortedMessagesModel->mapFromSource(
1743 m_activeSortedGroupsModel->mapToSource(sortedGroupIndex));
1747
1748
1749QModelIndex
MainWindow::prevGroup(
const QModelIndex &index)
const
1751 QModelIndex sortedGroupIndex = m_activeSortedGroupsModel->mapFromSource(
1752 m_activeSortedMessagesModel->mapToSource(index));
1754 int prevRow = sortedGroupIndex.row() - 1;
1756 const QSortFilterProxyModel *inactiveModel =
1757 m_activeSortedGroupsModel == m_sortedLabelsModel ? m_sortedContextsModel
1758 : m_sortedLabelsModel;
1759 if (inactiveModel->rowCount())
1760 m_contextAndLabelView->setCurrentIndex(1 - m_contextAndLabelView->currentIndex());
1761 prevRow = m_activeSortedGroupsModel->rowCount() - 1;
1763 sortedGroupIndex = m_activeSortedGroupsModel->index(prevRow, index.column());
1765 return m_activeSortedMessagesModel->mapFromSource(
1766 m_activeSortedGroupsModel->mapToSource(sortedGroupIndex));
1771 QModelIndex id = m_activeSortedMessagesModel->index(0, 0);
1772 QModelIndex firstId;
1773 if (id.isValid() && m_activeSortedMessagesModel->hasChildren(id))
1774 firstId = m_activeSortedMessagesModel->index(0, 0, id);
1775 else if (id.isValid())
1780QModelIndex
MainWindow::nextMessage(
const QModelIndex ¤tIndex,
bool checkUnfinished)
const
1783 currentIndex.isValid() ? currentIndex : m_activeSortedMessagesModel->index(0, 0);
1786 QModelIndex par = idx.parent();
1787 if (par.isValid()) {
1788 row = idx.row() + 1;
1793 if (row >= m_activeSortedMessagesModel->rowCount(par)) {
1794 par = nextGroup(par);
1797 idx = m_activeSortedMessagesModel->index(row, idx.column(), par);
1799 if (!checkUnfinished)
1802 QModelIndex item = m_activeSortedMessagesModel->mapToSource(idx);
1803 MultiDataIndex index = m_activeMessageModel->dataIndex(item, -1);
1806 }
while (idx != currentIndex);
1807 return QModelIndex();
1810QModelIndex
MainWindow::prevMessage(
const QModelIndex ¤tIndex,
bool checkUnfinished)
const
1813 currentIndex.isValid() ? currentIndex : m_activeSortedMessagesModel->index(0, 0);
1815 int row = idx.row() - 1;
1816 QModelIndex par = idx.parent();
1817 if (!par.isValid()) {
1823 par = prevGroup(par);
1824 row = m_activeSortedMessagesModel->rowCount(par) - 1;
1826 idx = m_activeSortedMessagesModel->index(row, idx.column(), par);
1828 if (!checkUnfinished)
1831 QModelIndex item = m_activeSortedMessagesModel->mapToSource(idx);
1832 MultiDataIndex index = m_activeMessageModel->dataIndex(item, -1);
1835 }
while (idx != currentIndex);
1836 return QModelIndex();
1841 if (m_ui.actionNextUnfinished->isEnabled()) {
1842 if (!doNext(
true)) {
1845 statusBar()->showMessage(tr(
"No untranslated translation units left."),
MessageMS);
1853 if (m_ui.actionNextUnfinished->isEnabled()) {
1854 if (!doPrev(
true)) {
1857 statusBar()->showMessage(tr(
"No untranslated translation units left."),
MessageMS);
1875 QModelIndex index = prevMessage(m_messageView->currentIndex(), checkUnfinished);
1876 if (index.isValid())
1877 setCurrentMessage(m_activeSortedMessagesModel->mapToSource(index));
1878 if (checkUnfinished)
1882 return index.isValid();
1887 QModelIndex index = nextMessage(m_messageView->currentIndex(), checkUnfinished);
1888 if (index.isValid())
1889 setCurrentMessage(m_activeSortedMessagesModel->mapToSource(index));
1890 if (checkUnfinished)
1894 return index.isValid();
1898 FindDialog::FindOptions options,
int statusFilter)
1903 m_findWhere = where;
1904 m_findOptions = options;
1905 m_findStatusFilter = statusFilter;
1906 if (options.testFlag(FindDialog::UseRegExp)) {
1907 m_findDialog->getRegExp().setPatternOptions(options.testFlag(FindDialog::MatchCase)
1908 ? QRegularExpression::NoPatternOption
1909 : QRegularExpression::CaseInsensitiveOption);
1911 m_ui.actionFindNext->setEnabled(
true);
1912 m_ui.actionFindPrev->setEnabled(
true);
1919 updateDanger(it,
false);
1921 updateDanger(it,
false);
1924 updateDanger(m_currentIndex,
true);
1929 const QString prefix = isDarkMode() ?
":/images/darkicons/"_L1:
":/images/lighticons/"_L1;
1930 auto getIcon = [&prefix](
const QString &name) {
1932 icon.addPixmap(QPixmap(prefix + name + QStringLiteral(
".png")), QIcon::Normal);
1933 icon.addPixmap(QPixmap(prefix + name + QStringLiteral(
"-disabled.png")), QIcon::Disabled);
1937 QIcon openIcon = getIcon(
"open-new"_L1);
1938 m_ui.actionOpen->setIcon(openIcon);
1939 m_ui.actionOpenAux->setIcon(openIcon);
1940 QIcon saveIcon = getIcon(
"save-fl-disk"_L1);
1941 m_ui.actionSave->setIcon(saveIcon);
1942 m_ui.actionSaveAll->setIcon(saveIcon);
1943 m_ui.actionPrint->setIcon(getIcon(
"print"_L1));
1944 m_ui.actionRedo->setIcon(getIcon(
"redo-arrow-right"_L1));
1945 m_ui.actionUndo->setIcon(getIcon(
"undo-arrow-left"_L1));
1946 m_ui.actionCut->setIcon(getIcon(
"cut"_L1));
1947 m_ui.actionCopy->setIcon(getIcon(
"copy-general"_L1));
1948 m_ui.actionPaste->setIcon(getIcon(
"paste-general"_L1));
1949 m_ui.actionFind->setIcon(getIcon(
"search-magnifier"_L1));
1951 m_ui.actionAccelerators->setIcon(getIcon(
"/check-ampersands"_L1));
1952 m_ui.actionOpenPhraseBook->setIcon(getIcon(
"library"_L1));
1953 m_ui.actionDone->setIcon(getIcon(
"mark-current-translation-done"_L1));
1954 m_ui.actionDoneAndNext->setIcon(getIcon(
"mark-current-translation-done-move-to-next"_L1));
1955 m_ui.actionNext->setIcon(getIcon(
"next-translation-item"_L1));
1956 m_ui.actionNextUnfinished->setIcon(getIcon(
"next-unfinished-translation-item"_L1));
1957 m_ui.actionPhraseMatches->setIcon(getIcon(
"check-phrase-suggestions"_L1));
1958 m_ui.actionSurroundingWhitespace->setIcon(getIcon(
"check-white-spaces"_L1));
1959 m_ui.actionEndingPunctuation->setIcon(getIcon(
"check-ending-pontuation"_L1));
1960 m_ui.actionPrev->setIcon(getIcon(
"previous-translation-item"_L1));
1961 m_ui.actionPrevUnfinished->setIcon(getIcon(
"previous-unfinished-translation-item"_L1));
1962 m_ui.actionPlaceMarkerMatches->setIcon(getIcon(
"check-place-markers"_L1));
1963 m_ui.actionWhatsThis->setIcon(getIcon(
"hit-help-chosen-option"_L1));
1968 m_ui.menuRecentlyOpenedFiles->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::DocumentOpenRecent));
1969 m_ui.actionCloseAll->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::WindowClose));
1970 m_ui.actionExit->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ApplicationExit));
1971 m_ui.actionSelectAll->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::EditSelectAll));
1975 connect(m_ui.menuFile, &QMenu::aboutToShow,
this, &
MainWindow::fileAboutToShow);
1976 connect(m_ui.actionOpen, &QAction::triggered,
this, &
MainWindow::open);
1977 connect(m_ui.actionOpenAux, &QAction::triggered,
this, &
MainWindow::openAux);
1978 connect(m_ui.actionSave, &QAction::triggered,
this, &
MainWindow::save);
1980 connect(m_ui.actionSaveAll, &QAction::triggered,
this, &
MainWindow::saveAll);
1981 connect(m_ui.actionSaveAs, &QAction::triggered,
this, &
MainWindow::saveAs);
1983 m_ui.actionSaveAs->setVisible(
false);
1984 m_ui.actionSaveAll->setVisible(
false);
1986 connect(m_ui.actionReleaseAll, &QAction::triggered,
this, &
MainWindow::releaseAll);
1987 connect(m_ui.actionRelease, &QAction::triggered,
this, &
MainWindow::release);
1988 connect(m_ui.actionReleaseAs, &QAction::triggered,
this, &
MainWindow::releaseAs);
1989#if QT_CONFIG(printsupport)
1990 connect(m_ui.actionPrint, &QAction::triggered,
this, &MainWindow::print);
1992 m_ui.actionPrint->setEnabled(
false);
1994 connect(m_ui.actionClose, &QAction::triggered,
this, &
MainWindow::closeFile);
1995 connect(m_ui.actionCloseAll, &QAction::triggered,
this, &
MainWindow::closeAll);
1996 connect(m_ui.actionExit, &QAction::triggered,
this, &MainWindow::close);
1999 connect(m_ui.menuEdit, &QMenu::aboutToShow,
this, &
MainWindow::editAboutToShow);
2001 connect(m_ui.actionUndo, &QAction::triggered, m_messageEditor, &MessageEditor::undo);
2007#ifndef QT_NO_CLIPBOARD
2018 connect(m_ui.actionSelectAll, &QAction::triggered,
2020 connect(m_ui.actionFind, &QAction::triggered,
2021 m_findDialog, &FindDialog::find);
2022 connect(m_ui.actionFindNext, &QAction::triggered,
2023 this, [
this] {findAgain(
FindNext);});
2024 connect(m_ui.actionFindPrev, &QAction::triggered,
2025 this, [
this] {findAgain(
FindPrev);});
2026 connect(m_ui.actionSearchAndTranslate, &QAction::triggered,
2028 connect(m_ui.actionBatchTranslation, &QAction::triggered,
2029 this, &
MainWindow::showBatchTranslateDialog);
2030 connect(m_ui.actionTranslationFileSettings, &QAction::triggered,
2033 connect(m_batchTranslateDialog, &BatchTranslationDialog::finished,
2038 connect(m_ui.actionAuto_Translation, &QAction::triggered,
this,
2040 connect(m_ui.actionPrevUnfinished, &QAction::triggered,
this, &
MainWindow::prevUnfinished);
2041 connect(m_ui.actionNextUnfinished, &QAction::triggered,
this, &
MainWindow::nextUnfinished);
2042 connect(m_ui.actionNext, &QAction::triggered,
this, &
MainWindow::next);
2043 connect(m_ui.actionPrev, &QAction::triggered,
this, &
MainWindow::prev);
2044 connect(m_ui.actionDone, &QAction::triggered,
this, &MainWindow::done);
2045 connect(m_ui.actionDoneAndNext, &QAction::triggered,
this, &
MainWindow::doneAndNext);
2046 connect(m_ui.actionBeginFromSource, &QAction::triggered, m_messageEditor,
2050 connect(m_ui.actionNewPhraseBook, &QAction::triggered,
this, &
MainWindow::newPhraseBook);
2051 connect(m_ui.actionOpenPhraseBook, &QAction::triggered,
this, &
MainWindow::openPhraseBook);
2052 connect(m_ui.menuClosePhraseBook, &QMenu::triggered,
2054 connect(m_ui.menuEditPhraseBook, &QMenu::triggered,
2056#if QT_CONFIG(printsupport)
2057 connect(m_ui.menuPrintPhraseBook, &QMenu::triggered,
2058 this, &MainWindow::printPhraseBook);
2060 m_ui.menuPrintPhraseBook->setEnabled(
false);
2062 connect(m_ui.actionAddToPhraseBook, &QAction::triggered,
2066 connect(m_ui.actionAccelerators, &QAction::triggered,
this, &
MainWindow::revalidate);
2067 connect(m_ui.actionSurroundingWhitespace, &QAction::triggered,
this, &
MainWindow::revalidate);
2068 connect(m_ui.actionEndingPunctuation, &QAction::triggered,
this, &
MainWindow::revalidate);
2069 connect(m_ui.actionPhraseMatches, &QAction::triggered,
this, &
MainWindow::revalidate);
2070 connect(m_ui.actionPlaceMarkerMatches, &QAction::triggered,
this, &
MainWindow::revalidate);
2073 connect(m_ui.actionResetSorting, &QAction::triggered,
2075 connect(m_ui.actionDisplayGuesses, &QAction::triggered,
2076 m_phraseView, &PhraseView::toggleGuessing);
2077 connect(m_ui.actionStatistics, &QAction::triggered,
this, &
MainWindow::showStatistics);
2079 connect(m_ui.actionQmlPreview, &QAction::triggered,
this, &
MainWindow::toggleQmlPreview);
2081 m_ui.actionQmlPreview->setVisible(
false);
2083 connect(m_ui.actionFormPreview, &QAction::triggered,
this, &
MainWindow::toggleFormPreview);
2084 connect(m_ui.actionVisualizeWhitespace, &QAction::triggered,
2085 this, &
MainWindow::toggleVisualizeWhitespace);
2086 connect(m_ui.actionIncreaseZoom, &QAction::triggered,
2088 connect(m_ui.actionDecreaseZoom, &QAction::triggered,
2090 connect(m_ui.actionResetZoomToDefault, &QAction::triggered,
2092 connect(m_ui.actionShowMoreGuesses, &QAction::triggered,
2094 connect(m_ui.actionShowFewerGuesses, &QAction::triggered,
2097 m_ui.actionShowFewerGuesses, &QAction::setEnabled);
2098 connect(m_ui.actionResetGuessesToDefault, &QAction::triggered,
2100 m_ui.menuViewViews->addAction(m_contextAndLabelDock->toggleViewAction());
2101 m_ui.menuViewViews->addAction(m_messagesDock->toggleViewAction());
2102 m_ui.menuViewViews->addAction(m_phrasesDock->toggleViewAction());
2103 m_ui.menuViewViews->addAction(m_sourceAndFormDock->toggleViewAction());
2104 m_ui.menuViewViews->addAction(m_errorsDock->toggleViewAction());
2106#if defined(Q_OS_MACOS)
2108 QMenu *windowMenu =
new QMenu(tr(
"&Window"),
this);
2109 menuBar()->insertMenu(m_ui.menuHelp->menuAction(), windowMenu);
2110 windowMenu->addAction(tr(
"Minimize"), QKeySequence(tr(
"Ctrl+M")),
2111 this, &QWidget::showMinimized);
2115 connect(m_ui.actionManual, &QAction::triggered,
this, &
MainWindow::manual);
2116 connect(m_ui.actionAbout, &QAction::triggered,
this, &
MainWindow::about);
2117 connect(m_ui.actionAboutQt, &QAction::triggered,
this, &
MainWindow::aboutQt);
2118 connect(m_ui.actionWhatsThis, &QAction::triggered,
this, &
MainWindow::onWhatsThis);
2120 connect(m_ui.menuRecentlyOpenedFiles, &QMenu::triggered,
2123 m_ui.actionManual->setToolTip(tr(
"Displays the manual for %1.").arg(tr(
"Qt Linguist")));
2124 m_ui.actionAbout->setToolTip(tr(
"Displays information about %1.").arg(tr(
"Qt Linguist")));
2125 m_ui.actionDone->setShortcuts(
2126 { Qt::AltModifier | Qt::Key_Return, Qt::AltModifier | Qt::Key_Enter });
2127 m_ui.actionDoneAndNext->setShortcuts({
2128 Qt::ControlModifier | Qt::Key_Return,
2129 Qt::ControlModifier | Qt::Key_Enter,
2133 connect(m_ui.menuPhrases, &QMenu::aboutToShow,
this, &
MainWindow::setupPhrase);
2135 connect(m_ui.menuRecentlyOpenedFiles, &QMenu::aboutToShow,
2142 doUpdateLatestModel(model);
2146void MainWindow::updateLatestModel(
const QModelIndex &index)
2148 if (index.column() && (index.column() - 1 < m_dataModel
->modelCount()))
2149 doUpdateLatestModel(index.column() - 1);
2152void MainWindow::doUpdateLatestModel(
int model)
2156 bool enable =
false;
2157 bool enableRw =
false;
2166 m_phraseView->setSourceText(model, item->text());
2168 m_phraseView->setSourceText(-1, QString());
2170 m_phraseView->setSourceText(-1, QString());
2174 updateSourceView(model, item);
2175 m_ui.actionSave->setEnabled(enableRw);
2176 m_ui.actionSaveAs->setEnabled(enableRw);
2177 m_ui.actionRelease->setEnabled(enableRw);
2178 m_ui.actionReleaseAs->setEnabled(enableRw);
2179 m_ui.actionClose->setEnabled(enable);
2180 m_ui.actionTranslationFileSettings->setEnabled(enableRw);
2181 m_ui.actionSearchAndTranslate->setEnabled(enableRw);
2183 updatePhraseBookActions();
2191 const QString shownPath = item ? previewFilePath(model, item->fileName()) : QString();
2192 if (shownPath != m_uiPreviewOnceFile)
2193 m_uiPreviewOnceFile.clear();
2194 if (shownPath != m_qmlPreviewOnceFile)
2195 m_qmlPreviewOnceFile.clear();
2197 if (item && !item->fileName().isEmpty()) {
2198 if (hasUiFormPreview(model, item->fileName())) {
2199 m_sourceAndFormView->setCurrentWidget(m_uiFormPreviewView);
2202 }
else if (hasQmlFormPreview(model, item->fileName())
2203 && m_qmlFormPreviewView->setSourceContext(model, item)) {
2204 m_sourceAndFormView->setCurrentWidget(m_qmlFormPreviewView);
2210 if (item->fileName().endsWith(
".qml"_L1)) {
2211 m_ui.actionQmlPreview->setChecked(
false);
2212 m_qmlPreviewOnceFile.clear();
2215 m_sourceAndFormView->setCurrentWidget(m_sourceCodeContainer);
2216 updatePreviewBanner(item->fileName());
2217 QDir dir = QFileInfo(m_dataModel->srcFileName(model)).dir();
2218 QString fileName = QDir::cleanPath(dir.absoluteFilePath(item->fileName()));
2219 m_sourceCodeView->setSourceContext(fileName, item
->lineNumber());
2222 m_sourceAndFormView->setCurrentWidget(m_sourceCodeContainer);
2223 updatePreviewBanner(QString());
2224 m_sourceCodeView->setSourceContext(QString(), 0);
2231void MainWindow::updatePreviewBanner(
const QString &fileName)
2233 if (fileName.endsWith(
".ui"_L1) || fileName.endsWith(
".jui"_L1)) {
2234 m_previewBannerLabel->setText(tr(
"This is a Qt Widgets Designer form."));
2235 m_previewBanner->show();
2237 }
else if (fileName.endsWith(
".qml"_L1)) {
2238 m_previewBannerLabel->setText(tr(
"This is a QML file."));
2239 m_previewBanner->show();
2242 m_previewBanner->hide();
2255 const QString fileName = item->fileName();
2257 if (fileName.endsWith(
".qml"_L1)) {
2258 m_ui.actionQmlPreview->trigger();
2262 if (fileName.endsWith(
".ui"_L1) || fileName.endsWith(
".jui"_L1))
2263 m_ui.actionFormPreview->trigger();
2271 if (m_fileActiveModel != m_currentIndex
.model()) {
2276 QString fn = QFileInfo(m_dataModel->srcFileName(m_currentIndex.model())).baseName();
2278 m_ui.actionSave->setText(tr(
"&Save '%1'").arg(fn));
2279 m_ui.actionSaveAs->setText(tr(
"Save '%1' &As...").arg(fn));
2281 m_ui.actionSave->setText(tr(
"&Download '%1'").arg(fn));
2283 m_ui.actionRelease->setText(tr(
"Release '%1'").arg(fn));
2284 m_ui.actionReleaseAs->setText(tr(
"Release '%1' As...").arg(fn));
2285 m_ui.actionClose->setText(tr(
"&Close '%1'").arg(fn));
2288 m_ui.actionSave->setText(tr(
"&Save"));
2289 m_ui.actionSaveAs->setText(tr(
"Save &As..."));
2291 m_ui.actionSave->setText(tr(
"&Download"));
2293 m_ui.actionRelease->setText(tr(
"Release"));
2294 m_ui.actionReleaseAs->setText(tr(
"Release As..."));
2295 m_ui.actionClose->setText(tr(
"&Close"));
2299 m_ui.actionSaveAll->setText(tr(
"Save All"));
2301 m_ui.actionReleaseAll->setText(tr(
"&Release All"));
2302 m_ui.actionCloseAll->setText(tr(
"Close All"));
2306 m_ui.actionSaveAs->setText(tr(
"Save &As..."));
2307 m_ui.actionSaveAll->setText(tr(
"&Save"));
2309 m_ui.actionSave->setText(tr(
"&Download"));
2311 m_ui.actionReleaseAs->setText(tr(
"Release As..."));
2312 m_ui.actionReleaseAll->setText(tr(
"&Release"));
2313 m_ui.actionCloseAll->setText(tr(
"&Close"));
2317 m_ui.actionSave->setVisible(en);
2319 m_ui.actionRelease->setVisible(en);
2320 m_ui.actionClose->setVisible(en);
2321 m_fileActiveModel = m_currentIndex
.model();
2327 if (m_editActiveModel != m_currentIndex
.model()) {
2329 QString fn = QFileInfo(m_dataModel->srcFileName(m_currentIndex.model())).baseName();
2330 m_ui.actionTranslationFileSettings->setText(tr(
"Translation File &Settings for '%1'...").arg(fn));
2331 m_ui.actionBatchTranslation->setText(tr(
"&Batch Translation of '%1'...").arg(fn));
2332 m_ui.actionSearchAndTranslate->setText(tr(
"Search And &Translate in '%1'...").arg(fn));
2334 m_ui.actionTranslationFileSettings->setText(tr(
"Translation File &Settings..."));
2335 m_ui.actionBatchTranslation->setText(tr(
"&Batch Translation..."));
2336 m_ui.actionSearchAndTranslate->setText(tr(
"Search And &Translate..."));
2338 m_editActiveModel = m_currentIndex
.model();
2344 m_contextAndLabelDock->show();
2345 m_contextAndLabelDock->raise();
2350 m_messagesDock->show();
2351 m_messagesDock->raise();
2356 m_phrasesDock->show();
2357 m_phrasesDock->raise();
2362 m_sourceAndFormDock->show();
2363 m_sourceAndFormDock->raise();
2368 m_errorsDock->show();
2369 m_errorsDock->raise();
2374 QWhatsThis::enterWhatsThisMode();
2379 QToolBar *filet =
new QToolBar(
this);
2380 filet->setObjectName(
"FileToolbar");
2381 filet->setWindowTitle(tr(
"File"));
2382 this->addToolBar(filet);
2383 m_ui.menuToolbars->addAction(filet->toggleViewAction());
2385 QToolBar *editt =
new QToolBar(
this);
2386 editt->setVisible(
false);
2387 editt->setObjectName(
"EditToolbar");
2388 editt->setWindowTitle(tr(
"Edit"));
2389 this->addToolBar(editt);
2390 m_ui.menuToolbars->addAction(editt->toggleViewAction());
2392 QToolBar *translationst =
new QToolBar(
this);
2393 translationst->setObjectName(
"TranslationToolbar");
2394 translationst->setWindowTitle(tr(
"Translation"));
2395 this->addToolBar(translationst);
2396 m_ui.menuToolbars->addAction(translationst->toggleViewAction());
2398 QToolBar *validationt =
new QToolBar(
this);
2399 validationt->setObjectName(
"ValidationToolbar");
2400 validationt->setWindowTitle(tr(
"Validation"));
2401 this->addToolBar(validationt);
2402 m_ui.menuToolbars->addAction(validationt->toggleViewAction());
2404 QToolBar *helpt =
new QToolBar(
this);
2405 helpt->setVisible(
false);
2406 helpt->setObjectName(
"HelpToolbar");
2407 helpt->setWindowTitle(tr(
"Help"));
2408 this->addToolBar(helpt);
2409 m_ui.menuToolbars->addAction(helpt->toggleViewAction());
2412 filet->addAction(m_ui.actionOpen);
2413 filet->addAction(m_ui.actionSaveAll);
2414 filet->addAction(m_ui.actionPrint);
2415 filet->addSeparator();
2416 filet->addAction(m_ui.actionOpenPhraseBook);
2418 editt->addAction(m_ui.actionUndo);
2419 editt->addAction(m_ui.actionRedo);
2420 editt->addSeparator();
2421 editt->addAction(m_ui.actionCut);
2422 editt->addAction(m_ui.actionCopy);
2423 editt->addAction(m_ui.actionPaste);
2424 editt->addSeparator();
2425 editt->addAction(m_ui.actionFind);
2427 translationst->addAction(m_ui.actionPrev);
2428 translationst->addAction(m_ui.actionNext);
2429 translationst->addAction(m_ui.actionPrevUnfinished);
2430 translationst->addAction(m_ui.actionNextUnfinished);
2431 translationst->addAction(m_ui.actionDone);
2432 translationst->addAction(m_ui.actionDoneAndNext);
2434 validationt->addAction(m_ui.actionAccelerators);
2435 validationt->addAction(m_ui.actionSurroundingWhitespace);
2436 validationt->addAction(m_ui.actionEndingPunctuation);
2437 validationt->addAction(m_ui.actionPhraseMatches);
2438 validationt->addAction(m_ui.actionPlaceMarkerMatches);
2440 helpt->addAction(m_ui.actionWhatsThis);
2443QModelIndex
MainWindow::setMessageViewRoot(
const QModelIndex &index)
2445 const QModelIndex &sortedGroupIndex = m_activeSortedMessagesModel->mapFromSource(index);
2446 const QModelIndex &trueGroupIndex =
2447 m_activeSortedMessagesModel->index(sortedGroupIndex.row(), 0);
2448 if (m_messageView->rootIndex() != trueGroupIndex)
2449 m_messageView->setRootIndex(trueGroupIndex);
2450 return trueGroupIndex;
2454
2455
2456void MainWindow::setCurrentMessage(
const QModelIndex &index)
2458 const QModelIndex &groupIndex = m_activeMessageModel->parent(index);
2459 if (!groupIndex.isValid())
2462 const QModelIndex &trueIndex =
2463 m_activeMessageModel->index(groupIndex.row(), index.column(), QModelIndex());
2464 m_settingCurrentMessage =
true;
2465 QTreeView *view = *m_activeTranslationType == IDBASED ? m_labelView : m_contextView;
2466 view->setCurrentIndex(m_activeSortedGroupsModel->mapFromSource(trueIndex));
2467 m_settingCurrentMessage =
false;
2468 setMessageViewRoot(groupIndex);
2469 m_messageView->setCurrentIndex(m_activeSortedMessagesModel->mapFromSource(index));
2472void MainWindow::setCurrentMessage(
const QModelIndex &index,
int model)
2474 const QModelIndex &theIndex =
2475 m_activeMessageModel->index(index.row(), model + 1, index.parent());
2476 setCurrentMessage(theIndex);
2482 if (cand.context.isEmpty()) {
2483 int labelIndex = m_dataModel->findGroupIndex(cand.label,
IDBASED);
2486 setCurrentMessage(m_activeMessageModel->modelIndex(
2487 MultiDataIndex(IDBASED, modelIndex, labelIndex, messageIndex)));
2489 int contextIndex = m_dataModel->findGroupIndex(cand.context,
TEXTBASED);
2491 ->findMessage(cand.source, cand.disambiguation);
2492 setCurrentMessage(m_activeMessageModel->modelIndex(
2493 MultiDataIndex(TEXTBASED, modelIndex, contextIndex, messageIndex)));
2499 auto refreshMessageView = [
this](QTreeView *view) {
2500 m_messageView->reset();
2501 m_messageView->setModel(m_activeSortedMessagesModel);
2502 view->setCurrentIndex(m_activeSortedGroupsModel->index(0, 0));
2503 connect(m_messageView->selectionModel(), &QItemSelectionModel::currentRowChanged,
this,
2504 &MainWindow::selectedMessageChanged);
2505 connect(m_messageView->selectionModel(), &QItemSelectionModel::currentColumnChanged,
this,
2506 &MainWindow::updateLatestModel);
2507 m_messageView->update();
2508 if (m_activeMessageModel->rowCount())
2509 setCurrentMessage(m_activeMessageModel->modelIndex(
2510 MultiDataIndex(*m_activeTranslationType, 0, 0, 0)));
2511 selectedMessageChanged(m_messageView->currentIndex(), QModelIndex{});
2512 updateVisibleColumns();
2515 if (m_contextAndLabelView->currentWidget() == m_labelView
2516 && (!m_activeTranslationType || *m_activeTranslationType != IDBASED)) {
2517 m_activeTranslationType.emplace(IDBASED);
2518 m_activeSortedMessagesModel = m_sortedIdBasedMessagesModel;
2519 m_activeSortedGroupsModel = m_sortedLabelsModel;
2520 m_activeMessageModel = m_idBasedMessageModel;
2521 refreshMessageView(m_labelView);
2522 }
else if (m_contextAndLabelView->currentWidget() == m_contextView
2523 && (!m_activeTranslationType || *m_activeTranslationType != TEXTBASED)) {
2524 m_activeTranslationType.emplace(TEXTBASED);
2525 m_activeSortedMessagesModel = m_sortedTextBasedMessagesModel;
2526 m_activeSortedGroupsModel = m_sortedContextsModel;
2527 m_activeMessageModel = m_textBasedMessageModel;
2528 refreshMessageView(m_contextView);
2535 if (*m_activeTranslationType == IDBASED)
2537 for (
int i = 1; i < cols; i++)
2538 m_messageView->setColumnHidden(i,
false);
2539 for (
int i = cols; i < m_messageView->header()->count(); i++)
2540 m_messageView->setColumnHidden(i,
true);
2541 m_messageView->header()->setStretchLastSection(
true);
2544QModelIndex
MainWindow::currentMessageIndex()
const
2546 return m_activeSortedMessagesModel->mapToSource(m_messageView->currentIndex());
2553 if (!pb->load(name, &langGuessed)) {
2554 QMessageBox::warning(
this, tr(
"Qt Linguist"),
2555 tr(
"Cannot read from phrase book '%1'.").arg(name));
2560 if (!m_translationSettingsDialog)
2561 m_translationSettingsDialog =
new TranslationSettingsDialog(
this);
2563 m_translationSettingsDialog->exec();
2566 m_phraseBooks.append(pb);
2568 QAction *a = m_ui.menuClosePhraseBook->addAction(pb->friendlyPhraseBookName());
2569 m_phraseBookMenu[PhraseCloseMenu].insert(a, pb);
2570 a->setToolTip(tr(
"Close this phrase book."));
2572 a = m_ui.menuEditPhraseBook->addAction(pb->friendlyPhraseBookName());
2573 m_phraseBookMenu[PhraseEditMenu].insert(a, pb);
2574 a->setToolTip(tr(
"Enables you to add, modify, or delete"
2575 " entries in this phrase book."));
2577 a = m_ui.menuPrintPhraseBook->addAction(pb->friendlyPhraseBookName());
2578 m_phraseBookMenu[PhrasePrintMenu].insert(a, pb);
2579 a->setToolTip(tr(
"Print the entries in this phrase book."));
2582 updatePhraseDicts();
2583 updatePhraseBookActions();
2590 if (!name->contains(u'.'))
2593 if (!pb.save(*name)) {
2594 QMessageBox::warning(
this, tr(
"Qt Linguist"),
2595 tr(
"Cannot create phrase book '%1'.").arg(*name));
2604 switch (QMessageBox::information(
this, tr(
"Qt Linguist"),
2605 tr(
"Do you want to save phrase book '%1'?").arg(pb->friendlyPhraseBookName()),
2606 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
2608 case QMessageBox::Cancel:
2610 case QMessageBox::Yes:
2611 if (!pb->save(pb->fileName()))
2622 for (PhraseBook *phraseBook : std::as_const(m_phraseBooks))
2623 if (!maybeSavePhraseBook(phraseBook))
2633 m_progressLabel->setText(QString(
" "_L1));
2634 m_progressLabel->setToolTip(QString());
2636 m_progressLabel->setText(QStringLiteral(
" %1/%2 ").arg(numFinished).arg(numEditable));
2637 m_progressLabel->setToolTip(tr(
"%n unfinished message(s) left.", 0,
2638 numEditable - numFinished));
2640 bool enable = numFinished != numEditable;
2641 m_ui.actionPrevUnfinished->setEnabled(enable);
2642 m_ui.actionNextUnfinished->setEnabled(enable);
2643 m_ui.actionDone->setEnabled(enable);
2644 m_ui.actionDoneAndNext->setEnabled(enable);
2646 m_ui.actionPrev->setEnabled(m_dataModel->contextCount() > 0 || m_dataModel->labelCount() > 0);
2647 m_ui.actionNext->setEnabled(m_dataModel->contextCount() > 0 || m_dataModel->labelCount() > 0);
2652 bool phraseBookLoaded = (m_currentIndex.model() >= 0) && !m_phraseBooks.isEmpty();
2653 m_ui.actionBatchTranslation->setEnabled(m_dataModel->contextCount() > 0 && phraseBookLoaded
2654 && m_dataModel->isModelWritable(m_currentIndex.model()));
2655 m_ui.actionAddToPhraseBook->setEnabled(currentMessageIndex().isValid() && phraseBookLoaded);
2658void MainWindow::updatePhraseDictInternal(
int model)
2660 QHash<QString, QList<Phrase *> > &pd = m_phraseDict[model];
2663 for (PhraseBook *pb : std::as_const(m_phraseBooks)) {
2665 if (pb->language() != QLocale::C && m_dataModel->language(model) != QLocale::C) {
2666 if (pb->language() != m_dataModel->language(model))
2668 before = (pb->territory() == m_dataModel->model(model)->territory());
2672 const auto phrases = pb->phrases();
2673 for (Phrase *p : phrases) {
2674 QString f = friendlyString(p->source());
2676 f = f.split(u' ').first();
2677 if (!pd.contains(f)) {
2678 pd.insert(f, QList<Phrase *>());
2691 updatePhraseDictInternal(model);
2697 for (
int i = 0; i < m_phraseDict.size(); ++i)
2699 m_phraseDict[i].clear();
2701 updatePhraseDictInternal(i);
2713 Validator::Checks checks{ m_ui.actionAccelerators->isChecked(),
2714 m_ui.actionEndingPunctuation->isChecked(),
2715 m_ui.actionPlaceMarkerMatches->isChecked(),
2716 m_ui.actionSurroundingWhitespace->isChecked(),
2717 m_ui.actionPhraseMatches->isChecked() };
2727 bool danger =
false;
2729 if (source.isEmpty()) {
2730 source = m->pluralText();
2731 if (source.isEmpty())
2735 Validator validator = Validator::fromSource(
2736 source, checks, m_dataModel->sourceLanguage(mi), m_phraseDict[mi]);
2738 validator.validate(m->translations(), m
->message(), m_dataModel->language(mi),
2739 m_dataModel
->model(mi
)->countRefNeeds());
2740 danger = !errors.isEmpty();
2742 for (
const auto &[error, message] : errors)
2743 m_errorsView->addError(mi, error, message);
2751 statusBar()->showMessage(m_errorsView->firstError());
2758 restoreGeometry(config.value(settingPath(
"Geometry/WindowGeometry")).toByteArray());
2759 restoreState(config.value(settingPath(
"MainWindowState")).toByteArray());
2761 m_ui.actionAccelerators->setChecked(
2762 config.value(settingPath(
"Validators/Accelerator"),
true).toBool());
2763 m_ui.actionSurroundingWhitespace->setChecked(
2764 config.value(settingPath(
"Validators/SurroundingWhitespace"),
true).toBool());
2765 m_ui.actionEndingPunctuation->setChecked(
2766 config.value(settingPath(
"Validators/EndingPunctuation"),
true).toBool());
2767 m_ui.actionPhraseMatches->setChecked(
2768 config.value(settingPath(
"Validators/PhraseMatch"),
true).toBool());
2769 m_ui.actionPlaceMarkerMatches->setChecked(
2770 config.value(settingPath(
"Validators/PlaceMarkers"),
true).toBool());
2771 m_ui.actionLengthVariants->setChecked(
2772 config.value(settingPath(
"Options/LengthVariants"),
false).toBool());
2773 m_ui.actionVisualizeWhitespace->setChecked(
2774 config.value(settingPath(
"Options/VisualizeWhitespace"),
true).toBool());
2777 config.value(settingPath(
"Options/EditorFontsize"), font().pointSize()).toReal()
);
2781 m_recentFiles.readConfig();
2783 int size = config.beginReadArray(settingPath(
"OpenedPhraseBooks"));
2784 for (
int i = 0; i < size; ++i) {
2785 config.setArrayIndex(i);
2786 doOpenPhraseBook(config.value(
"FileName"_L1).toString());
2794 config.setValue(settingPath(
"Geometry/WindowGeometry"),
2796 config.setValue(settingPath(
"Validators/Accelerator"),
2797 m_ui.actionAccelerators->isChecked());
2798 config.setValue(settingPath(
"Validators/SurroundingWhitespace"),
2799 m_ui.actionSurroundingWhitespace->isChecked());
2800 config.setValue(settingPath(
"Validators/EndingPunctuation"),
2801 m_ui.actionEndingPunctuation->isChecked());
2802 config.setValue(settingPath(
"Validators/PhraseMatch"),
2803 m_ui.actionPhraseMatches->isChecked());
2804 config.setValue(settingPath(
"Validators/PlaceMarkers"),
2805 m_ui.actionPlaceMarkerMatches->isChecked());
2806 config.setValue(settingPath(
"Options/LengthVariants"),
2807 m_ui.actionLengthVariants->isChecked());
2808 config.setValue(settingPath(
"Options/VisualizeWhitespace"),
2809 m_ui.actionVisualizeWhitespace->isChecked());
2810 config.setValue(settingPath(
"MainWindowState"),
2812 m_recentFiles.writeConfig();
2814 config.setValue(settingPath(
"Options/EditorFontsize"), m_messageEditor
->fontSize());
2817 config.beginWriteArray(settingPath(
"OpenedPhraseBooks"),
2818 m_phraseBooks.size());
2819 for (
int i = 0; i < m_phraseBooks.size(); ++i) {
2820 config.setArrayIndex(i);
2821 config.setValue(
"FileName"_L1, m_phraseBooks.at(i)->fileName());
2828 m_ui.menuRecentlyOpenedFiles->clear();
2829 for (
const QStringList &strList : m_recentFiles.filesLists())
2830 if (strList.size() == 1) {
2831 const QString &str = strList.first();
2832 m_ui.menuRecentlyOpenedFiles->addAction(
2833 DataModel::prettifyFileName(str))->setData(str);
2835 QMenu *menu = m_ui.menuRecentlyOpenedFiles->addMenu(
2836 MultiDataModel::condenseFileNames(
2837 MultiDataModel::prettifyFileNames(strList)));
2838 menu->addAction(tr(
"All"))->setData(strList);
2839 for (
const QString &str : strList)
2840 menu->addAction(DataModel::prettifyFileName(str))->setData(str);
2844void MainWindow::recentFileActivated(QAction *action)
2846 openFiles(action->data().toStringList());
2851 if (!m_statistics) {
2852 m_statistics =
new Statistics(
this);
2855 m_statistics->show();
2864 updateSourceView(m_currentIndex
.model(), item);
2870QString
MainWindow::previewFilePath(
int model,
const QString &fileName)
const
2872 if (model < 0 || fileName.isEmpty())
2874 const QDir dir = QFileInfo(m_dataModel->srcFileName(model)).dir();
2875 return QDir::cleanPath(dir.absoluteFilePath(fileName));
2878bool MainWindow::hasUiFormPreview(
int model,
const QString &fileName)
const
2880 if (!fileName.endsWith(
".ui"_L1) && !fileName.endsWith(
".jui"_L1))
2882 if (m_ui.actionFormPreview->isChecked())
2884 const QString path = previewFilePath(model, fileName);
2885 return !path.isEmpty() && path == m_uiPreviewOnceFile;
2889bool MainWindow::hasQmlFormPreview(
int model,
const QString &fileName)
const
2891 if (!fileName.endsWith(
".qml"_L1))
2893 if (m_ui.actionQmlPreview->isChecked())
2895 const QString path = previewFilePath(model, fileName);
2896 return !path.isEmpty() && path == m_qmlPreviewOnceFile;
2904bool MainWindow::askPreviewActivation(
bool isQml)
2906 QAction *action = isQml ? m_ui.actionQmlPreview : m_ui.actionFormPreview;
2910 const QString fileName = item ? item->fileName() : QString();
2912 const bool canPreviewOnce = isQml
2913 ? fileName.endsWith(
".qml"_L1)
2914 : (fileName.endsWith(
".ui"_L1) || fileName.endsWith(
".jui"_L1));
2916 const QString text = isQml
2917 ? tr(
"The QML preview loads and renders QML files where the current message "
2918 "is used. This can execute code embedded in the QML, and may cause "
2919 "crashes or other unexpected behavior.\n\n"
2920 "Activate the preview only for sources you trust.\n\n"
2921 "Do you want to activate the preview?")
2922 : tr(
"The UI form preview loads and renders Qt Widgets Designer forms where "
2923 "the current message is used. This can execute code embedded in the UI "
2924 "file, and may cause 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?");
2928 QMessageBox box(QMessageBox::Warning, tr(
"Qt Linguist"), text, QMessageBox::NoButton,
this);
2929 QPushButton *dontPreview = box.addButton(tr(
"Do Not Preview"), QMessageBox::RejectRole);
2930 QPushButton *forSession = box.addButton(tr(
"Preview for Session"), QMessageBox::AcceptRole);
2932 canPreviewOnce ? box.addButton(tr(
"Preview Once"), QMessageBox::AcceptRole) :
nullptr;
2933 box.setDefaultButton(once ? once : dontPreview);
2936 if (box.clickedButton() == forSession)
2938 action->setChecked(
false);
2939 QString &onceFile = isQml ? m_qmlPreviewOnceFile : m_uiPreviewOnceFile;
2940 if (once && box.clickedButton() == once) {
2941 onceFile = previewFilePath(m_currentIndex.model(), fileName);
2951 if (m_ui.actionQmlPreview->isChecked()) {
2952 if (askPreviewActivation(
true))
2953 showSourceCodeDock();
2955 m_qmlPreviewOnceFile.clear();
2957 refreshFormPreview();
2963 if (m_ui.actionFormPreview->isChecked()) {
2964 if (askPreviewActivation(
false))
2965 showSourceCodeDock();
2967 m_uiPreviewOnceFile.clear();
2969 refreshFormPreview();
2987 if (!m_statistics || !m_statistics->isVisible() || m_currentIndex
.model() < 0)
2993void MainWindow::doShowTranslationSettings(
int model)
2995 if (!m_translationSettingsDialog)
2996 m_translationSettingsDialog =
new TranslationSettingsDialog(
this);
2998 m_translationSettingsDialog->exec();
3003 doShowTranslationSettings(m_currentIndex
.model());
3008 if (event->type() == QEvent::DragEnter) {
3009 QDragEnterEvent *e =
static_cast<QDragEnterEvent*>(event);
3010 if (e->mimeData()->hasFormat(
"text/uri-list"_L1)) {
3011 e->acceptProposedAction();
3014 }
else if (event->type() == QEvent::Drop) {
3015 QDropEvent *e =
static_cast<QDropEvent*>(event);
3016 if (!e->mimeData()->hasFormat(
"text/uri-list"_L1))
3019 const auto &qurls = e->mimeData()->urls();
3020 for (
const QUrl &url : qurls)
3021 if (!url.toLocalFile().isEmpty())
3022 urls << url.toLocalFile();
3023 if (!urls.isEmpty())
3025 e->acceptProposedAction();
3027 }
else if (event->type() == QEvent::KeyPress) {
3028 QKeyEvent *ke =
static_cast<QKeyEvent *>(event);
3029 if (ke->key() == Qt::Key_Escape) {
3030 if (object == m_messageEditor)
3031 m_messageView->setFocus();
3032 else if (object == m_messagesDock)
3033 m_contextAndLabelView->currentWidget()->setFocus();
3034 }
else if ((ke->key() == Qt::Key_Plus || ke->key() == Qt::Key_Equal)
3035 && (ke->modifiers() & Qt::ControlModifier)) {
3037 }
else if (ke->key() == Qt::Key_Minus
3038 && (ke->modifiers() & Qt::ControlModifier)) {
3041 }
else if (event->type() == QEvent::Wheel) {
3042 QWheelEvent *we =
static_cast<QWheelEvent *>(event);
3043 if (we->modifiers() & Qt::ControlModifier) {
3044 if (we->angleDelta().y() > 0)
3049 }
else if (event->type() == QEvent::ApplicationPaletteChange) {
3053 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)