5
6
7
15#include <helpclient.h>
33#include <QApplication>
37#include <QDesktopServices>
43#include <QInputDialog>
44#include <QItemDelegate>
47#include <QLibraryInfo>
52#include <QRegularExpression>
56#include <QSortFilterProxyModel>
57#include <QStackedWidget>
64#if QT_CONFIG(printsupport)
65#include <QPrintDialog>
69using namespace Qt::Literals::StringLiterals;
72static bool hasUiFormPreview(
const QString &fileName)
74 return fileName.endsWith(
".ui"_L1) || fileName.endsWith(
".jui"_L1);
78static bool hasQmlFormPreview(
const QString &fileName,
bool qmlPreviewChecked)
80 return fileName.endsWith(QLatin1String(
".qml")) && qmlPreviewChecked;
84static const int MessageMS = 2500;
90class GroupItemDelegate :
public QItemDelegate
93 GroupItemDelegate(QObject *parent, MultiDataModel *model)
94 : QItemDelegate(parent), m_dataModel(model)
98 void paint(QPainter *painter,
const QStyleOptionViewItem &option,
99 const QModelIndex &index)
const override
101 const QAbstractItemModel *model = index.model();
104 if (!model->parent(index).isValid()) {
105 if (index.column() - 1 == m_dataModel->modelCount()) {
106 QStyleOptionViewItem opt = option;
107 opt.font.setBold(
true);
108 QItemDelegate::paint(painter, opt, index);
112 QItemDelegate::paint(painter, option, index);
116 MultiDataModel *m_dataModel;
121 static const QVariant v = createMarkIcon(TranslationMarks::ObsoleteMark);
136 if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
142 return MainWindow::tr(
"Source text");
146 case 0:
return QString();
148 return MainWindow::tr(
"ID");
150 return MainWindow::tr(
"Source text");
154 if (role == Qt::DecorationRole && orientation == Qt::Horizontal && section - 1 < m_dataModel->modelCount())
175 if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
177 case 0:
return QString();
179 return m_translationType == TEXTBASED ? MainWindow::tr(
"Context")
180 : MainWindow::tr(
"Label");
182 return MainWindow::tr(
"Items");
185 if (role == Qt::DecorationRole && orientation == Qt::Horizontal && section - 1 < m_dataModel->modelCount())
210 if (event->type() == QEvent::FocusIn)
218#if QT_CONFIG(printsupport)
222 m_translationSettingsDialog(0),
223 m_settingCurrentMessage(
false),
224 m_fileActiveModel(-1),
225 m_editActiveModel(-1),
229 setUnifiedTitleAndToolBarOnMac(
true);
232#if !defined(Q_OS_MACOS) && !defined(Q_OS_WIN)
233 setWindowIcon(QPixmap(
":/images/appicon.png"_L1));
236 m_dataModel =
new MultiDataModel(
this);
237 m_idBasedMessageModel =
new MessageModel(IDBASED,
this, m_dataModel);
238 m_textBasedMessageModel =
new MessageModel(TEXTBASED,
this, m_dataModel);
241 m_contextAndLabelDock =
new QDockWidget(
this);
242 m_contextAndLabelDock->setObjectName(
"ContextLabelDockWidget");
243 m_contextAndLabelDock->setAllowedAreas(Qt::AllDockWidgetAreas);
244 m_contextAndLabelDock->setWindowTitle(tr(
"Context/Label"));
245 m_contextAndLabelDock->setAcceptDrops(
true);
246 m_contextAndLabelDock->installEventFilter(
this);
248 m_contextAndLabelView =
new QTabWidget(
this);
250 QWidget* dockContent =
new QWidget(
this);
251 QBoxLayout* layout =
new QBoxLayout(QBoxLayout::LeftToRight, dockContent);
252 layout->addWidget(m_contextAndLabelView);
254 m_contextAndLabelDock->setWidget(dockContent);
257 m_sortedContextsModel =
new SortedGroupsModel(
this, m_dataModel, TEXTBASED);
258 m_sortedContextsModel->setSortRole(MessageModel::SortRole);
259 m_sortedContextsModel->setSortCaseSensitivity(Qt::CaseInsensitive);
260 m_sortedContextsModel->setSourceModel(m_textBasedMessageModel);
262 m_contextView =
new QTreeView(
this);
263 m_contextView->setRootIsDecorated(
false);
264 m_contextView->setItemsExpandable(
false);
265 m_contextView->setUniformRowHeights(
true);
266 m_contextView->setAlternatingRowColors(
true);
267 m_contextView->setAllColumnsShowFocus(
true);
268 m_contextView->setItemDelegate(
new GroupItemDelegate(
this, m_dataModel));
269 m_contextView->setSortingEnabled(
true);
270 m_contextView->setWhatsThis(tr(
"This panel lists the source contexts."));
271 m_contextView->setModel(m_sortedContextsModel);
272 m_contextView->header()->setSectionsMovable(
false);
273 m_contextView->setColumnHidden(0,
true);
274 m_contextView->header()->setStretchLastSection(
false);
276 m_contextAndLabelView->addTab(m_contextView,
"Text Based"_L1);
279 m_sortedLabelsModel =
new SortedGroupsModel(
this, m_dataModel, IDBASED);
280 m_sortedLabelsModel->setSortRole(MessageModel::SortRole);
281 m_sortedLabelsModel->setSortCaseSensitivity(Qt::CaseInsensitive);
282 m_sortedLabelsModel->setSourceModel(m_idBasedMessageModel);
284 m_labelView =
new QTreeView(
this);
285 m_labelView->setRootIsDecorated(
false);
286 m_labelView->setItemsExpandable(
false);
287 m_labelView->setUniformRowHeights(
true);
288 m_labelView->setAlternatingRowColors(
true);
289 m_labelView->setAllColumnsShowFocus(
true);
290 m_labelView->setItemDelegate(
new GroupItemDelegate(
this, m_dataModel));
291 m_labelView->setSortingEnabled(
true);
292 m_labelView->setWhatsThis(tr(
"This panel lists the source labels."));
293 m_labelView->setModel(m_sortedLabelsModel);
294 m_labelView->header()->setSectionsMovable(
false);
295 m_labelView->setColumnHidden(0,
true);
296 m_labelView->header()->setStretchLastSection(
false);
298 m_contextAndLabelView->addTab(m_labelView,
"ID Based"_L1);
301 m_messagesDock =
new QDockWidget(
this);
302 m_messagesDock->setObjectName(
"StringsDockWidget");
303 m_messagesDock->setAllowedAreas(Qt::AllDockWidgetAreas);
304 m_messagesDock->setWindowTitle(tr(
"Strings"));
305 m_messagesDock->setAcceptDrops(
true);
306 m_messagesDock->installEventFilter(
this);
308 m_sortedTextBasedMessagesModel =
new SortedMessagesModel(
this, m_dataModel, TEXTBASED);
309 m_sortedTextBasedMessagesModel->setSortRole(MessageModel::SortRole);
310 m_sortedTextBasedMessagesModel->setSortCaseSensitivity(Qt::CaseInsensitive);
311 m_sortedTextBasedMessagesModel->setSortLocaleAware(
true);
312 m_sortedTextBasedMessagesModel->setSourceModel(m_textBasedMessageModel);
314 m_sortedIdBasedMessagesModel =
new SortedMessagesModel(
this, m_dataModel, IDBASED);
315 m_sortedIdBasedMessagesModel->setSortRole(MessageModel::SortRole);
316 m_sortedIdBasedMessagesModel->setSortCaseSensitivity(Qt::CaseInsensitive);
317 m_sortedIdBasedMessagesModel->setSortLocaleAware(
true);
318 m_sortedIdBasedMessagesModel->setSourceModel(m_idBasedMessageModel);
320 m_messageView =
new QTreeView(m_messagesDock);
321 m_messageView->setSortingEnabled(
true);
322 m_messageView->setRootIsDecorated(
false);
323 m_messageView->setUniformRowHeights(
true);
324 m_messageView->setAllColumnsShowFocus(
true);
325 m_messageView->setItemsExpandable(
false);
326 m_messageView->setModel(m_sortedTextBasedMessagesModel);
327 m_messageView->header()->setSectionsMovable(
false);
328 m_messageView->setColumnHidden(0,
true);
330 m_messagesDock->setWidget(m_messageView);
333 m_messageEditor =
new MessageEditor(m_dataModel,
this);
334 m_messageEditor->setAcceptDrops(
true);
335 m_messageEditor->installEventFilter(
this);
337 QBoxLayout *lout =
new QBoxLayout(QBoxLayout::TopToBottom, m_ui.centralwidget);
338 lout->addWidget(m_messageEditor);
339 lout->setContentsMargins(QMargins());
340 m_ui.centralwidget->setLayout(lout);
343 m_phrasesDock =
new QDockWidget(
this);
344 m_phrasesDock->setObjectName(
"PhrasesDockwidget");
345 m_phrasesDock->setAllowedAreas(Qt::AllDockWidgetAreas);
346 m_phrasesDock->setWindowTitle(tr(
"Phrases and guesses"));
348 m_phraseView =
new PhraseView(m_dataModel, &m_phraseDict,
this);
349 m_phrasesDock->setWidget(m_phraseView);
352 m_sourceAndFormDock =
new QDockWidget(
this);
353 m_sourceAndFormDock->setObjectName(
"SourceAndFormDock");
354 m_sourceAndFormDock->setAllowedAreas(Qt::AllDockWidgetAreas);
355 m_sourceAndFormDock->setWindowTitle(tr(
"Sources and Forms"));
356 m_sourceAndFormView =
new QStackedWidget(
this);
357 m_sourceAndFormDock->setWidget(m_sourceAndFormView);
358 m_uiFormPreviewView =
new UiFormPreviewView(0, m_dataModel);
359 m_sourceCodeView =
new SourceCodeView(0);
361 m_qmlFormPreviewView =
new QmlFormPreviewView(m_dataModel);
362 m_sourceAndFormView->addWidget(m_qmlFormPreviewView);
364 m_sourceAndFormView->addWidget(m_sourceCodeView);
365 m_sourceAndFormView->addWidget(m_uiFormPreviewView);
368 m_errorsDock =
new QDockWidget(
this);
369 m_errorsDock->setObjectName(
"ErrorsDockWidget");
370 m_errorsDock->setAllowedAreas(Qt::AllDockWidgetAreas);
371 m_errorsDock->setWindowTitle(tr(
"Warnings"));
372 m_errorsView =
new ErrorsView(m_dataModel,
this);
373 m_errorsDock->setWidget(m_errorsView);
376 setDockNestingEnabled(
true);
377 setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
378 setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
379 setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
380 setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
381 addDockWidget(Qt::LeftDockWidgetArea, m_contextAndLabelDock);
382 addDockWidget(Qt::TopDockWidgetArea, m_messagesDock);
383 addDockWidget(Qt::BottomDockWidgetArea, m_phrasesDock);
384 addDockWidget(Qt::TopDockWidgetArea, m_sourceAndFormDock);
385 m_sourceAndFormDock->hide();
386 addDockWidget(Qt::BottomDockWidgetArea, m_errorsDock);
391 m_messageEditor->installEventFilter(m_phraseView);
394 QShortcut *contextShortcut =
new QShortcut(QKeySequence(Qt::Key_F6),
this);
395 connect(contextShortcut, &QShortcut::activated,
397 QShortcut *messagesShortcut =
new QShortcut(QKeySequence(Qt::Key_F7),
this);
398 connect(messagesShortcut, &QShortcut::activated,
400 QShortcut *errorsShortcut =
new QShortcut(QKeySequence(Qt::Key_F8),
this);
401 connect(errorsShortcut, &QShortcut::activated,
403 QShortcut *sourceCodeShortcut =
new QShortcut(QKeySequence(Qt::Key_F9),
this);
404 connect(sourceCodeShortcut, &QShortcut::activated,
406 QShortcut *phrasesShortcut =
new QShortcut(QKeySequence(Qt::Key_F10),
this);
407 connect(phrasesShortcut, &QShortcut::activated,
410 connect(m_phraseView, &PhraseView::phraseSelected,
413 this, &
MainWindow::setCurrentMessageFromGuess);
414 connect(m_contextView->selectionModel(), &QItemSelectionModel::currentRowChanged,
415 this, &MainWindow::selectedContextChanged);
416 connect(m_labelView->selectionModel(), &QItemSelectionModel::currentRowChanged,
this,
417 &MainWindow::selectedLabelChanged);
418 connect(m_contextView->selectionModel(), &QItemSelectionModel::currentColumnChanged,
419 this, &MainWindow::updateLatestModel);
420 connect(m_labelView->selectionModel(), &QItemSelectionModel::currentColumnChanged,
this,
421 &MainWindow::updateLatestModel);
424 connect(m_contextAndLabelView, &QTabWidget::currentChanged,
this,
427 m_translateDialog =
new TranslateDialog(
this);
428 m_batchTranslateDialog =
new BatchTranslationDialog(m_dataModel,
this);
429 m_findDialog =
new FindDialog(
this);
434 m_progressLabel =
new QLabel();
435 statusBar()->addPermanentWidget(m_progressLabel);
436 m_modifiedLabel =
new QLabel(tr(
" MOD ",
"status bar: file(s) modified"));
437 statusBar()->addPermanentWidget(m_modifiedLabel);
438 m_contextAndLabelView->setCurrentWidget(m_contextView);
439 contextAndLabelTabChanged();
446 this, &QWidget::setWindowModified);
448 m_modifiedLabel, &QWidget::setVisible);
460 connect(m_messageView, &QAbstractItemView::clicked,
461 this, &MainWindow::toggleFinished);
462 connect(m_messageView, &QAbstractItemView::activated,
464 connect(m_contextView, &QAbstractItemView::activated,
465 m_messageView, qOverload<>(&QWidget::setFocus));
466 connect(m_labelView, &QAbstractItemView::activated, m_messageView,
467 qOverload<>(&QWidget::setFocus));
468 connect(m_messageEditor, &MessageEditor::translationChanged,
469 this, &MainWindow::updateTranslation);
470 connect(m_messageEditor, &
MessageEditor::translatorCommentChanged,
471 this, &MainWindow::updateTranslatorComment);
472 connect(m_findDialog, &FindDialog::findNext,
473 this, &MainWindow::findNext);
474 connect(m_translateDialog, &TranslateDialog::requestMatchUpdate,
476 connect(m_translateDialog, &TranslateDialog::activated,
479 QSize as(screen()->size());
481 resize(QSize(1000, 800).boundedTo(as));
486 connect(m_ui.actionLengthVariants, &QAction::toggled,
491 m_focusWatcher =
new FocusWatcher(m_messageEditor,
this);
492 m_contextView->installEventFilter(m_focusWatcher);
493 m_labelView->installEventFilter(m_focusWatcher);
494 m_messageView->installEventFilter(m_focusWatcher);
495 m_messageEditor->installEventFilter(m_focusWatcher);
496 m_sourceAndFormView->installEventFilter(m_focusWatcher);
497 m_phraseView->installEventFilter(m_focusWatcher);
498 m_errorsView->installEventFilter(m_focusWatcher);
504 qDeleteAll(m_phraseBooks);
507#if QT_CONFIG(printsupport)
514 m_contextView->header()->setSectionResizeMode(1, QHeaderView::Stretch);
515 m_contextView->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
516 m_labelView->header()->setSectionResizeMode(1, QHeaderView::Stretch);
517 m_labelView->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
525 for (
int i = 1; i < mc + 1; ++i) {
526 m_contextView->header()->setSectionResizeMode(i, QHeaderView::Fixed);
527 m_contextView->header()->resizeSection(i, 24);
529 m_labelView->header()->setSectionResizeMode(i, QHeaderView::Fixed);
530 m_labelView->header()->resizeSection(i, 24);
532 m_messageView->header()->setSectionResizeMode(i, QHeaderView::Fixed);
533 m_messageView->header()->resizeSection(i, 24);
535 for (
int i = mc + 1; i < m_messageView->header()->count(); i++)
536 m_messageView->header()->setSectionResizeMode(i, QHeaderView::Stretch);
539 selectedMessageChanged(QModelIndex(), QModelIndex());
540 doUpdateLatestModel(-1);
542 QTreeView *view = qobject_cast<QTreeView *>(m_contextAndLabelView->currentWidget());
543 if (!view->currentIndex().isValid()) {
545 view->setCurrentIndex(m_activeSortedGroupsModel->index(0, 0));
548 view->selectionModel()->select(view->currentIndex(),
549 QItemSelectionModel::SelectCurrent
550 | QItemSelectionModel::Rows);
551 m_messageView->selectionModel()->select(m_messageView->currentIndex(),
552 QItemSelectionModel::SelectCurrent|QItemSelectionModel::Rows);
557 doUpdateLatestModel(0);
558 else if (m_currentIndex
.model() >= mc)
559 doUpdateLatestModel(mc - 1);
562 m_contextView->setUpdatesEnabled(
true);
563 m_labelView->setUpdatesEnabled(
true);
564 m_messageView->setUpdatesEnabled(
true);
569 m_ui.actionFind->setEnabled(m_dataModel->contextCount() > 0 || m_dataModel->labelCount() > 0);
570 m_ui.actionFindNext->setEnabled(
false);
571 m_ui.actionFindPrev->setEnabled(
false);
577 updateVisibleColumns();
593 bool waitCursor =
false;
594 statusBar()->showMessage(tr(
"Loading..."));
595 qApp->processEvents();
598 bool closeOld =
false;
599 for (QString name : names) {
601 QApplication::setOverrideCursor(Qt::WaitCursor);
605 bool readWrite = m_globalReadWrite;
606 if (name.startsWith(u'=')) {
612 name = fi.canonicalFilePath();
613 if (m_dataModel->isFileLoaded(name) >= 0)
617 DataModel *dm =
new DataModel(m_dataModel);
618 if (!dm->load(name, &langGuessed,
this)) {
622 if (opened.isEmpty()) {
623 if (!m_dataModel->isWellMergeable(dm)) {
624 QApplication::restoreOverrideCursor();
626 switch (QMessageBox::information(
this, tr(
"Loading File - Qt Linguist"),
627 tr(
"The file '%1' does not seem to be related to the currently open file(s) '%2'.\n\n"
628 "Close the open file(s) first?")
629 .arg(DataModel::prettifyPlainFileName(name), m_dataModel->condensedSrcFileNames(
true)),
630 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
632 case QMessageBox::Cancel:
635 case QMessageBox::Yes:
643 if (!opened.first().dataModel->isWellMergeable(dm)) {
644 QApplication::restoreOverrideCursor();
646 switch (QMessageBox::information(
this, tr(
"Loading File - Qt Linguist"),
647 tr(
"The file '%1' does not seem to be related to the file '%2'"
648 " which is being loaded as well.\n\n"
649 "Skip loading the first named file?")
650 .arg(DataModel::prettifyPlainFileName(name), opened.first().dataModel->srcFileName(
true)),
651 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
653 case QMessageBox::Cancel:
655 for (
const OpenedFile &op : std::as_const(opened))
658 case QMessageBox::Yes:
666 opened.append(OpenedFile(dm, readWrite, langGuessed));
671 QApplication::restoreOverrideCursor();
675 for (
const OpenedFile &op : std::as_const(opened))
681 for (
const OpenedFile &op : std::as_const(opened)) {
682 if (op.langGuessed) {
684 QApplication::restoreOverrideCursor();
687 if (!m_translationSettingsDialog)
688 m_translationSettingsDialog =
new TranslationSettingsDialog(
this);
689 m_translationSettingsDialog->setDataModel(op.dataModel);
690 m_translationSettingsDialog->exec();
695 QApplication::setOverrideCursor(Qt::WaitCursor);
696 m_contextView->setUpdatesEnabled(
false);
697 m_labelView->setUpdatesEnabled(
false);
698 m_messageView->setUpdatesEnabled(
false);
700 for (
const OpenedFile &op : std::as_const(opened)) {
701 m_phraseDict.append(QHash<QString, QList<Phrase *> >());
702 m_dataModel->append(op.dataModel, op.readWrite);
704 updatePhraseDictInternal(m_phraseDict.size() - 1);
705 totalCount += op.dataModel->messageCount();
707 statusBar()->showMessage(tr(
"%n translation unit(s) loaded.", 0, totalCount), MessageMS);
709 m_recentFiles.addFiles(m_dataModel->srcFileNames());
712 QApplication::restoreOverrideCursor();
718 m_globalReadWrite =
true;
719 pickTranslationFiles();
724 m_globalReadWrite =
false;
725 pickTranslationFiles();
731 if (model >= 0 && maybeSave(model)) {
732 m_phraseDict.removeAt(model);
733 m_contextView->setUpdatesEnabled(
false);
734 m_labelView->setUpdatesEnabled(
false);
735 m_messageView->setUpdatesEnabled(
false);
743 if (maybeSaveAll()) {
744 m_phraseDict.clear();
745 m_contextView->setUpdatesEnabled(
false);
746 m_labelView->setUpdatesEnabled(
false);
747 m_messageView->setUpdatesEnabled(
false);
751 m_recentFiles.closeGroup();
759 static const QString pattern(
"%1 (*.%2);;"_L1);
760 QStringList allExtensions;
762 for (
const Translator::FileFormat &format : std::as_const(Translator::registeredFileFormats())) {
763 if (format.fileType == Translator::FileFormat::TranslationSource && format.priority >= 0) {
764 filter.append(pattern.arg(format.description(), format.extension));
765 allExtensions.append(
"*."_L1 + format.extension);
768 QString allFilter = QObject::tr(
"Translation files (%1);;").arg(allExtensions.join(u' '));
770 filter.prepend(allFilter);
772 filter.append(allFilter);
773 filter.append(QObject::tr(
"All files (*)"));
780 if (!m_recentFiles.isEmpty())
781 dir = QFileInfo(m_recentFiles.lastOpenedFile()).path();
785 QFileInfo mainFile(m_dataModel->srcFileName(0));
786 QString mainFileBase = mainFile.baseName();
787 int pos = mainFileBase.indexOf(u'_');
789 varFilt = tr(
"Related files (%1);;")
790 .arg(mainFileBase.left(pos) +
"_*."_L1 + mainFile.completeSuffix());
794 openFiles(QFileDialog::getOpenFileNames(
this, tr(
"Open Translation Files"), dir,
795 varFilt + fileFilters(
true)));
797 const auto fileOpenCompleted = [
this](
const QString &fileName,
const QByteArray &fileContent) {
798 const QString copyFileName =
799 QDir::tempPath() + QLatin1String(
"/") + QFileInfo(fileName).fileName();
800 QFile tsFile(copyFileName);
801 if (tsFile.open(QIODevice::WriteOnly)) {
802 tsFile.write(fileContent);
804 openFiles({ copyFileName });
805 m_wasmFileMap[copyFileName] = std::move(fileName);
809 QFileDialog::getOpenFileContent(varFilt + fileFilters(
true), fileOpenCompleted);
815 QApplication::setOverrideCursor(Qt::WaitCursor);
816 if (m_dataModel->save(model,
this)) {
819 QString wasmFileName = m_dataModel->model(model)->srcFileName();
820 if (
const auto itr = m_wasmFileMap.find(wasmFileName); itr != m_wasmFileMap.end()) {
821 QFile tsFile(wasmFileName);
822 if (tsFile.open(QIODevice::ReadOnly)) {
824 QByteArray content = tsFile.readAll();
825 QFileDialog::saveFileContent(content, itr.value(),
this);
829 statusBar()->showMessage(tr(
"File saved."), MessageMS);
831 QApplication::restoreOverrideCursor();
839 m_recentFiles.closeGroup();
845 QMessageBox::warning(
this, tr(
"Qt Linguist"), tr(
"Please select a file to be saved."));
849 saveInternal(m_currentIndex
.model());
855 QMessageBox::warning(
this, tr(
"Qt Linguist"),
856 tr(
"This function is not available on WebAssembly"));
863 QString newFilename = QFileDialog::getSaveFileName(
864 this, QString(), m_dataModel->srcFileName(m_currentIndex.model()), fileFilters(
false));
865 if (!newFilename.isEmpty()) {
866 if (m_dataModel->saveAs(m_currentIndex
.model(), newFilename,
this)) {
868 statusBar()->showMessage(tr(
"File saved."), MessageMS);
869 m_recentFiles.addFiles(m_dataModel->srcFileNames());
879 QFileInfo oldFile(m_dataModel->srcFileName(m_currentIndex
.model()));
880 QString newFilename = oldFile.path() +
"/"_L1 + oldFile.completeBaseName() +
".qm"_L1;
882 newFilename = QFileDialog::getSaveFileName(
this, tr(
"Release"), newFilename,
883 tr(
"Qt message files for released applications (*.qm)\nAll files (*)"));
884 if (!newFilename.isEmpty()) {
886 statusBar()->showMessage(tr(
"File created."), MessageMS);
892 QFileInfo oldFile(m_dataModel->srcFileName(model));
893 QString newFilename = oldFile.path() + u'/' + oldFile.completeBaseName() +
".qm"_L1;
895 if (!newFilename.isEmpty()) {
896 if (m_dataModel->release(model, newFilename,
false,
false,
SaveEverything,
this))
897 statusBar()->showMessage(tr(
"File created."), MessageMS);
907 releaseInternal(m_currentIndex
.model());
917#if QT_CONFIG(printsupport)
918QPrinter *MainWindow::printer()
921 m_printer =
new QPrinter;
925void MainWindow::print()
928 QPrintDialog dlg(printer(),
this);
930 QApplication::setOverrideCursor(Qt::WaitCursor);
931 printer()->setDocName(m_dataModel->condensedSrcFileNames(
true));
932 statusBar()->showMessage(tr(
"Printing..."));
933 PrintOut pout(printer());
935 auto printGroupItem = [&pout, &pageNum,
this](
int index, TranslationType type) {
936 MultiGroupItem *mg = m_dataModel->multiGroupItem(index, type);
938 pout.setRule(PrintOut::ThickRule);
939 pout.setGuide(mg->group());
941 pout.addBox(100, tr(
"Label: %1").arg(mg->group()), PrintOut::Strong);
943 pout.addBox(100, tr(
"Context: %1").arg(mg->group()), PrintOut::Strong);
946 pout.addBox(92, mg->comment(), PrintOut::Emphasis);
948 pout.setRule(PrintOut::ThickRule);
950 for (
int j = 0; j < mg->messageCount(); ++j) {
951 pout.setRule(PrintOut::ThinRule);
952 bool printedSrc =
false;
954 for (
int k = 0; k < m_dataModel->modelCount(); ++k) {
955 if (
const MessageItem *m = mg->messageItem(k, j)) {
957 pout.addBox(40, m->text());
959 comment = m->comment();
964 if (m->message().isPlural() && m_dataModel->language(k) != QLocale::C) {
965 QStringList transls = m->translations();
966 pout.addBox(40, transls.join(u'\n'));
968 pout.addBox(40, m->translation());
972 switch (m->message().type()) {
973 case TranslatorMessage::Finished:
974 type = tr(
"finished");
976 case TranslatorMessage::Unfinished:
977 type = m->danger() ? tr(
"unresolved") :
"unfinished"_L1;
979 case TranslatorMessage::Obsolete:
980 case TranslatorMessage::Vanished:
981 type = tr(
"obsolete");
984 pout.addBox(12, type, PrintOut::Normal, Qt::AlignRight);
988 if (!comment.isEmpty()) {
990 pout.addBox(92, comment, PrintOut::Emphasis);
991 pout.flushLine(
true);
994 if (pout.pageNum() != pageNum) {
995 pageNum = pout.pageNum();
996 statusBar()->showMessage(tr(
"Printing... (page %1)").arg(pageNum));
1001 for (
int i = 0; i < m_dataModel->labelCount(); ++i)
1002 printGroupItem(i, IDBASED);
1003 for (
int i = 0; i < m_dataModel->contextCount(); ++i)
1004 printGroupItem(i, TEXTBASED);
1005 pout.flushLine(
true);
1006 QApplication::restoreOverrideCursor();
1007 statusBar()->showMessage(tr(
"Printing completed"), MessageMS);
1009 statusBar()->showMessage(tr(
"Printing aborted"), MessageMS);
1017 if ((m_findWhere & where) == 0)
1020 QString text = searchWhat;
1022 if (m_findOptions.testFlag(FindDialog::IgnoreAccelerators))
1026 if (m_findOptions.testFlag(FindDialog::UseRegExp))
1027 return m_findDialog->getRegExp().match(text).hasMatch();
1029 return text.indexOf(m_findText, 0, m_findOptions.testFlag(FindDialog::MatchCase)
1030 ? Qt::CaseSensitive : Qt::CaseInsensitive) >= 0;
1038 const QModelIndex &startIndex = m_messageView->currentIndex();
1039 QModelIndex index = (direction == FindNext
1040 ? nextMessage(startIndex)
1041 : prevMessage(startIndex));
1043 while (index.isValid()) {
1044 QModelIndex realIndex = m_activeSortedMessagesModel->mapToSource(index);
1045 MultiDataIndex dataIndex = m_activeMessageModel->dataIndex(realIndex, -1);
1046 bool hadMessage =
false;
1049 if (m_findStatusFilter != -1 && m_findStatusFilter != m
->type())
1052 if (m_findOptions.testFlag(FindDialog::SkipObsolete)
1059 if (searchItem(DataModel::SourceText, m->text()))
1061 if (searchItem(DataModel::SourceText, m->pluralText()))
1063 if (searchItem(DataModel::Comments, m->comment()))
1065 if (searchItem(DataModel::Comments, m->extraComment()))
1068 const auto translations = m->translations();
1069 for (
const QString &trans : translations)
1070 if (searchItem(DataModel::Translations, trans))
1072 if (searchItem(DataModel::Comments, m->translatorComment()))
1079 setCurrentMessage(realIndex, i);
1082 const QModelIndex &c1 =
1083 m_activeSortedGroupsModel
1085 m_activeSortedMessagesModel->mapToSource(startIndex))
1087 const QModelIndex &c2 =
1088 m_activeSortedGroupsModel->mapFromSource(realIndex).parent();
1089 const QModelIndex &m = m_activeSortedMessagesModel->mapFromSource(realIndex);
1091 if (c2.row() < c1.row() || (c1.row() == c2.row() && m.row() <= startIndex.row()))
1092 statusBar()->showMessage(tr(
"Search wrapped."), MessageMS);
1094 m_findDialog->hide();
1102 if (index == startIndex)
1105 index = (direction == FindNext
1106 ? nextMessage(index)
1107 : prevMessage(index));
1111 QMessageBox::warning(m_findDialog, tr(
"Qt Linguist"),
1112 tr(
"Cannot find the string '%1'.").arg(m_findText));
1117 m_activeMessageModel->blockSignals(
true);
1118 m_batchTranslateDialog->setPhraseBooks(m_phraseBooks, m_currentIndex
.model());
1119 if (m_batchTranslateDialog->exec() != QDialog::Accepted)
1120 m_activeMessageModel->blockSignals(
false);
1126 m_latestCaseSensitivity = -1;
1127 QModelIndex idx = m_messageView->currentIndex();
1129 m_activeSortedMessagesModel->index(idx.row(), m_currentIndex
.model() + 1, idx.parent());
1130 m_messageView->setCurrentIndex(idx2);
1131 QString fn = QFileInfo(m_dataModel->srcFileName(m_currentIndex.model())).baseName();
1132 m_translateDialog->setWindowTitle(tr(
"Search And Translate in '%1' - Qt Linguist").arg(fn));
1133 m_translateDialog->exec();
1136void MainWindow::updateTranslateHit(
bool &hit)
1141 && m->compare(m_translateDialog->findText(),
false, m_translateDialog->caseSensitivity());
1146 QString findText = m_translateDialog->findText();
1147 QString replaceText = m_translateDialog->replaceText();
1148 bool markFinished = m_translateDialog->markFinished();
1149 Qt::CaseSensitivity caseSensitivity = m_translateDialog->caseSensitivity();
1151 int translatedCount = 0;
1153 if (mode == TranslateDialog::TranslateAll) {
1154 auto setTranslations = [
this, &translatedCount, &findText, &caseSensitivity, &replaceText,
1159 if (m && !m
->isObsolete() && m->compare(findText,
false, caseSensitivity)) {
1160 if (!translatedCount)
1161 m_activeMessageModel->blockSignals(
true);
1162 m_dataModel->setTranslation(it, replaceText);
1171 if (translatedCount) {
1173 QMessageBox::warning(m_translateDialog, tr(
"Translate - Qt Linguist"),
1174 tr(
"Translated %n entry(s)", 0, translatedCount));
1177 if (mode == TranslateDialog::Translate) {
1178 m_dataModel->setTranslation(m_currentIndex, replaceText);
1182 const QModelIndex firstIndex = firstMessage();
1183 if (findText != m_latestFindText || caseSensitivity != m_latestCaseSensitivity) {
1184 m_latestFindText = findText;
1185 m_latestCaseSensitivity = caseSensitivity;
1186 m_searchIndex = firstIndex;
1191 QModelIndex realIndex = m_activeSortedMessagesModel->mapToSource(m_searchIndex);
1192 MultiDataIndex dataIndex = m_activeMessageModel->dataIndex(realIndex, m_currentIndex.model());
1193 m_searchIndex = nextMessage(m_searchIndex);
1194 if (MessageItem *m = m_dataModel->messageItem(dataIndex)) {
1195 if (!m->isObsolete() && m->compare(findText,
false, caseSensitivity)) {
1196 setCurrentMessage(realIndex, m_currentIndex.model());
1202 if (m_searchIndex == firstIndex) {
1203 if (QMessageBox::question(
1204 m_translateDialog, tr(
"Translate - Qt Linguist"),
1205 tr(
"No more occurrences of '%1'. Start over?").arg(findText),
1206 QMessageBox::Yes | QMessageBox::No)
1207 != QMessageBox::Yes) {
1208 m_searchIndex = prevMessage(m_searchIndex);
1215 if (!translatedCount) {
1217 QMessageBox::warning(m_translateDialog, tr(
"Translate - Qt Linguist"),
1218 tr(
"Cannot find the string '%1'.").arg(findText));
1224 QString name = QFileDialog::getSaveFileName(
this, tr(
"Create New Phrase Book"),
1225 m_phraseBookDir, tr(
"Qt phrase books (*.qph)\nAll files (*)"));
1226 if (!name.isEmpty()) {
1228 if (!m_translationSettingsDialog)
1229 m_translationSettingsDialog =
new TranslationSettingsDialog(
this);
1231 if (!m_translationSettingsDialog->exec())
1233 m_phraseBookDir = QFileInfo(name).absolutePath();
1234 if (savePhraseBook(&name, pb)) {
1235 if (doOpenPhraseBook(name))
1236 statusBar()->showMessage(tr(
"Phrase book created."), MessageMS);
1241bool MainWindow::isPhraseBookOpen(
const QString &name)
1243 for (
const PhraseBook *pb : std::as_const(m_phraseBooks)) {
1244 if (pb->fileName() == name)
1253 QString name = QFileDialog::getOpenFileName(
this, tr(
"Open Phrase Book"),
1254 m_phraseBookDir, tr(
"Qt phrase books (*.qph);;All files (*)"));
1256 if (!name.isEmpty()) {
1257 m_phraseBookDir = QFileInfo(name).absolutePath();
1258 if (!isPhraseBookOpen(name)) {
1259 if (
PhraseBook *phraseBook = doOpenPhraseBook(name)) {
1260 int n = phraseBook->phrases().size();
1261 statusBar()->showMessage(tr(
"%n phrase(s) loaded.", 0, n), MessageMS);
1267void MainWindow::closePhraseBook(QAction *action)
1269 PhraseBook *pb = m_phraseBookMenu[PhraseCloseMenu].value(action);
1270 if (!maybeSavePhraseBook(pb))
1273 m_phraseBookMenu[PhraseCloseMenu].remove(action);
1274 m_ui.menuClosePhraseBook->removeAction(action);
1276 QAction *act = m_phraseBookMenu[PhraseEditMenu].key(pb);
1277 m_phraseBookMenu[PhraseEditMenu].remove(act);
1278 m_ui.menuEditPhraseBook->removeAction(act);
1280 act = m_phraseBookMenu[PhrasePrintMenu].key(pb);
1281 m_ui.menuPrintPhraseBook->removeAction(act);
1283 m_phraseBooks.removeOne(pb);
1286 updatePhraseDicts();
1288 updatePhraseBookActions();
1291void MainWindow::editPhraseBook(QAction *action)
1293 PhraseBook *pb = m_phraseBookMenu[PhraseEditMenu].value(action);
1297 updatePhraseDicts();
1300#if QT_CONFIG(printsupport)
1302void MainWindow::printPhraseBook(QAction *action)
1304 PhraseBook *phraseBook = m_phraseBookMenu[PhrasePrintMenu].value(action);
1308 QPrintDialog dlg(printer(),
this);
1310 printer()->setDocName(phraseBook->fileName());
1311 statusBar()->showMessage(tr(
"Printing..."));
1312 PrintOut pout(printer());
1313 pout.setRule(PrintOut::ThinRule);
1314 const auto phrases = phraseBook->phrases();
1315 for (
const Phrase *p : phrases) {
1316 pout.setGuide(p->source());
1317 pout.addBox(29, p->source());
1319 pout.addBox(29, p->target());
1321 pout.addBox(34, p->definition(), PrintOut::Emphasis);
1323 if (pout.pageNum() != pageNum) {
1324 pageNum = pout.pageNum();
1325 statusBar()->showMessage(tr(
"Printing... (page %1)")
1328 pout.setRule(PrintOut::NoRule);
1329 pout.flushLine(
true);
1331 pout.flushLine(
true);
1332 statusBar()->showMessage(tr(
"Printing completed"), MessageMS);
1334 statusBar()->showMessage(tr(
"Printing aborted"), MessageMS);
1342 QStringList phraseBookList;
1343 QHash<QString, PhraseBook *> phraseBookHash;
1344 for (PhraseBook *pb : std::as_const(m_phraseBooks)) {
1345 if (pb->language() != QLocale::C && m_dataModel->language(m_currentIndex.model()) != QLocale::C) {
1346 if (pb->language() != m_dataModel->language(m_currentIndex.model()))
1348 if (pb->territory() == m_dataModel->model(m_currentIndex.model())->territory())
1349 phraseBookList.prepend(pb->friendlyPhraseBookName());
1351 phraseBookList.append(pb->friendlyPhraseBookName());
1353 phraseBookList.append(pb->friendlyPhraseBookName());
1355 phraseBookHash.insert(pb->friendlyPhraseBookName(), pb);
1357 if (phraseBookList.isEmpty()) {
1358 QMessageBox::warning(
this, tr(
"Add to phrase book"),
1359 tr(
"No appropriate phrasebook found."));
1363 QString selectedPhraseBook;
1364 if (phraseBookList.size() == 1) {
1365 selectedPhraseBook = phraseBookList.at(0);
1366 if (QMessageBox::information(
this, tr(
"Add to phrase book"),
1367 tr(
"Adding entry to phrasebook %1").arg(selectedPhraseBook),
1368 QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok)
1372 bool okPressed =
false;
1373 selectedPhraseBook = QInputDialog::getItem(
this, tr(
"Add to phrase book"),
1374 tr(
"Select phrase book to add to"),
1375 phraseBookList, 0,
false, &okPressed);
1381 Phrase *phrase =
new Phrase(currentMessage->text(), currentMessage->translation(),
1382 QString(),
nullptr);
1384 phraseBookHash.value(selectedPhraseBook)->append(phrase);
1389 m_contextView->sortByColumn(-1, Qt::AscendingOrder);
1390 m_labelView->sortByColumn(-1, Qt::AscendingOrder);
1391 m_messageView->sortByColumn(-1, Qt::AscendingOrder);
1396 QString errorMessage;
1397 const QString page = m_helpClient->documentUrl(u"linguist"_s) +
"/qtlinguist-index.html"_L1;
1398 if (!m_helpClient->showPage(page, &errorMessage))
1399 qWarning(
"%s", qPrintable(errorMessage));
1404 return tr(
"Qt Linguist is a tool for adding translations to Qt applications.");
1409 QMessageBox box(
this);
1410 box.setTextFormat(Qt::RichText);
1411 QString version = tr(
"Version %1");
1412 version = version.arg(QLatin1String(QT_VERSION_STR));
1414 box.setText(QStringLiteral(
"<center><img src=\":/images/icons/linguist-128-32.png\"/></img><p>%1</p></center>"
1416 "<p>Copyright (C) The Qt Company Ltd.</p>").arg(version, description()));
1418 box.setWindowTitle(QApplication::translate(
"AboutDialog",
"Qt Linguist"));
1419 box.setIcon(QMessageBox::NoIcon);
1425 QMessageBox::aboutQt(
this, tr(
"Qt Linguist"));
1430 bool enabled = !m_phraseBooks.isEmpty();
1431 m_ui.menuClosePhraseBook->setEnabled(enabled);
1432 m_ui.menuEditPhraseBook->setEnabled(enabled);
1433#if QT_CONFIG(printsupport)
1434 m_ui.menuPrintPhraseBook->setEnabled(enabled);
1440 if (maybeSaveAll() && maybeSavePhraseBooks())
1451 switch (QMessageBox::information(
this, tr(
"Qt Linguist"),
1452 tr(
"Do you want to save the modified files?"),
1453 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
1455 case QMessageBox::Cancel:
1457 case QMessageBox::Yes:
1471 switch (QMessageBox::information(
this, tr(
"Qt Linguist"),
1472 tr(
"Do you want to save '%1'?").arg(m_dataModel->srcFileName(model,
true)),
1473 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
1475 case QMessageBox::Cancel:
1477 case QMessageBox::Yes:
1478 saveInternal(model);
1489 bool enable =
false;
1490 bool enableRw =
false;
1498 m_ui.actionSaveAll->setEnabled(enableRw);
1499 m_ui.actionReleaseAll->setEnabled(enableRw);
1500 m_ui.actionCloseAll->setEnabled(enable);
1501#if QT_CONFIG(printsupport)
1502 m_ui.actionPrint->setEnabled(enable);
1504 m_ui.actionAccelerators->setEnabled(enable);
1505 m_ui.actionSurroundingWhitespace->setEnabled(enable);
1506 m_ui.actionEndingPunctuation->setEnabled(enable);
1507 m_ui.actionPhraseMatches->setEnabled(enable);
1508 m_ui.actionPlaceMarkerMatches->setEnabled(enable);
1509 m_ui.actionResetSorting->setEnabled(enable);
1513 m_fileActiveModel = m_editActiveModel = -2;
1516 cap = tr(
"Qt Linguist[*]");
1518 cap = tr(
"%1[*] - Qt Linguist").arg(m_dataModel->condensedSrcFileNames(
true));
1519 setWindowTitle(cap);
1522void MainWindow::selectedContextChanged(
const QModelIndex &sortedIndex,
const QModelIndex &oldIndex)
1524 if (sortedIndex.isValid()) {
1525 if (m_settingCurrentMessage)
1528 if (!m_activeTranslationType || *m_activeTranslationType == IDBASED)
1529 contextAndLabelTabChanged();
1530 QModelIndex sourceIndex = m_sortedContextsModel->mapToSource(sortedIndex);
1531 if (m_activeMessageModel->parent(currentMessageIndex()).row() == sourceIndex.row())
1533 QModelIndex contextIndex = setMessageViewRoot(sourceIndex);
1534 const QModelIndex &firstChild =
1535 m_activeSortedMessagesModel->index(0, sourceIndex.column(), contextIndex);
1536 m_messageView->setCurrentIndex(firstChild);
1537 }
else if (oldIndex.isValid()) {
1538 m_contextView->setCurrentIndex(oldIndex);
1542void MainWindow::selectedLabelChanged(
const QModelIndex &sortedIndex,
const QModelIndex &oldIndex)
1544 if (sortedIndex.isValid()) {
1545 if (m_settingCurrentMessage)
1548 if (!m_activeTranslationType || *m_activeTranslationType != IDBASED)
1549 contextAndLabelTabChanged();
1550 QModelIndex sourceIndex = m_sortedLabelsModel->mapToSource(sortedIndex);
1551 if (m_activeMessageModel->parent(currentMessageIndex()).row() == sourceIndex.row())
1553 QModelIndex labelIndex = setMessageViewRoot(sourceIndex);
1554 const QModelIndex &firstChild =
1555 m_activeSortedMessagesModel->index(0, sourceIndex.column(), labelIndex);
1556 m_messageView->setCurrentIndex(firstChild);
1557 }
else if (oldIndex.isValid()) {
1558 m_labelView->setCurrentIndex(oldIndex);
1563
1564
1565void MainWindow::selectedMessageChanged(
const QModelIndex &sortedIndex,
const QModelIndex &oldIndex)
1568 if (!sortedIndex.isValid() && oldIndex.isValid()) {
1569 m_messageView->setCurrentIndex(oldIndex);
1575 QModelIndex index = m_activeSortedMessagesModel->mapToSource(sortedIndex);
1576 if (index.isValid()) {
1577 model = (index.column() && (index.column() - 1 < m_dataModel
->modelCount()))
1578 ? index.column() - 1
1580 m_currentIndex = m_activeMessageModel->dataIndex(index, model);
1584 m_phraseView->setSourceText(m_currentIndex
.model(), m->text());
1586 m_phraseView->setSourceText(-1, QString());
1594 m_phraseView->setSourceText(-1, QString());
1596 m_errorsView->setEnabled(m != 0);
1597 updateDanger(m_currentIndex,
true);
1601 m_phraseView->setSourceText(-1, QString());
1603 updateSourceView(model, m);
1605 updatePhraseBookActions();
1606 m_ui.actionSelectAll->setEnabled(index.isValid());
1612 updateDanger(index,
true);
1615 if (hasUiFormPreview(m->fileName()))
1618 else if (hasQmlFormPreview(m->fileName(), m_ui.actionQmlPreview->isChecked()))
1619 if (!m_qmlFormPreviewView->setSourceContext(index.model(), m))
1620 m_ui.actionQmlPreview->setChecked(
false);
1626void MainWindow::updateTranslation(
const QStringList &translations)
1631 if (translations == m->translations())
1634 m->setTranslations(translations);
1635 if (!m->fileName().isEmpty() && hasUiFormPreview(m->fileName()))
1638 else if (!m->fileName().isEmpty()
1639 && hasQmlFormPreview(m->fileName(), m_ui.actionQmlPreview->isChecked()))
1640 if (!m_qmlFormPreviewView->setSourceContext(m_currentIndex.model(), m))
1641 m_ui.actionQmlPreview->setChecked(
false);
1643 updateDanger(m_currentIndex,
true);
1651void MainWindow::updateTranslatorComment(
const QString &comment)
1656 if (comment == m->translatorComment())
1659 m->setTranslatorComment(comment);
1666 m_activeMessageModel->blockSignals(
false);
1667 m_contextView->update();
1668 m_labelView->update();
1669 m_messageView->update();
1689void MainWindow::toggleFinished(
const QModelIndex &index)
1691 if (!index.isValid() || index.column() - 1 >= m_dataModel
->modelCount()
1695 QModelIndex item = m_activeSortedMessagesModel->mapToSource(index);
1696 MultiDataIndex dataIndex = m_activeMessageModel->dataIndex(item);
1708 if (!m_machineTranslationDialog)
1709 m_machineTranslationDialog =
new MachineTranslationDialog(
this);
1711 m_machineTranslationDialog->open();
1715
1716
1717
1718
1719QModelIndex
MainWindow::nextGroup(
const QModelIndex &index)
const
1721 QModelIndex sortedGroupIndex = m_activeSortedGroupsModel->mapFromSource(
1722 m_activeSortedMessagesModel->mapToSource(index));
1724 int nextRow = sortedGroupIndex.row() + 1;
1725 if (nextRow >= m_activeSortedGroupsModel->rowCount()) {
1726 const QSortFilterProxyModel *inactiveModel =
1727 m_activeSortedGroupsModel == m_sortedLabelsModel ? m_sortedContextsModel
1728 : m_sortedLabelsModel;
1729 if (inactiveModel->rowCount())
1730 m_contextAndLabelView->setCurrentIndex(1 - m_contextAndLabelView->currentIndex());
1733 sortedGroupIndex = m_activeSortedGroupsModel->index(nextRow, index.column());
1735 return m_activeSortedMessagesModel->mapFromSource(
1736 m_activeSortedGroupsModel->mapToSource(sortedGroupIndex));
1740
1741
1742QModelIndex
MainWindow::prevGroup(
const QModelIndex &index)
const
1744 QModelIndex sortedGroupIndex = m_activeSortedGroupsModel->mapFromSource(
1745 m_activeSortedMessagesModel->mapToSource(index));
1747 int prevRow = sortedGroupIndex.row() - 1;
1749 const QSortFilterProxyModel *inactiveModel =
1750 m_activeSortedGroupsModel == m_sortedLabelsModel ? m_sortedContextsModel
1751 : m_sortedLabelsModel;
1752 if (inactiveModel->rowCount())
1753 m_contextAndLabelView->setCurrentIndex(1 - m_contextAndLabelView->currentIndex());
1754 prevRow = m_activeSortedGroupsModel->rowCount() - 1;
1756 sortedGroupIndex = m_activeSortedGroupsModel->index(prevRow, index.column());
1758 return m_activeSortedMessagesModel->mapFromSource(
1759 m_activeSortedGroupsModel->mapToSource(sortedGroupIndex));
1764 QModelIndex id = m_activeSortedMessagesModel->index(0, 0);
1765 QModelIndex firstId;
1766 if (id.isValid() && m_activeSortedMessagesModel->hasChildren(id))
1767 firstId = m_activeSortedMessagesModel->index(0, 0, id);
1768 else if (id.isValid())
1773QModelIndex
MainWindow::nextMessage(
const QModelIndex ¤tIndex,
bool checkUnfinished)
const
1776 currentIndex.isValid() ? currentIndex : m_activeSortedMessagesModel->index(0, 0);
1779 QModelIndex par = idx.parent();
1780 if (par.isValid()) {
1781 row = idx.row() + 1;
1786 if (row >= m_activeSortedMessagesModel->rowCount(par)) {
1787 par = nextGroup(par);
1790 idx = m_activeSortedMessagesModel->index(row, idx.column(), par);
1792 if (!checkUnfinished)
1795 QModelIndex item = m_activeSortedMessagesModel->mapToSource(idx);
1796 MultiDataIndex index = m_activeMessageModel->dataIndex(item, -1);
1799 }
while (idx != currentIndex);
1800 return QModelIndex();
1803QModelIndex
MainWindow::prevMessage(
const QModelIndex ¤tIndex,
bool checkUnfinished)
const
1806 currentIndex.isValid() ? currentIndex : m_activeSortedMessagesModel->index(0, 0);
1808 int row = idx.row() - 1;
1809 QModelIndex par = idx.parent();
1810 if (!par.isValid()) {
1816 par = prevGroup(par);
1817 row = m_activeSortedMessagesModel->rowCount(par) - 1;
1819 idx = m_activeSortedMessagesModel->index(row, idx.column(), par);
1821 if (!checkUnfinished)
1824 QModelIndex item = m_activeSortedMessagesModel->mapToSource(idx);
1825 MultiDataIndex index = m_activeMessageModel->dataIndex(item, -1);
1828 }
while (idx != currentIndex);
1829 return QModelIndex();
1834 if (m_ui.actionNextUnfinished->isEnabled()) {
1835 if (!doNext(
true)) {
1838 statusBar()->showMessage(tr(
"No untranslated translation units left."), MessageMS);
1846 if (m_ui.actionNextUnfinished->isEnabled()) {
1847 if (!doPrev(
true)) {
1850 statusBar()->showMessage(tr(
"No untranslated translation units left."), MessageMS);
1868 QModelIndex index = prevMessage(m_messageView->currentIndex(), checkUnfinished);
1869 if (index.isValid())
1870 setCurrentMessage(m_activeSortedMessagesModel->mapToSource(index));
1871 if (checkUnfinished)
1875 return index.isValid();
1880 QModelIndex index = nextMessage(m_messageView->currentIndex(), checkUnfinished);
1881 if (index.isValid())
1882 setCurrentMessage(m_activeSortedMessagesModel->mapToSource(index));
1883 if (checkUnfinished)
1887 return index.isValid();
1891 FindDialog::FindOptions options,
int statusFilter)
1896 m_findWhere = where;
1897 m_findOptions = options;
1898 m_findStatusFilter = statusFilter;
1899 if (options.testFlag(FindDialog::UseRegExp)) {
1900 m_findDialog->getRegExp().setPatternOptions(options.testFlag(FindDialog::MatchCase)
1901 ? QRegularExpression::NoPatternOption
1902 : QRegularExpression::CaseInsensitiveOption);
1904 m_ui.actionFindNext->setEnabled(
true);
1905 m_ui.actionFindPrev->setEnabled(
true);
1912 updateDanger(it,
false);
1914 updateDanger(it,
false);
1917 updateDanger(m_currentIndex,
true);
1922 const QString prefix = isDarkMode() ?
":/images/darkicons/"_L1:
":/images/lighticons/"_L1;
1923 auto getIcon = [&prefix](
const QString &name) {
1925 icon.addPixmap(QPixmap(prefix + name + QStringLiteral(
".png")), QIcon::Normal);
1926 icon.addPixmap(QPixmap(prefix + name + QStringLiteral(
"-disabled.png")), QIcon::Disabled);
1930 QIcon openIcon = getIcon(
"open-new"_L1);
1931 m_ui.actionOpen->setIcon(openIcon);
1932 m_ui.actionOpenAux->setIcon(openIcon);
1933 QIcon saveIcon = getIcon(
"save-fl-disk"_L1);
1934 m_ui.actionSave->setIcon(saveIcon);
1935 m_ui.actionSaveAll->setIcon(saveIcon);
1936 m_ui.actionPrint->setIcon(getIcon(
"print"_L1));
1937 m_ui.actionRedo->setIcon(getIcon(
"redo-arrow-right"_L1));
1938 m_ui.actionUndo->setIcon(getIcon(
"undo-arrow-left"_L1));
1939 m_ui.actionCut->setIcon(getIcon(
"cut"_L1));
1940 m_ui.actionCopy->setIcon(getIcon(
"copy-general"_L1));
1941 m_ui.actionPaste->setIcon(getIcon(
"paste-general"_L1));
1942 m_ui.actionFind->setIcon(getIcon(
"search-magnifier"_L1));
1944 m_ui.actionAccelerators->setIcon(getIcon(
"/check-ampersands"_L1));
1945 m_ui.actionOpenPhraseBook->setIcon(getIcon(
"library"_L1));
1946 m_ui.actionDone->setIcon(getIcon(
"mark-current-translation-done"_L1));
1947 m_ui.actionDoneAndNext->setIcon(getIcon(
"mark-current-translation-done-move-to-next"_L1));
1948 m_ui.actionNext->setIcon(getIcon(
"next-translation-item"_L1));
1949 m_ui.actionNextUnfinished->setIcon(getIcon(
"next-unfinished-translation-item"_L1));
1950 m_ui.actionPhraseMatches->setIcon(getIcon(
"check-phrase-suggestions"_L1));
1951 m_ui.actionSurroundingWhitespace->setIcon(getIcon(
"check-white-spaces"_L1));
1952 m_ui.actionEndingPunctuation->setIcon(getIcon(
"check-ending-pontuation"_L1));
1953 m_ui.actionPrev->setIcon(getIcon(
"previous-translation-item"_L1));
1954 m_ui.actionPrevUnfinished->setIcon(getIcon(
"previous-unfinished-translation-item"_L1));
1955 m_ui.actionPlaceMarkerMatches->setIcon(getIcon(
"check-place-markers"_L1));
1956 m_ui.actionWhatsThis->setIcon(getIcon(
"hit-help-chosen-option"_L1));
1961 m_ui.menuRecentlyOpenedFiles->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::DocumentOpenRecent));
1962 m_ui.actionCloseAll->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::WindowClose));
1963 m_ui.actionExit->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ApplicationExit));
1964 m_ui.actionSelectAll->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::EditSelectAll));
1968 connect(m_ui.menuFile, &QMenu::aboutToShow,
this, &
MainWindow::fileAboutToShow);
1969 connect(m_ui.actionOpen, &QAction::triggered,
this, &
MainWindow::open);
1970 connect(m_ui.actionOpenAux, &QAction::triggered,
this, &
MainWindow::openAux);
1971 connect(m_ui.actionSave, &QAction::triggered,
this, &
MainWindow::save);
1973 connect(m_ui.actionSaveAll, &QAction::triggered,
this, &
MainWindow::saveAll);
1974 connect(m_ui.actionSaveAs, &QAction::triggered,
this, &
MainWindow::saveAs);
1976 m_ui.actionSaveAs->setVisible(
false);
1977 m_ui.actionSaveAll->setVisible(
false);
1979 connect(m_ui.actionReleaseAll, &QAction::triggered,
this, &
MainWindow::releaseAll);
1980 connect(m_ui.actionRelease, &QAction::triggered,
this, &
MainWindow::release);
1981 connect(m_ui.actionReleaseAs, &QAction::triggered,
this, &
MainWindow::releaseAs);
1982#if QT_CONFIG(printsupport)
1983 connect(m_ui.actionPrint, &QAction::triggered,
this, &MainWindow::print);
1985 m_ui.actionPrint->setEnabled(
false);
1987 connect(m_ui.actionClose, &QAction::triggered,
this, &
MainWindow::closeFile);
1988 connect(m_ui.actionCloseAll, &QAction::triggered,
this, &
MainWindow::closeAll);
1989 connect(m_ui.actionExit, &QAction::triggered,
this, &MainWindow::close);
1992 connect(m_ui.menuEdit, &QMenu::aboutToShow,
this, &
MainWindow::editAboutToShow);
1994 connect(m_ui.actionUndo, &QAction::triggered, m_messageEditor, &MessageEditor::undo);
2000#ifndef QT_NO_CLIPBOARD
2011 connect(m_ui.actionSelectAll, &QAction::triggered,
2013 connect(m_ui.actionFind, &QAction::triggered,
2014 m_findDialog, &FindDialog::find);
2015 connect(m_ui.actionFindNext, &QAction::triggered,
2016 this, [
this] {findAgain(
FindNext);});
2017 connect(m_ui.actionFindPrev, &QAction::triggered,
2018 this, [
this] {findAgain(
FindPrev);});
2019 connect(m_ui.actionSearchAndTranslate, &QAction::triggered,
2021 connect(m_ui.actionBatchTranslation, &QAction::triggered,
2022 this, &
MainWindow::showBatchTranslateDialog);
2023 connect(m_ui.actionTranslationFileSettings, &QAction::triggered,
2026 connect(m_batchTranslateDialog, &BatchTranslationDialog::finished,
2031 connect(m_ui.actionAuto_Translation, &QAction::triggered,
this,
2033 connect(m_ui.actionPrevUnfinished, &QAction::triggered,
this, &
MainWindow::prevUnfinished);
2034 connect(m_ui.actionNextUnfinished, &QAction::triggered,
this, &
MainWindow::nextUnfinished);
2035 connect(m_ui.actionNext, &QAction::triggered,
this, &
MainWindow::next);
2036 connect(m_ui.actionPrev, &QAction::triggered,
this, &
MainWindow::prev);
2037 connect(m_ui.actionDone, &QAction::triggered,
this, &MainWindow::done);
2038 connect(m_ui.actionDoneAndNext, &QAction::triggered,
this, &
MainWindow::doneAndNext);
2039 connect(m_ui.actionBeginFromSource, &QAction::triggered, m_messageEditor,
2043 connect(m_ui.actionNewPhraseBook, &QAction::triggered,
this, &
MainWindow::newPhraseBook);
2044 connect(m_ui.actionOpenPhraseBook, &QAction::triggered,
this, &
MainWindow::openPhraseBook);
2045 connect(m_ui.menuClosePhraseBook, &QMenu::triggered,
2047 connect(m_ui.menuEditPhraseBook, &QMenu::triggered,
2049#if QT_CONFIG(printsupport)
2050 connect(m_ui.menuPrintPhraseBook, &QMenu::triggered,
2051 this, &MainWindow::printPhraseBook);
2053 m_ui.menuPrintPhraseBook->setEnabled(
false);
2055 connect(m_ui.actionAddToPhraseBook, &QAction::triggered,
2059 connect(m_ui.actionAccelerators, &QAction::triggered,
this, &
MainWindow::revalidate);
2060 connect(m_ui.actionSurroundingWhitespace, &QAction::triggered,
this, &
MainWindow::revalidate);
2061 connect(m_ui.actionEndingPunctuation, &QAction::triggered,
this, &
MainWindow::revalidate);
2062 connect(m_ui.actionPhraseMatches, &QAction::triggered,
this, &
MainWindow::revalidate);
2063 connect(m_ui.actionPlaceMarkerMatches, &QAction::triggered,
this, &
MainWindow::revalidate);
2066 connect(m_ui.actionResetSorting, &QAction::triggered,
2068 connect(m_ui.actionDisplayGuesses, &QAction::triggered,
2069 m_phraseView, &PhraseView::toggleGuessing);
2070 connect(m_ui.actionStatistics, &QAction::triggered,
this, &
MainWindow::showStatistics);
2072 connect(m_ui.actionQmlPreview, &QAction::triggered,
this, &
MainWindow::toggleQmlPreview);
2074 m_ui.actionQmlPreview->setVisible(
false);
2076 connect(m_ui.actionVisualizeWhitespace, &QAction::triggered,
2077 this, &
MainWindow::toggleVisualizeWhitespace);
2078 connect(m_ui.actionIncreaseZoom, &QAction::triggered,
2080 connect(m_ui.actionDecreaseZoom, &QAction::triggered,
2082 connect(m_ui.actionResetZoomToDefault, &QAction::triggered,
2084 connect(m_ui.actionShowMoreGuesses, &QAction::triggered,
2086 connect(m_ui.actionShowFewerGuesses, &QAction::triggered,
2089 m_ui.actionShowFewerGuesses, &QAction::setEnabled);
2090 connect(m_ui.actionResetGuessesToDefault, &QAction::triggered,
2092 m_ui.menuViewViews->addAction(m_contextAndLabelDock->toggleViewAction());
2093 m_ui.menuViewViews->addAction(m_messagesDock->toggleViewAction());
2094 m_ui.menuViewViews->addAction(m_phrasesDock->toggleViewAction());
2095 m_ui.menuViewViews->addAction(m_sourceAndFormDock->toggleViewAction());
2096 m_ui.menuViewViews->addAction(m_errorsDock->toggleViewAction());
2098#if defined(Q_OS_MAC)
2100 QMenu *windowMenu =
new QMenu(tr(
"&Window"),
this);
2101 menuBar()->insertMenu(m_ui.menuHelp->menuAction(), windowMenu);
2102 windowMenu->addAction(tr(
"Minimize"), QKeySequence(tr(
"Ctrl+M")),
2103 this, &QWidget::showMinimized);
2107 connect(m_ui.actionManual, &QAction::triggered,
this, &
MainWindow::manual);
2108 connect(m_ui.actionAbout, &QAction::triggered,
this, &
MainWindow::about);
2109 connect(m_ui.actionAboutQt, &QAction::triggered,
this, &
MainWindow::aboutQt);
2110 connect(m_ui.actionWhatsThis, &QAction::triggered,
this, &
MainWindow::onWhatsThis);
2112 connect(m_ui.menuRecentlyOpenedFiles, &QMenu::triggered,
2115 m_ui.actionManual->setToolTip(tr(
"Displays the manual for %1.").arg(tr(
"Qt Linguist")));
2116 m_ui.actionAbout->setToolTip(tr(
"Displays information about %1.").arg(tr(
"Qt Linguist")));
2117 m_ui.actionDone->setShortcuts(
2118 { Qt::AltModifier | Qt::Key_Return, Qt::AltModifier | Qt::Key_Enter });
2119 m_ui.actionDoneAndNext->setShortcuts({
2120 Qt::ControlModifier | Qt::Key_Return,
2121 Qt::ControlModifier | Qt::Key_Enter,
2125 connect(m_ui.menuPhrases, &QMenu::aboutToShow,
this, &
MainWindow::setupPhrase);
2127 connect(m_ui.menuRecentlyOpenedFiles, &QMenu::aboutToShow,
2134 doUpdateLatestModel(model);
2138void MainWindow::updateLatestModel(
const QModelIndex &index)
2140 if (index.column() && (index.column() - 1 < m_dataModel
->modelCount()))
2141 doUpdateLatestModel(index.column() - 1);
2144void MainWindow::doUpdateLatestModel(
int model)
2148 bool enable =
false;
2149 bool enableRw =
false;
2158 m_phraseView->setSourceText(model, item->text());
2160 m_phraseView->setSourceText(-1, QString());
2162 m_phraseView->setSourceText(-1, QString());
2166 updateSourceView(model, item);
2167 m_ui.actionSave->setEnabled(enableRw);
2168 m_ui.actionSaveAs->setEnabled(enableRw);
2169 m_ui.actionRelease->setEnabled(enableRw);
2170 m_ui.actionReleaseAs->setEnabled(enableRw);
2171 m_ui.actionClose->setEnabled(enable);
2172 m_ui.actionTranslationFileSettings->setEnabled(enableRw);
2173 m_ui.actionSearchAndTranslate->setEnabled(enableRw);
2175 updatePhraseBookActions();
2181 if (item && !item->fileName().isEmpty()) {
2182 if (hasUiFormPreview(item->fileName())) {
2183 m_sourceAndFormView->setCurrentWidget(m_uiFormPreviewView);
2186 }
else if (hasQmlFormPreview(item->fileName(), m_ui.actionQmlPreview->isChecked())
2188 m_sourceAndFormView->setCurrentWidget(m_qmlFormPreviewView);
2192 m_ui.actionQmlPreview->setChecked(
false);
2194 m_sourceAndFormView->setCurrentWidget(m_sourceCodeView);
2195 QDir dir = QFileInfo(m_dataModel->srcFileName(model)).dir();
2196 QString fileName = QDir::cleanPath(dir.absoluteFilePath(item->fileName()));
2197 m_sourceCodeView->setSourceContext(fileName, item
->lineNumber());
2200 m_sourceAndFormView->setCurrentWidget(m_sourceCodeView);
2201 m_sourceCodeView->setSourceContext(QString(), 0);
2210 if (m_fileActiveModel != m_currentIndex
.model()) {
2215 QString fn = QFileInfo(m_dataModel->srcFileName(m_currentIndex.model())).baseName();
2217 m_ui.actionSave->setText(tr(
"&Save '%1'").arg(fn));
2218 m_ui.actionSaveAs->setText(tr(
"Save '%1' &As...").arg(fn));
2220 m_ui.actionSave->setText(tr(
"&Download '%1'").arg(fn));
2222 m_ui.actionRelease->setText(tr(
"Release '%1'").arg(fn));
2223 m_ui.actionReleaseAs->setText(tr(
"Release '%1' As...").arg(fn));
2224 m_ui.actionClose->setText(tr(
"&Close '%1'").arg(fn));
2227 m_ui.actionSave->setText(tr(
"&Save"));
2228 m_ui.actionSaveAs->setText(tr(
"Save &As..."));
2230 m_ui.actionSave->setText(tr(
"&Download"));
2232 m_ui.actionRelease->setText(tr(
"Release"));
2233 m_ui.actionReleaseAs->setText(tr(
"Release As..."));
2234 m_ui.actionClose->setText(tr(
"&Close"));
2238 m_ui.actionSaveAll->setText(tr(
"Save All"));
2240 m_ui.actionReleaseAll->setText(tr(
"&Release All"));
2241 m_ui.actionCloseAll->setText(tr(
"Close All"));
2245 m_ui.actionSaveAs->setText(tr(
"Save &As..."));
2246 m_ui.actionSaveAll->setText(tr(
"&Save"));
2248 m_ui.actionSave->setText(tr(
"&Download"));
2250 m_ui.actionReleaseAs->setText(tr(
"Release As..."));
2251 m_ui.actionReleaseAll->setText(tr(
"&Release"));
2252 m_ui.actionCloseAll->setText(tr(
"&Close"));
2256 m_ui.actionSave->setVisible(en);
2258 m_ui.actionRelease->setVisible(en);
2259 m_ui.actionClose->setVisible(en);
2260 m_fileActiveModel = m_currentIndex
.model();
2266 if (m_editActiveModel != m_currentIndex
.model()) {
2268 QString fn = QFileInfo(m_dataModel->srcFileName(m_currentIndex.model())).baseName();
2269 m_ui.actionTranslationFileSettings->setText(tr(
"Translation File &Settings for '%1'...").arg(fn));
2270 m_ui.actionBatchTranslation->setText(tr(
"&Batch Translation of '%1'...").arg(fn));
2271 m_ui.actionSearchAndTranslate->setText(tr(
"Search And &Translate in '%1'...").arg(fn));
2273 m_ui.actionTranslationFileSettings->setText(tr(
"Translation File &Settings..."));
2274 m_ui.actionBatchTranslation->setText(tr(
"&Batch Translation..."));
2275 m_ui.actionSearchAndTranslate->setText(tr(
"Search And &Translate..."));
2277 m_editActiveModel = m_currentIndex
.model();
2283 m_contextAndLabelDock->show();
2284 m_contextAndLabelDock->raise();
2289 m_messagesDock->show();
2290 m_messagesDock->raise();
2295 m_phrasesDock->show();
2296 m_phrasesDock->raise();
2301 m_sourceAndFormDock->show();
2302 m_sourceAndFormDock->raise();
2307 m_errorsDock->show();
2308 m_errorsDock->raise();
2313 QWhatsThis::enterWhatsThisMode();
2318 QToolBar *filet =
new QToolBar(
this);
2319 filet->setObjectName(
"FileToolbar");
2320 filet->setWindowTitle(tr(
"File"));
2321 this->addToolBar(filet);
2322 m_ui.menuToolbars->addAction(filet->toggleViewAction());
2324 QToolBar *editt =
new QToolBar(
this);
2325 editt->setVisible(
false);
2326 editt->setObjectName(
"EditToolbar");
2327 editt->setWindowTitle(tr(
"Edit"));
2328 this->addToolBar(editt);
2329 m_ui.menuToolbars->addAction(editt->toggleViewAction());
2331 QToolBar *translationst =
new QToolBar(
this);
2332 translationst->setObjectName(
"TranslationToolbar");
2333 translationst->setWindowTitle(tr(
"Translation"));
2334 this->addToolBar(translationst);
2335 m_ui.menuToolbars->addAction(translationst->toggleViewAction());
2337 QToolBar *validationt =
new QToolBar(
this);
2338 validationt->setObjectName(
"ValidationToolbar");
2339 validationt->setWindowTitle(tr(
"Validation"));
2340 this->addToolBar(validationt);
2341 m_ui.menuToolbars->addAction(validationt->toggleViewAction());
2343 QToolBar *helpt =
new QToolBar(
this);
2344 helpt->setVisible(
false);
2345 helpt->setObjectName(
"HelpToolbar");
2346 helpt->setWindowTitle(tr(
"Help"));
2347 this->addToolBar(helpt);
2348 m_ui.menuToolbars->addAction(helpt->toggleViewAction());
2351 filet->addAction(m_ui.actionOpen);
2352 filet->addAction(m_ui.actionSaveAll);
2353 filet->addAction(m_ui.actionPrint);
2354 filet->addSeparator();
2355 filet->addAction(m_ui.actionOpenPhraseBook);
2357 editt->addAction(m_ui.actionUndo);
2358 editt->addAction(m_ui.actionRedo);
2359 editt->addSeparator();
2360 editt->addAction(m_ui.actionCut);
2361 editt->addAction(m_ui.actionCopy);
2362 editt->addAction(m_ui.actionPaste);
2363 editt->addSeparator();
2364 editt->addAction(m_ui.actionFind);
2366 translationst->addAction(m_ui.actionPrev);
2367 translationst->addAction(m_ui.actionNext);
2368 translationst->addAction(m_ui.actionPrevUnfinished);
2369 translationst->addAction(m_ui.actionNextUnfinished);
2370 translationst->addAction(m_ui.actionDone);
2371 translationst->addAction(m_ui.actionDoneAndNext);
2373 validationt->addAction(m_ui.actionAccelerators);
2374 validationt->addAction(m_ui.actionSurroundingWhitespace);
2375 validationt->addAction(m_ui.actionEndingPunctuation);
2376 validationt->addAction(m_ui.actionPhraseMatches);
2377 validationt->addAction(m_ui.actionPlaceMarkerMatches);
2379 helpt->addAction(m_ui.actionWhatsThis);
2382QModelIndex
MainWindow::setMessageViewRoot(
const QModelIndex &index)
2384 const QModelIndex &sortedGroupIndex = m_activeSortedMessagesModel->mapFromSource(index);
2385 const QModelIndex &trueGroupIndex =
2386 m_activeSortedMessagesModel->index(sortedGroupIndex.row(), 0);
2387 if (m_messageView->rootIndex() != trueGroupIndex)
2388 m_messageView->setRootIndex(trueGroupIndex);
2389 return trueGroupIndex;
2393
2394
2395void MainWindow::setCurrentMessage(
const QModelIndex &index)
2397 const QModelIndex &groupIndex = m_activeMessageModel->parent(index);
2398 if (!groupIndex.isValid())
2401 const QModelIndex &trueIndex =
2402 m_activeMessageModel->index(groupIndex.row(), index.column(), QModelIndex());
2403 m_settingCurrentMessage =
true;
2404 QTreeView *view = *m_activeTranslationType == IDBASED ? m_labelView : m_contextView;
2405 view->setCurrentIndex(m_activeSortedGroupsModel->mapFromSource(trueIndex));
2406 m_settingCurrentMessage =
false;
2407 setMessageViewRoot(groupIndex);
2408 m_messageView->setCurrentIndex(m_activeSortedMessagesModel->mapFromSource(index));
2411void MainWindow::setCurrentMessage(
const QModelIndex &index,
int model)
2413 const QModelIndex &theIndex =
2414 m_activeMessageModel->index(index.row(), model + 1, index.parent());
2415 setCurrentMessage(theIndex);
2421 if (cand.context.isEmpty()) {
2422 int labelIndex = m_dataModel->findGroupIndex(cand.label,
IDBASED);
2425 setCurrentMessage(m_activeMessageModel->modelIndex(
2426 MultiDataIndex(IDBASED, modelIndex, labelIndex, messageIndex)));
2428 int contextIndex = m_dataModel->findGroupIndex(cand.context,
TEXTBASED);
2430 ->findMessage(cand.source, cand.disambiguation);
2431 setCurrentMessage(m_activeMessageModel->modelIndex(
2432 MultiDataIndex(TEXTBASED, modelIndex, contextIndex, messageIndex)));
2438 auto refreshMessageView = [
this](QTreeView *view) {
2439 m_messageView->reset();
2440 m_messageView->setModel(m_activeSortedMessagesModel);
2441 view->setCurrentIndex(m_activeSortedGroupsModel->index(0, 0));
2442 connect(m_messageView->selectionModel(), &QItemSelectionModel::currentRowChanged,
this,
2443 &MainWindow::selectedMessageChanged);
2444 connect(m_messageView->selectionModel(), &QItemSelectionModel::currentColumnChanged,
this,
2445 &MainWindow::updateLatestModel);
2446 m_messageView->update();
2447 if (m_activeMessageModel->rowCount())
2448 setCurrentMessage(m_activeMessageModel->modelIndex(
2449 MultiDataIndex(*m_activeTranslationType, 0, 0, 0)));
2450 selectedMessageChanged(m_messageView->currentIndex(), QModelIndex{});
2451 updateVisibleColumns();
2454 if (m_contextAndLabelView->currentWidget() == m_labelView
2455 && (!m_activeTranslationType || *m_activeTranslationType != IDBASED)) {
2456 m_activeTranslationType.emplace(IDBASED);
2457 m_activeSortedMessagesModel = m_sortedIdBasedMessagesModel;
2458 m_activeSortedGroupsModel = m_sortedLabelsModel;
2459 m_activeMessageModel = m_idBasedMessageModel;
2460 refreshMessageView(m_labelView);
2461 }
else if (m_contextAndLabelView->currentWidget() == m_contextView
2462 && (!m_activeTranslationType || *m_activeTranslationType != TEXTBASED)) {
2463 m_activeTranslationType.emplace(TEXTBASED);
2464 m_activeSortedMessagesModel = m_sortedTextBasedMessagesModel;
2465 m_activeSortedGroupsModel = m_sortedContextsModel;
2466 m_activeMessageModel = m_textBasedMessageModel;
2467 refreshMessageView(m_contextView);
2474 if (*m_activeTranslationType == IDBASED)
2476 for (
int i = 1; i < cols; i++)
2477 m_messageView->setColumnHidden(i,
false);
2478 for (
int i = cols; i < m_messageView->header()->count(); i++)
2479 m_messageView->setColumnHidden(i,
true);
2480 m_messageView->header()->setStretchLastSection(
true);
2483QModelIndex
MainWindow::currentMessageIndex()
const
2485 return m_activeSortedMessagesModel->mapToSource(m_messageView->currentIndex());
2492 if (!pb->load(name, &langGuessed)) {
2493 QMessageBox::warning(
this, tr(
"Qt Linguist"),
2494 tr(
"Cannot read from phrase book '%1'.").arg(name));
2499 if (!m_translationSettingsDialog)
2500 m_translationSettingsDialog =
new TranslationSettingsDialog(
this);
2502 m_translationSettingsDialog->exec();
2505 m_phraseBooks.append(pb);
2507 QAction *a = m_ui.menuClosePhraseBook->addAction(pb->friendlyPhraseBookName());
2508 m_phraseBookMenu[PhraseCloseMenu].insert(a, pb);
2509 a->setToolTip(tr(
"Close this phrase book."));
2511 a = m_ui.menuEditPhraseBook->addAction(pb->friendlyPhraseBookName());
2512 m_phraseBookMenu[PhraseEditMenu].insert(a, pb);
2513 a->setToolTip(tr(
"Enables you to add, modify, or delete"
2514 " entries in this phrase book."));
2516 a = m_ui.menuPrintPhraseBook->addAction(pb->friendlyPhraseBookName());
2517 m_phraseBookMenu[PhrasePrintMenu].insert(a, pb);
2518 a->setToolTip(tr(
"Print the entries in this phrase book."));
2521 updatePhraseDicts();
2522 updatePhraseBookActions();
2529 if (!name->contains(u'.'))
2532 if (!pb.save(*name)) {
2533 QMessageBox::warning(
this, tr(
"Qt Linguist"),
2534 tr(
"Cannot create phrase book '%1'.").arg(*name));
2543 switch (QMessageBox::information(
this, tr(
"Qt Linguist"),
2544 tr(
"Do you want to save phrase book '%1'?").arg(pb->friendlyPhraseBookName()),
2545 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes))
2547 case QMessageBox::Cancel:
2549 case QMessageBox::Yes:
2550 if (!pb->save(pb->fileName()))
2561 for (PhraseBook *phraseBook : std::as_const(m_phraseBooks))
2562 if (!maybeSavePhraseBook(phraseBook))
2572 m_progressLabel->setText(QString(
" "_L1));
2573 m_progressLabel->setToolTip(QString());
2575 m_progressLabel->setText(QStringLiteral(
" %1/%2 ").arg(numFinished).arg(numEditable));
2576 m_progressLabel->setToolTip(tr(
"%n unfinished message(s) left.", 0,
2577 numEditable - numFinished));
2579 bool enable = numFinished != numEditable;
2580 m_ui.actionPrevUnfinished->setEnabled(enable);
2581 m_ui.actionNextUnfinished->setEnabled(enable);
2582 m_ui.actionDone->setEnabled(enable);
2583 m_ui.actionDoneAndNext->setEnabled(enable);
2585 m_ui.actionPrev->setEnabled(m_dataModel->contextCount() > 0 || m_dataModel->labelCount() > 0);
2586 m_ui.actionNext->setEnabled(m_dataModel->contextCount() > 0 || m_dataModel->labelCount() > 0);
2591 bool phraseBookLoaded = (m_currentIndex.model() >= 0) && !m_phraseBooks.isEmpty();
2592 m_ui.actionBatchTranslation->setEnabled(m_dataModel->contextCount() > 0 && phraseBookLoaded
2593 && m_dataModel->isModelWritable(m_currentIndex.model()));
2594 m_ui.actionAddToPhraseBook->setEnabled(currentMessageIndex().isValid() && phraseBookLoaded);
2597void MainWindow::updatePhraseDictInternal(
int model)
2599 QHash<QString, QList<Phrase *> > &pd = m_phraseDict[model];
2602 for (PhraseBook *pb : std::as_const(m_phraseBooks)) {
2604 if (pb->language() != QLocale::C && m_dataModel->language(model) != QLocale::C) {
2605 if (pb->language() != m_dataModel->language(model))
2607 before = (pb->territory() == m_dataModel->model(model)->territory());
2611 const auto phrases = pb->phrases();
2612 for (Phrase *p : phrases) {
2613 QString f = friendlyString(p->source());
2615 f = f.split(u' ').first();
2616 if (!pd.contains(f)) {
2617 pd.insert(f, QList<Phrase *>());
2630 updatePhraseDictInternal(model);
2636 for (
int i = 0; i < m_phraseDict.size(); ++i)
2638 m_phraseDict[i].clear();
2640 updatePhraseDictInternal(i);
2652 Validator::Checks checks{ m_ui.actionAccelerators->isChecked(),
2653 m_ui.actionEndingPunctuation->isChecked(),
2654 m_ui.actionPlaceMarkerMatches->isChecked(),
2655 m_ui.actionSurroundingWhitespace->isChecked(),
2656 m_ui.actionPhraseMatches->isChecked() };
2666 bool danger =
false;
2668 if (source.isEmpty()) {
2669 source = m->pluralText();
2670 if (source.isEmpty())
2674 Validator validator = Validator::fromSource(
2675 source, checks, m_dataModel->sourceLanguage(mi), m_phraseDict[mi]);
2677 validator.validate(m->translations(), m
->message(), m_dataModel->language(mi),
2678 m_dataModel
->model(mi
)->countRefNeeds());
2680 for (
const auto &[error, message] : errors.asKeyValueRange())
2681 m_errorsView->addError(mi, error, message);
2689 statusBar()->showMessage(m_errorsView->firstError());
2696 restoreGeometry(config.value(settingPath(
"Geometry/WindowGeometry")).toByteArray());
2697 restoreState(config.value(settingPath(
"MainWindowState")).toByteArray());
2699 m_ui.actionAccelerators->setChecked(
2700 config.value(settingPath(
"Validators/Accelerator"),
true).toBool());
2701 m_ui.actionSurroundingWhitespace->setChecked(
2702 config.value(settingPath(
"Validators/SurroundingWhitespace"),
true).toBool());
2703 m_ui.actionEndingPunctuation->setChecked(
2704 config.value(settingPath(
"Validators/EndingPunctuation"),
true).toBool());
2705 m_ui.actionPhraseMatches->setChecked(
2706 config.value(settingPath(
"Validators/PhraseMatch"),
true).toBool());
2707 m_ui.actionPlaceMarkerMatches->setChecked(
2708 config.value(settingPath(
"Validators/PlaceMarkers"),
true).toBool());
2709 m_ui.actionLengthVariants->setChecked(
2710 config.value(settingPath(
"Options/LengthVariants"),
false).toBool());
2711 m_ui.actionVisualizeWhitespace->setChecked(
2712 config.value(settingPath(
"Options/VisualizeWhitespace"),
true).toBool());
2715 config.value(settingPath(
"Options/EditorFontsize"), font().pointSize()).toReal()
);
2719 m_recentFiles.readConfig();
2721 int size = config.beginReadArray(settingPath(
"OpenedPhraseBooks"));
2722 for (
int i = 0; i < size; ++i) {
2723 config.setArrayIndex(i);
2724 doOpenPhraseBook(config.value(
"FileName"_L1).toString());
2732 config.setValue(settingPath(
"Geometry/WindowGeometry"),
2734 config.setValue(settingPath(
"Validators/Accelerator"),
2735 m_ui.actionAccelerators->isChecked());
2736 config.setValue(settingPath(
"Validators/SurroundingWhitespace"),
2737 m_ui.actionSurroundingWhitespace->isChecked());
2738 config.setValue(settingPath(
"Validators/EndingPunctuation"),
2739 m_ui.actionEndingPunctuation->isChecked());
2740 config.setValue(settingPath(
"Validators/PhraseMatch"),
2741 m_ui.actionPhraseMatches->isChecked());
2742 config.setValue(settingPath(
"Validators/PlaceMarkers"),
2743 m_ui.actionPlaceMarkerMatches->isChecked());
2744 config.setValue(settingPath(
"Options/LengthVariants"),
2745 m_ui.actionLengthVariants->isChecked());
2746 config.setValue(settingPath(
"Options/VisualizeWhitespace"),
2747 m_ui.actionVisualizeWhitespace->isChecked());
2748 config.setValue(settingPath(
"MainWindowState"),
2750 m_recentFiles.writeConfig();
2752 config.setValue(settingPath(
"Options/EditorFontsize"), m_messageEditor
->fontSize());
2755 config.beginWriteArray(settingPath(
"OpenedPhraseBooks"),
2756 m_phraseBooks.size());
2757 for (
int i = 0; i < m_phraseBooks.size(); ++i) {
2758 config.setArrayIndex(i);
2759 config.setValue(
"FileName"_L1, m_phraseBooks.at(i)->fileName());
2766 m_ui.menuRecentlyOpenedFiles->clear();
2767 for (
const QStringList &strList : m_recentFiles.filesLists())
2768 if (strList.size() == 1) {
2769 const QString &str = strList.first();
2770 m_ui.menuRecentlyOpenedFiles->addAction(
2771 DataModel::prettifyFileName(str))->setData(str);
2773 QMenu *menu = m_ui.menuRecentlyOpenedFiles->addMenu(
2774 MultiDataModel::condenseFileNames(
2775 MultiDataModel::prettifyFileNames(strList)));
2776 menu->addAction(tr(
"All"))->setData(strList);
2777 for (
const QString &str : strList)
2778 menu->addAction(DataModel::prettifyFileName(str))->setData(str);
2782void MainWindow::recentFileActivated(QAction *action)
2784 openFiles(action->data().toStringList());
2789 if (!m_statistics) {
2790 m_statistics =
new Statistics(
this);
2793 m_statistics->show();
2800 if (m_ui.actionQmlPreview->isChecked())
2801 m_sourceAndFormView->setCurrentWidget(m_qmlFormPreviewView);
2803 m_sourceAndFormView->setCurrentWidget(m_sourceCodeView);
2822 if (!m_statistics || !m_statistics->isVisible() || m_currentIndex
.model() < 0)
2828void MainWindow::doShowTranslationSettings(
int model)
2830 if (!m_translationSettingsDialog)
2831 m_translationSettingsDialog =
new TranslationSettingsDialog(
this);
2833 m_translationSettingsDialog->exec();
2838 doShowTranslationSettings(m_currentIndex
.model());
2843 if (event->type() == QEvent::DragEnter) {
2844 QDragEnterEvent *e =
static_cast<QDragEnterEvent*>(event);
2845 if (e->mimeData()->hasFormat(
"text/uri-list"_L1)) {
2846 e->acceptProposedAction();
2849 }
else if (event->type() == QEvent::Drop) {
2850 QDropEvent *e =
static_cast<QDropEvent*>(event);
2851 if (!e->mimeData()->hasFormat(
"text/uri-list"_L1))
2854 const auto &qurls = e->mimeData()->urls();
2855 for (
const QUrl &url : qurls)
2856 if (!url.toLocalFile().isEmpty())
2857 urls << url.toLocalFile();
2858 if (!urls.isEmpty())
2860 e->acceptProposedAction();
2862 }
else if (event->type() == QEvent::KeyPress) {
2863 QKeyEvent *ke =
static_cast<QKeyEvent *>(event);
2864 if (ke->key() == Qt::Key_Escape) {
2865 if (object == m_messageEditor)
2866 m_messageView->setFocus();
2867 else if (object == m_messagesDock)
2868 m_contextAndLabelView->currentWidget()->setFocus();
2869 }
else if ((ke->key() == Qt::Key_Plus || ke->key() == Qt::Key_Equal)
2870 && (ke->modifiers() & Qt::ControlModifier)) {
2872 }
else if (ke->key() == Qt::Key_Minus
2873 && (ke->modifiers() & Qt::ControlModifier)) {
2876 }
else if (event->type() == QEvent::Wheel) {
2877 QWheelEvent *we =
static_cast<QWheelEvent *>(event);
2878 if (we->modifiers() & Qt::ControlModifier) {
2879 if (we->angleDelta().y() > 0)
2884 }
else if (event->type() == QEvent::ApplicationPaletteChange) {
2888 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 QString fileFilters(bool allFirst)
static const QVariant & pxObsolete()
int firstNonobsoleteMessageIndex(int msgIdx) const
bool isUnfinished() const
OpenedFile(DataModel *_dataModel, bool _readWrite, bool _langGuessed)