| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the Qt Linguist of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** This file may be used under the terms of the GNU Lesser General Public |
| ** License version 2.1 as published by the Free Software Foundation and |
| ** appearing in the file LICENSE.LGPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU Lesser |
| ** General Public License version 2.1 requirements will be met: |
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| ** |
| ** In addition, as a special exception, Nokia gives you certain additional |
| ** rights. These rights are described in the Nokia Qt LGPL Exception |
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| /* TRANSLATOR MainWindow |
| |
| This is the application's main window. |
| */ |
| |
| #include "mainwindow.h" |
| |
| #include "batchtranslationdialog.h" |
| #include "errorsview.h" |
| #include "finddialog.h" |
| #include "formpreviewview.h" |
| #include "globals.h" |
| #include "messageeditor.h" |
| #include "messagemodel.h" |
| #include "phrasebookbox.h" |
| #include "phrasemodel.h" |
| #include "phraseview.h" |
| #include "printout.h" |
| #include "sourcecodeview.h" |
| #include "statistics.h" |
| #include "translatedialog.h" |
| #include "translationsettingsdialog.h" |
| |
| #include <QAction> |
| #include <QApplication> |
| #include <QBitmap> |
| #include <QCloseEvent> |
| #include <QDebug> |
| #include <QDesktopWidget> |
| #include <QDockWidget> |
| #include <QFile> |
| #include <QFileDialog> |
| #include <QFileInfo> |
| #include <QHeaderView> |
| #include <QInputDialog> |
| #include <QItemDelegate> |
| #include <QLabel> |
| #include <QLayout> |
| #include <QLibraryInfo> |
| #include <QMenu> |
| #include <QMenuBar> |
| #include <QMessageBox> |
| #include <QPrintDialog> |
| #include <QPrinter> |
| #include <QProcess> |
| #include <QRegExp> |
| #include <QSettings> |
| #include <QSortFilterProxyModel> |
| #include <QStackedWidget> |
| #include <QStatusBar> |
| #include <QTextStream> |
| #include <QToolBar> |
| #include <QUrl> |
| #include <QWhatsThis> |
| |
| #include <ctype.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| static const int MessageMS = 2500; |
| |
| enum Ending { |
| End_None, |
| End_FullStop, |
| End_Interrobang, |
| End_Colon, |
| End_Ellipsis |
| }; |
| |
| static bool hasFormPreview(const QString &fileName) |
| { |
| return fileName.endsWith(QLatin1String(".ui")) |
| || fileName.endsWith(QLatin1String(".jui")); |
| } |
| |
| static Ending ending(QString str, QLocale::Language lang) |
| { |
| str = str.simplified(); |
| if (str.isEmpty()) |
| return End_None; |
| |
| switch (str.at(str.length() - 1).unicode()) { |
| case 0x002e: // full stop |
| if (str.endsWith(QLatin1String("..."))) |
| return End_Ellipsis; |
| else |
| return End_FullStop; |
| case 0x0589: // armenian full stop |
| case 0x06d4: // arabic full stop |
| case 0x3002: // ideographic full stop |
| return End_FullStop; |
| case 0x0021: // exclamation mark |
| case 0x003f: // question mark |
| case 0x00a1: // inverted exclamation mark |
| case 0x00bf: // inverted question mark |
| case 0x01c3: // latin letter retroflex click |
| case 0x037e: // greek question mark |
| case 0x061f: // arabic question mark |
| case 0x203c: // double exclamation mark |
| case 0x203d: // interrobang |
| case 0x2048: // question exclamation mark |
| case 0x2049: // exclamation question mark |
| case 0x2762: // heavy exclamation mark ornament |
| case 0xff01: // full width exclamation mark |
| case 0xff1f: // full width question mark |
| return End_Interrobang; |
| case 0x003b: // greek 'compatibility' questionmark |
| return lang == QLocale::Greek ? End_Interrobang : End_None; |
| case 0x003a: // colon |
| case 0xff1a: // full width colon |
| return End_Colon; |
| case 0x2026: // horizontal ellipsis |
| return End_Ellipsis; |
| default: |
| return End_None; |
| } |
| } |
| |
| |
| class ContextItemDelegate : public QItemDelegate |
| { |
| public: |
| ContextItemDelegate(QObject *parent, MultiDataModel *model) : QItemDelegate(parent), m_dataModel(model) {} |
| |
| void paint(QPainter *painter, const QStyleOptionViewItem &option, |
| const QModelIndex &index) const |
| { |
| const QAbstractItemModel *model = index.model(); |
| Q_ASSERT(model); |
| |
| if (!model->parent(index).isValid()) { |
| if (index.column() - 1 == m_dataModel->modelCount()) { |
| QStyleOptionViewItem opt = option; |
| opt.font.setBold(true); |
| QItemDelegate::paint(painter, opt, index); |
| return; |
| } |
| } |
| QItemDelegate::paint(painter, option, index); |
| } |
| |
| private: |
| MultiDataModel *m_dataModel; |
| }; |
| |
| static const QVariant &pxObsolete() |
| { |
| static const QVariant v = |
| qVariantFromValue(QPixmap(QLatin1String(":/images/s_check_obsolete.png"))); |
| return v; |
| } |
| |
| |
| class SortedMessagesModel : public QSortFilterProxyModel |
| { |
| public: |
| SortedMessagesModel(QObject *parent, MultiDataModel *model) : QSortFilterProxyModel(parent), m_dataModel(model) {} |
| |
| QVariant headerData(int section, Qt::Orientation orientation, int role) const |
| { |
| if (role == Qt::DisplayRole && orientation == Qt::Horizontal) |
| switch (section - m_dataModel->modelCount()) { |
| case 0: return QString(); |
| case 1: return MainWindow::tr("Source text"); |
| case 2: return MainWindow::tr("Index"); |
| } |
| |
| if (role == Qt::DecorationRole && orientation == Qt::Horizontal && section - 1 < m_dataModel->modelCount()) |
| return pxObsolete(); |
| |
| return QVariant(); |
| } |
| |
| private: |
| MultiDataModel *m_dataModel; |
| }; |
| |
| class SortedContextsModel : public QSortFilterProxyModel |
| { |
| public: |
| SortedContextsModel(QObject *parent, MultiDataModel *model) : QSortFilterProxyModel(parent), m_dataModel(model) {} |
| |
| QVariant headerData(int section, Qt::Orientation orientation, int role) const |
| { |
| if (role == Qt::DisplayRole && orientation == Qt::Horizontal) |
| switch (section - m_dataModel->modelCount()) { |
| case 0: return QString(); |
| case 1: return MainWindow::tr("Context"); |
| case 2: return MainWindow::tr("Items"); |
| case 3: return MainWindow::tr("Index"); |
| } |
| |
| if (role == Qt::DecorationRole && orientation == Qt::Horizontal && section - 1 < m_dataModel->modelCount()) |
| return pxObsolete(); |
| |
| return QVariant(); |
| } |
| |
| private: |
| MultiDataModel *m_dataModel; |
| }; |
| |
| class FocusWatcher : public QObject |
| { |
| public: |
| FocusWatcher(MessageEditor *msgedit, QObject *parent) : QObject(parent), m_messageEditor(msgedit) {} |
| |
| protected: |
| bool eventFilter(QObject *object, QEvent *event); |
| |
| private: |
| MessageEditor *m_messageEditor; |
| }; |
| |
| bool FocusWatcher::eventFilter(QObject *, QEvent *event) |
| { |
| if (event->type() == QEvent::FocusIn) |
| m_messageEditor->setEditorFocus(-1); |
| return false; |
| } |
| |
| MainWindow::MainWindow() |
| : QMainWindow(0, Qt::Window), |
| m_assistantProcess(0), |
| m_printer(0), |
| m_findMatchCase(Qt::CaseInsensitive), |
| m_findIgnoreAccelerators(true), |
| m_findWhere(DataModel::NoLocation), |
| m_foundWhere(DataModel::NoLocation), |
| m_translationSettingsDialog(0), |
| m_settingCurrentMessage(false), |
| m_fileActiveModel(-1), |
| m_editActiveModel(-1), |
| m_statistics(0) |
| { |
| setUnifiedTitleAndToolBarOnMac(true); |
| m_ui.setupUi(this); |
| |
| #ifndef Q_WS_MAC |
| setWindowIcon(QPixmap(QLatin1String(":/images/appicon.png") )); |
| #endif |
| |
| m_dataModel = new MultiDataModel(this); |
| m_messageModel = new MessageModel(this, m_dataModel); |
| |
| // Set up the context dock widget |
| m_contextDock = new QDockWidget(this); |
| m_contextDock->setObjectName(QLatin1String("ContextDockWidget")); |
| m_contextDock->setAllowedAreas(Qt::AllDockWidgetAreas); |
| m_contextDock->setFeatures(QDockWidget::AllDockWidgetFeatures); |
| m_contextDock->setWindowTitle(tr("Context")); |
| m_contextDock->setAcceptDrops(true); |
| m_contextDock->installEventFilter(this); |
| |
| m_sortedContextsModel = new SortedContextsModel(this, m_dataModel); |
| m_sortedContextsModel->setSortRole(MessageModel::SortRole); |
| m_sortedContextsModel->setSortCaseSensitivity(Qt::CaseInsensitive); |
| m_sortedContextsModel->setSourceModel(m_messageModel); |
| |
| m_contextView = new QTreeView(this); |
| m_contextView->setRootIsDecorated(false); |
| m_contextView->setItemsExpandable(false); |
| m_contextView->setUniformRowHeights(true); |
| m_contextView->setAlternatingRowColors(true); |
| m_contextView->setAllColumnsShowFocus(true); |
| m_contextView->setItemDelegate(new ContextItemDelegate(this, m_dataModel)); |
| m_contextView->setSortingEnabled(true); |
| m_contextView->setWhatsThis(tr("This panel lists the source contexts.")); |
| m_contextView->setModel(m_sortedContextsModel); |
| m_contextView->header()->setMovable(false); |
| m_contextView->setColumnHidden(0, true); |
| m_contextView->header()->setStretchLastSection(false); |
| |
| m_contextDock->setWidget(m_contextView); |
| |
| // Set up the messages dock widget |
| m_messagesDock = new QDockWidget(this); |
| m_messagesDock->setObjectName(QLatin1String("StringsDockWidget")); |
| m_messagesDock->setAllowedAreas(Qt::AllDockWidgetAreas); |
| m_messagesDock->setFeatures(QDockWidget::AllDockWidgetFeatures); |
| m_messagesDock->setWindowTitle(tr("Strings")); |
| m_messagesDock->setAcceptDrops(true); |
| m_messagesDock->installEventFilter(this); |
| |
| m_sortedMessagesModel = new SortedMessagesModel(this, m_dataModel); |
| m_sortedMessagesModel->setSortRole(MessageModel::SortRole); |
| m_sortedMessagesModel->setSortCaseSensitivity(Qt::CaseInsensitive); |
| m_sortedMessagesModel->setSortLocaleAware(true); |
| m_sortedMessagesModel->setSourceModel(m_messageModel); |
| |
| m_messageView = new QTreeView(m_messagesDock); |
| m_messageView->setSortingEnabled(true); |
| m_messageView->setRootIsDecorated(false); |
| m_messageView->setUniformRowHeights(true); |
| m_messageView->setAllColumnsShowFocus(true); |
| m_messageView->setItemsExpandable(false); |
| m_messageView->setModel(m_sortedMessagesModel); |
| m_messageView->header()->setMovable(false); |
| m_messageView->setColumnHidden(0, true); |
| |
| m_messagesDock->setWidget(m_messageView); |
| |
| // Set up main message view |
| m_messageEditor = new MessageEditor(m_dataModel, this); |
| m_messageEditor->setAcceptDrops(true); |
| m_messageEditor->installEventFilter(this); |
| // We can't call setCentralWidget(m_messageEditor), since it is already called in m_ui.setupUi() |
| QBoxLayout *lout = new QBoxLayout(QBoxLayout::TopToBottom, m_ui.centralwidget); |
| lout->addWidget(m_messageEditor); |
| lout->setMargin(0); |
| m_ui.centralwidget->setLayout(lout); |
| |
| // Set up the phrases & guesses dock widget |
| m_phrasesDock = new QDockWidget(this); |
| m_phrasesDock->setObjectName(QLatin1String("PhrasesDockwidget")); |
| m_phrasesDock->setAllowedAreas(Qt::AllDockWidgetAreas); |
| m_phrasesDock->setFeatures(QDockWidget::AllDockWidgetFeatures); |
| m_phrasesDock->setWindowTitle(tr("Phrases and guesses")); |
| |
| m_phraseView = new PhraseView(m_dataModel, &m_phraseDict, this); |
| m_phrasesDock->setWidget(m_phraseView); |
| |
| // Set up source code and form preview dock widget |
| m_sourceAndFormDock = new QDockWidget(this); |
| m_sourceAndFormDock->setObjectName(QLatin1String("SourceAndFormDock")); |
| m_sourceAndFormDock->setAllowedAreas(Qt::AllDockWidgetAreas); |
| m_sourceAndFormDock->setFeatures(QDockWidget::AllDockWidgetFeatures); |
| m_sourceAndFormDock->setWindowTitle(tr("Sources and Forms")); |
| m_sourceAndFormView = new QStackedWidget(this); |
| m_sourceAndFormDock->setWidget(m_sourceAndFormView); |
| //connect(m_sourceAndDock, SIGNAL(visibilityChanged(bool)), |
| // m_sourceCodeView, SLOT(setActivated(bool))); |
| m_formPreviewView = new FormPreviewView(0, m_dataModel); |
| m_sourceCodeView = new SourceCodeView(0); |
| m_sourceAndFormView->addWidget(m_sourceCodeView); |
| m_sourceAndFormView->addWidget(m_formPreviewView); |
| |
| // Set up errors dock widget |
| m_errorsDock = new QDockWidget(this); |
| m_errorsDock->setObjectName(QLatin1String("ErrorsDockWidget")); |
| m_errorsDock->setAllowedAreas(Qt::AllDockWidgetAreas); |
| m_errorsDock->setFeatures(QDockWidget::AllDockWidgetFeatures); |
| m_errorsDock->setWindowTitle(tr("Warnings")); |
| m_errorsView = new ErrorsView(m_dataModel, this); |
| m_errorsDock->setWidget(m_errorsView); |
| |
| // Arrange dock widgets |
| setDockNestingEnabled(true); |
| setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea); |
| setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea); |
| setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); |
| setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); |
| addDockWidget(Qt::LeftDockWidgetArea, m_contextDock); |
| addDockWidget(Qt::TopDockWidgetArea, m_messagesDock); |
| addDockWidget(Qt::BottomDockWidgetArea, m_phrasesDock); |
| addDockWidget(Qt::TopDockWidgetArea, m_sourceAndFormDock); |
| addDockWidget(Qt::BottomDockWidgetArea, m_errorsDock); |
| //tabifyDockWidget(m_errorsDock, m_sourceAndFormDock); |
| //tabifyDockWidget(m_sourceCodeDock, m_phrasesDock); |
| |
| // Allow phrases doc to intercept guesses shortcuts |
| m_messageEditor->installEventFilter(m_phraseView); |
| |
| // Set up shortcuts for the dock widgets |
| QShortcut *contextShortcut = new QShortcut(QKeySequence(Qt::Key_F6), this); |
| connect(contextShortcut, SIGNAL(activated()), this, SLOT(showContextDock())); |
| QShortcut *messagesShortcut = new QShortcut(QKeySequence(Qt::Key_F7), this); |
| connect(messagesShortcut, SIGNAL(activated()), this, SLOT(showMessagesDock())); |
| QShortcut *errorsShortcut = new QShortcut(QKeySequence(Qt::Key_F8), this); |
| connect(errorsShortcut, SIGNAL(activated()), this, SLOT(showErrorDock())); |
| QShortcut *sourceCodeShortcut = new QShortcut(QKeySequence(Qt::Key_F9), this); |
| connect(sourceCodeShortcut, SIGNAL(activated()), this, SLOT(showSourceCodeDock())); |
| QShortcut *phrasesShortcut = new QShortcut(QKeySequence(Qt::Key_F10), this); |
| connect(phrasesShortcut, SIGNAL(activated()), this, SLOT(showPhrasesDock())); |
| |
| connect(m_phraseView, SIGNAL(phraseSelected(int,QString)), |
| m_messageEditor, SLOT(setTranslation(int,QString))); |
| connect(m_contextView->selectionModel(), |
| SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), |
| this, SLOT(selectedContextChanged(QModelIndex,QModelIndex))); |
| connect(m_messageView->selectionModel(), |
| SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), |
| this, SLOT(selectedMessageChanged(QModelIndex,QModelIndex))); |
| connect(m_contextView->selectionModel(), |
| SIGNAL(currentColumnChanged(QModelIndex,QModelIndex)), |
| SLOT(updateLatestModel(QModelIndex))); |
| connect(m_messageView->selectionModel(), |
| SIGNAL(currentColumnChanged(QModelIndex,QModelIndex)), |
| SLOT(updateLatestModel(QModelIndex))); |
| |
| connect(m_messageEditor, SIGNAL(activeModelChanged(int)), SLOT(updateActiveModel(int))); |
| |
| m_translateDialog = new TranslateDialog(this); |
| m_batchTranslateDialog = new BatchTranslationDialog(m_dataModel, this); |
| m_findDialog = new FindDialog(this); |
| |
| setupMenuBar(); |
| setupToolBars(); |
| |
| m_progressLabel = new QLabel(); |
| statusBar()->addPermanentWidget(m_progressLabel); |
| m_modifiedLabel = new QLabel(tr(" MOD ", "status bar: file(s) modified")); |
| statusBar()->addPermanentWidget(m_modifiedLabel); |
| |
| modelCountChanged(); |
| initViewHeaders(); |
| resetSorting(); |
| |
| connect(m_dataModel, SIGNAL(modifiedChanged(bool)), |
| this, SLOT(setWindowModified(bool))); |
| connect(m_dataModel, SIGNAL(modifiedChanged(bool)), |
| m_modifiedLabel, SLOT(setVisible(bool))); |
| connect(m_dataModel, SIGNAL(multiContextDataChanged(MultiDataIndex)), |
| SLOT(updateProgress())); |
| connect(m_dataModel, SIGNAL(messageDataChanged(MultiDataIndex)), |
| SLOT(maybeUpdateStatistics(MultiDataIndex))); |
| connect(m_dataModel, SIGNAL(translationChanged(MultiDataIndex)), |
| SLOT(translationChanged(MultiDataIndex))); |
| connect(m_dataModel, SIGNAL(languageChanged(int)), |
| SLOT(updatePhraseDict(int))); |
| |
| setWindowModified(m_dataModel->isModified()); |
| m_modifiedLabel->setVisible(m_dataModel->isModified()); |
| |
| connect(m_messageView, SIGNAL(clicked(QModelIndex)), |
| this, SLOT(toggleFinished(QModelIndex))); |
| connect(m_messageView, SIGNAL(activated(QModelIndex)), |
| m_messageEditor, SLOT(setEditorFocus())); |
| connect(m_contextView, SIGNAL(activated(QModelIndex)), |
| m_messageView, SLOT(setFocus())); |
| connect(m_messageEditor, SIGNAL(translationChanged(QStringList)), |
| this, SLOT(updateTranslation(QStringList))); |
| connect(m_messageEditor, SIGNAL(translatorCommentChanged(QString)), |
| this, SLOT(updateTranslatorComment(QString))); |
| connect(m_findDialog, SIGNAL(findNext(QString,DataModel::FindLocation,bool,bool)), |
| this, SLOT(findNext(QString,DataModel::FindLocation,bool,bool))); |
| connect(m_translateDialog, SIGNAL(requestMatchUpdate(bool&)), SLOT(updateTranslateHit(bool&))); |
| connect(m_translateDialog, SIGNAL(activated(int)), SLOT(translate(int))); |
| |
| QSize as(qApp->desktop()->size()); |
| as -= QSize(30, 30); |
| resize(QSize(1000, 800).boundedTo(as)); |
| show(); |
| readConfig(); |
| m_statistics = 0; |
| |
| connect(m_ui.actionLengthVariants, SIGNAL(toggled(bool)), |
| m_messageEditor, SLOT(setLengthVariants(bool))); |
| m_messageEditor->setLengthVariants(m_ui.actionLengthVariants->isChecked()); |
| |
| m_focusWatcher = new FocusWatcher(m_messageEditor, this); |
| m_contextView->installEventFilter(m_focusWatcher); |
| m_messageView->installEventFilter(m_focusWatcher); |
| m_messageEditor->installEventFilter(m_focusWatcher); |
| m_sourceAndFormView->installEventFilter(m_focusWatcher); |
| m_phraseView->installEventFilter(m_focusWatcher); |
| m_errorsView->installEventFilter(m_focusWatcher); |
| } |
| |
| MainWindow::~MainWindow() |
| { |
| writeConfig(); |
| if (m_assistantProcess && m_assistantProcess->state() == QProcess::Running) { |
| m_assistantProcess->terminate(); |
| m_assistantProcess->waitForFinished(3000); |
| } |
| qDeleteAll(m_phraseBooks); |
| delete m_dataModel; |
| delete m_statistics; |
| delete m_printer; |
| } |
| |
| void MainWindow::initViewHeaders() |
| { |
| m_contextView->header()->setResizeMode(1, QHeaderView::Stretch); |
| m_contextView->header()->setResizeMode(2, QHeaderView::ResizeToContents); |
| m_messageView->setColumnHidden(2, true); |
| // last visible column auto-stretches |
| } |
| |
| void MainWindow::modelCountChanged() |
| { |
| int mc = m_dataModel->modelCount(); |
| |
| for (int i = 0; i < mc; ++i) { |
| m_contextView->header()->setResizeMode(i + 1, QHeaderView::Fixed); |
| m_contextView->header()->resizeSection(i + 1, 24); |
| |
| m_messageView->header()->setResizeMode(i + 1, QHeaderView::Fixed); |
| m_messageView->header()->resizeSection(i + 1, 24); |
| } |
| |
| if (!mc) { |
| selectedMessageChanged(QModelIndex(), QModelIndex()); |
| updateLatestModel(-1); |
| } else { |
| if (!m_contextView->currentIndex().isValid()) { |
| // Ensure that something is selected |
| m_contextView->setCurrentIndex(m_sortedContextsModel->index(0, 0)); |
| } else { |
| // Plug holes that turn up in the selection due to inserting columns |
| m_contextView->selectionModel()->select(m_contextView->currentIndex(), |
| QItemSelectionModel::SelectCurrent|QItemSelectionModel::Rows); |
| m_messageView->selectionModel()->select(m_messageView->currentIndex(), |
| QItemSelectionModel::SelectCurrent|QItemSelectionModel::Rows); |
| } |
| // Field insertions/removals are automatic, but not the re-fill |
| m_messageEditor->showMessage(m_currentIndex); |
| if (mc == 1) |
| updateLatestModel(0); |
| else if (m_currentIndex.model() >= mc) |
| updateLatestModel(mc - 1); |
| } |
| |
| m_contextView->setUpdatesEnabled(true); |
| m_messageView->setUpdatesEnabled(true); |
| |
| updateProgress(); |
| updateCaption(); |
| |
| m_ui.actionFind->setEnabled(m_dataModel->contextCount() > 0); |
| m_ui.actionFindNext->setEnabled(false); |
| |
| m_formPreviewView->setSourceContext(-1, 0); |
| } |
| |
| struct OpenedFile { |
| OpenedFile(DataModel *_dataModel, bool _readWrite, bool _langGuessed) |
| { dataModel = _dataModel; readWrite = _readWrite; langGuessed = _langGuessed; } |
| DataModel *dataModel; |
| bool readWrite; |
| bool langGuessed; |
| }; |
| |
| bool MainWindow::openFiles(const QStringList &names, bool globalReadWrite) |
| { |
| if (names.isEmpty()) |
| return false; |
| |
| bool waitCursor = false; |
| statusBar()->showMessage(tr("Loading...")); |
| qApp->processEvents(); |
| |
| QList<OpenedFile> opened; |
| bool closeOld = false; |
| foreach (QString name, names) { |
| if (!waitCursor) { |
| QApplication::setOverrideCursor(Qt::WaitCursor); |
| waitCursor = true; |
| } |
| |
| bool readWrite = globalReadWrite; |
| if (name.startsWith(QLatin1Char('='))) { |
| name.remove(0, 1); |
| readWrite = false; |
| } |
| QFileInfo fi(name); |
| if (fi.exists()) // Make the loader error out instead of reading stdin |
| name = fi.canonicalFilePath(); |
| if (m_dataModel->isFileLoaded(name) >= 0) |
| continue; |
| |
| bool langGuessed; |
| DataModel *dm = new DataModel(m_dataModel); |
| if (!dm->load(name, &langGuessed, this)) { |
| delete dm; |
| continue; |
| } |
| if (opened.isEmpty()) { |
| if (!m_dataModel->isWellMergeable(dm)) { |
| QApplication::restoreOverrideCursor(); |
| waitCursor = false; |
| switch (QMessageBox::information(this, tr("Loading File - Qt Linguist"), |
| tr("The file '%1' does not seem to be related to the currently open file(s) '%2'.\n\n" |
| "Close the open file(s) first?") |
| .arg(DataModel::prettifyPlainFileName(name), m_dataModel->condensedSrcFileNames(true)), |
| QMessageBox::Yes | QMessageBox::Default, |
| QMessageBox::No, |
| QMessageBox::Cancel | QMessageBox::Escape)) |
| { |
| case QMessageBox::Cancel: |
| delete dm; |
| return false; |
| case QMessageBox::Yes: |
| closeOld = true; |
| break; |
| case QMessageBox::No: |
| break; |
| } |
| } |
| } else { |
| if (!opened.first().dataModel->isWellMergeable(dm)) { |
| QApplication::restoreOverrideCursor(); |
| waitCursor = false; |
| switch (QMessageBox::information(this, tr("Loading File - Qt Linguist"), |
| tr("The file '%1' does not seem to be related to the file '%2'" |
| " which is being loaded as well.\n\n" |
| "Skip loading the first named file?") |
| .arg(DataModel::prettifyPlainFileName(name), opened.first().dataModel->srcFileName(true)), |
| QMessageBox::Yes | QMessageBox::Default, |
| QMessageBox::No, |
| QMessageBox::Cancel | QMessageBox::Escape)) |
| { |
| case QMessageBox::Cancel: |
| delete dm; |
| foreach (const OpenedFile &op, opened) |
| delete op.dataModel; |
| return false; |
| case QMessageBox::Yes: |
| delete dm; |
| continue; |
| case QMessageBox::No: |
| break; |
| } |
| } |
| } |
| opened.append(OpenedFile(dm, readWrite, langGuessed)); |
| } |
| |
| if (closeOld) { |
| if (waitCursor) { |
| QApplication::restoreOverrideCursor(); |
| waitCursor = false; |
| } |
| if (!closeAll()) { |
| foreach (const OpenedFile &op, opened) |
| delete op.dataModel; |
| return false; |
| } |
| } |
| |
| foreach (const OpenedFile &op, opened) { |
| if (op.langGuessed) { |
| if (waitCursor) { |
| QApplication::restoreOverrideCursor(); |
| waitCursor = false; |
| } |
| if (!m_translationSettingsDialog) |
| m_translationSettingsDialog = new TranslationSettingsDialog(this); |
| m_translationSettingsDialog->setDataModel(op.dataModel); |
| m_translationSettingsDialog->exec(); |
| } |
| } |
| |
| if (!waitCursor) |
| QApplication::setOverrideCursor(Qt::WaitCursor); |
| m_contextView->setUpdatesEnabled(false); |
| m_messageView->setUpdatesEnabled(false); |
| int totalCount = 0; |
| foreach (const OpenedFile &op, opened) { |
| m_phraseDict.append(QHash<QString, QList<Phrase *> >()); |
| m_dataModel->append(op.dataModel, op.readWrite); |
| if (op.readWrite) |
| updatePhraseDictInternal(m_phraseDict.size() - 1); |
| totalCount += op.dataModel->messageCount(); |
| } |
| statusBar()->showMessage(tr("%n translation unit(s) loaded.", 0, totalCount), MessageMS); |
| modelCountChanged(); |
| recentFiles().addFiles(m_dataModel->srcFileNames()); |
| |
| revalidate(); |
| QApplication::restoreOverrideCursor(); |
| return true; |
| } |
| |
| RecentFiles &MainWindow::recentFiles() |
| { |
| static RecentFiles recentFiles(10); |
| return recentFiles; |
| } |
| |
| const QString &MainWindow::resourcePrefix() |
| { |
| #ifdef Q_WS_MAC |
| static const QString prefix(QLatin1String(":/images/mac")); |
| #else |
| static const QString prefix(QLatin1String(":/images/win")); |
| #endif |
| return prefix; |
| } |
| |
| void MainWindow::open() |
| { |
| openFiles(pickTranslationFiles()); |
| } |
| |
| void MainWindow::openAux() |
| { |
| openFiles(pickTranslationFiles(), false); |
| } |
| |
| void MainWindow::closeFile() |
| { |
| int model = m_currentIndex.model(); |
| if (model >= 0 && maybeSave(model)) { |
| m_phraseDict.removeAt(model); |
| m_contextView->setUpdatesEnabled(false); |
| m_messageView->setUpdatesEnabled(false); |
| m_dataModel->close(model); |
| modelCountChanged(); |
| } |
| } |
| |
| bool MainWindow::closeAll() |
| { |
| if (maybeSaveAll()) { |
| m_phraseDict.clear(); |
| m_contextView->setUpdatesEnabled(false); |
| m_messageView->setUpdatesEnabled(false); |
| m_dataModel->closeAll(); |
| modelCountChanged(); |
| initViewHeaders(); |
| recentFiles().closeGroup(); |
| return true; |
| } |
| return false; |
| } |
| |
| static QString fileFilters(bool allFirst) |
| { |
| static const QString pattern(QLatin1String("%1 (*.%2);;")); |
| QStringList allExtensions; |
| QString filter; |
| foreach (const Translator::FileFormat &format, Translator::registeredFileFormats()) { |
| if (format.fileType == Translator::FileFormat::TranslationSource && format.priority >= 0) { |
| filter.append(pattern.arg(format.description).arg(format.extension)); |
| allExtensions.append(QLatin1String("*.") + format.extension); |
| } |
| } |
| QString allFilter = QObject::tr("Translation files (%1);;").arg(allExtensions.join(QLatin1String(" "))); |
| if (allFirst) |
| filter.prepend(allFilter); |
| else |
| filter.append(allFilter); |
| filter.append(QObject::tr("All files (*)")); |
| return filter; |
| } |
| |
| QStringList MainWindow::pickTranslationFiles() |
| { |
| QString dir; |
| if (!recentFiles().isEmpty()) |
| dir = QFileInfo(recentFiles().lastOpenedFile()).path(); |
| |
| QString varFilt; |
| if (m_dataModel->modelCount()) { |
| QFileInfo mainFile(m_dataModel->srcFileName(0)); |
| QString mainFileBase = mainFile.baseName(); |
| int pos = mainFileBase.indexOf(QLatin1Char('_')); |
| if (pos > 0) |
| varFilt = tr("Related files (%1);;") |
| .arg(mainFileBase.left(pos) + QLatin1String("_*.") + mainFile.completeSuffix()); |
| } |
| |
| return QFileDialog::getOpenFileNames(this, tr("Open Translation Files"), dir, |
| varFilt + |
| fileFilters(true)); |
| } |
| |
| void MainWindow::saveInternal(int model) |
| { |
| QApplication::setOverrideCursor(Qt::WaitCursor); |
| if (m_dataModel->save(model, this)) { |
| updateCaption(); |
| statusBar()->showMessage(tr("File saved."), MessageMS); |
| } |
| QApplication::restoreOverrideCursor(); |
| } |
| |
| void MainWindow::saveAll() |
| { |
| for (int i = 0; i < m_dataModel->modelCount(); ++i) |
| if (m_dataModel->isModelWritable(i)) |
| saveInternal(i); |
| recentFiles().closeGroup(); |
| } |
| |
| void MainWindow::save() |
| { |
| if (m_currentIndex.model() < 0) |
| return; |
| |
| saveInternal(m_currentIndex.model()); |
| } |
| |
| void MainWindow::saveAs() |
| { |
| if (m_currentIndex.model() < 0) |
| return; |
| |
| QString newFilename = QFileDialog::getSaveFileName(this, QString(), m_dataModel->srcFileName(m_currentIndex.model()), |
| fileFilters(false)); |
| if (!newFilename.isEmpty()) { |
| if (m_dataModel->saveAs(m_currentIndex.model(), newFilename, this)) { |
| updateCaption(); |
| statusBar()->showMessage(tr("File saved."), MessageMS); |
| recentFiles().addFiles(m_dataModel->srcFileNames()); |
| } |
| } |
| } |
| |
| void MainWindow::releaseAs() |
| { |
| if (m_currentIndex.model() < 0) |
| return; |
| |
| QFileInfo oldFile(m_dataModel->srcFileName(m_currentIndex.model())); |
| QString newFilename = oldFile.path() + QLatin1String("/") |
| + oldFile.completeBaseName() + QLatin1String(".qm"); |
| |
| newFilename = QFileDialog::getSaveFileName(this, tr("Release"), newFilename, |
| tr("Qt message files for released applications (*.qm)\nAll files (*)")); |
| if (!newFilename.isEmpty()) { |
| if (m_dataModel->release(m_currentIndex.model(), newFilename, false, false, SaveEverything, this)) |
| statusBar()->showMessage(tr("File created."), MessageMS); |
| } |
| } |
| |
| void MainWindow::releaseInternal(int model) |
| { |
| QFileInfo oldFile(m_dataModel->srcFileName(model)); |
| QString newFilename = oldFile.path() + QLatin1Char('/') |
| + oldFile.completeBaseName() + QLatin1String(".qm"); |
| |
| if (!newFilename.isEmpty()) { |
| if (m_dataModel->release(model, newFilename, false, false, SaveEverything, this)) |
| statusBar()->showMessage(tr("File created."), MessageMS); |
| } |
| } |
| |
| // No-question |
| void MainWindow::release() |
| { |
| if (m_currentIndex.model() < 0) |
| return; |
| |
| releaseInternal(m_currentIndex.model()); |
| } |
| |
| void MainWindow::releaseAll() |
| { |
| for (int i = 0; i < m_dataModel->modelCount(); ++i) |
| if (m_dataModel->isModelWritable(i)) |
| releaseInternal(i); |
| } |
| |
| QPrinter *MainWindow::printer() |
| { |
| if (!m_printer) |
| m_printer = new QPrinter; |
| return m_printer; |
| } |
| |
| void MainWindow::print() |
| { |
| int pageNum = 0; |
| QPrintDialog dlg(printer(), this); |
| if (dlg.exec()) { |
| QApplication::setOverrideCursor(Qt::WaitCursor); |
| printer()->setDocName(m_dataModel->condensedSrcFileNames(true)); |
| statusBar()->showMessage(tr("Printing...")); |
| PrintOut pout(printer()); |
| |
| for (int i = 0; i < m_dataModel->contextCount(); ++i) { |
| MultiContextItem *mc = m_dataModel->multiContextItem(i); |
| pout.vskip(); |
| pout.setRule(PrintOut::ThickRule); |
| pout.setGuide(mc->context()); |
| pout.addBox(100, tr("Context: %1").arg(mc->context()), |
| PrintOut::Strong); |
| pout.flushLine(); |
| pout.addBox(4); |
| pout.addBox(92, mc->comment(), PrintOut::Emphasis); |
| pout.flushLine(); |
| pout.setRule(PrintOut::ThickRule); |
| |
| for (int j = 0; j < mc->messageCount(); ++j) { |
| pout.setRule(PrintOut::ThinRule); |
| bool printedSrc = false; |
| QString comment; |
| for (int k = 0; k < m_dataModel->modelCount(); ++k) { |
| if (const MessageItem *m = mc->messageItem(k, j)) { |
| if (!printedSrc) { |
| pout.addBox(40, m->text()); |
| pout.addBox(4); |
| comment = m->comment(); |
| printedSrc = true; |
| } else { |
| pout.addBox(44); // Maybe put the name of the translation here |
| } |
| if (m->message().isPlural() && m_dataModel->language(k) != QLocale::C) { |
| QStringList transls = m->translations(); |
| pout.addBox(40, transls.join(QLatin1String("\n"))); |
| } else { |
| pout.addBox(40, m->translation()); |
| } |
| pout.addBox(4); |
| QString type; |
| switch (m->message().type()) { |
| case TranslatorMessage::Finished: |
| type = tr("finished"); |
| break; |
| case TranslatorMessage::Unfinished: |
| type = m->danger() ? tr("unresolved") : QLatin1String("unfinished"); |
| break; |
| case TranslatorMessage::Obsolete: |
| type = tr("obsolete"); |
| break; |
| } |
| pout.addBox(12, type, PrintOut::Normal, Qt::AlignRight); |
| pout.flushLine(); |
| } |
| } |
| if (!comment.isEmpty()) { |
| pout.addBox(4); |
| pout.addBox(92, comment, PrintOut::Emphasis); |
| pout.flushLine(true); |
| } |
| |
| if (pout.pageNum() != pageNum) { |
| pageNum = pout.pageNum(); |
| statusBar()->showMessage(tr("Printing... (page %1)") |
| .arg(pageNum)); |
| } |
| } |
| } |
| pout.flushLine(true); |
| QApplication::restoreOverrideCursor(); |
| statusBar()->showMessage(tr("Printing completed"), MessageMS); |
| } else { |
| statusBar()->showMessage(tr("Printing aborted"), MessageMS); |
| } |
| } |
| |
| bool MainWindow::searchItem(const QString &searchWhat) |
| { |
| if ((m_findWhere & m_foundWhere) == 0) |
| return false; |
| |
| QString text = searchWhat; |
| |
| if (m_findIgnoreAccelerators) |
| // FIXME: This removes too much. The proper solution might be too slow, though. |
| text.remove(QLatin1Char('&')); |
| |
| int foundOffset = text.indexOf(m_findText, 0, m_findMatchCase); |
| return foundOffset >= 0; |
| } |
| |
| void MainWindow::findAgain() |
| { |
| if (m_dataModel->contextCount() == 0) |
| return; |
| |
| const QModelIndex &startIndex = m_messageView->currentIndex(); |
| QModelIndex index = nextMessage(startIndex); |
| |
| while (index.isValid()) { |
| QModelIndex realIndex = m_sortedMessagesModel->mapToSource(index); |
| MultiDataIndex dataIndex = m_messageModel->dataIndex(realIndex, -1); |
| bool hadMessage = false; |
| for (int i = 0; i < m_dataModel->modelCount(); ++i) { |
| if (MessageItem *m = m_dataModel->messageItem(dataIndex, i)) { |
| // Note: we do not look into plurals on grounds of them not |
| // containing anything much different from the singular. |
| if (hadMessage) { |
| m_foundWhere = DataModel::Translations; |
| if (!searchItem(m->translation())) |
| m_foundWhere = DataModel::NoLocation; |
| } else { |
| switch (m_foundWhere) { |
| case 0: |
| m_foundWhere = DataModel::SourceText; |
| // fall-through to search source text |
| case DataModel::SourceText: |
| if (searchItem(m->text())) |
| break; |
| if (searchItem(m->pluralText())) |
| break; |
| m_foundWhere = DataModel::Translations; |
| // fall-through to search translation |
| case DataModel::Translations: |
| if (searchItem(m->translation())) |
| break; |
| m_foundWhere = DataModel::Comments; |
| // fall-through to search comment |
| case DataModel::Comments: |
| if (searchItem(m->comment())) |
| break; |
| if (searchItem(m->extraComment())) |
| break; |
| if (searchItem(m->translatorComment())) |
| break; |
| m_foundWhere = DataModel::NoLocation; |
| // did not find the search string in this message |
| } |
| } |
| if (m_foundWhere != DataModel::NoLocation) { |
| setCurrentMessage(realIndex, i); |
| |
| // determine whether the search wrapped |
| const QModelIndex &c1 = m_sortedContextsModel->mapFromSource( |
| m_sortedMessagesModel->mapToSource(startIndex)).parent(); |
| const QModelIndex &c2 = m_sortedContextsModel->mapFromSource(realIndex).parent(); |
| const QModelIndex &m = m_sortedMessagesModel->mapFromSource(realIndex); |
| |
| if (c2.row() < c1.row() || (c1.row() == c2.row() && m.row() <= startIndex.row())) |
| statusBar()->showMessage(tr("Search wrapped."), MessageMS); |
| |
| m_findDialog->hide(); |
| return; |
| } |
| hadMessage = true; |
| } |
| } |
| |
| // since we don't search startIndex at the beginning, only now we have searched everything |
| if (index == startIndex) |
| break; |
| |
| index = nextMessage(index); |
| } |
| |
| qApp->beep(); |
| QMessageBox::warning(m_findDialog, tr("Qt Linguist"), |
| tr("Cannot find the string '%1'.").arg(m_findText)); |
| m_foundWhere = DataModel::NoLocation; |
| } |
| |
| void MainWindow::showBatchTranslateDialog() |
| { |
| m_messageModel->blockSignals(true); |
| m_batchTranslateDialog->setPhraseBooks(m_phraseBooks, m_currentIndex.model()); |
| if (m_batchTranslateDialog->exec() != QDialog::Accepted) |
| m_messageModel->blockSignals(false); |
| // else signal finished() calls refreshItemViews() |
| } |
| |
| void MainWindow::showTranslateDialog() |
| { |
| m_latestCaseSensitivity = -1; |
| QModelIndex idx = m_messageView->currentIndex(); |
| QModelIndex idx2 = m_sortedMessagesModel->index(idx.row(), m_currentIndex.model() + 1, idx.parent()); |
| m_messageView->setCurrentIndex(idx2); |
| QString fn = QFileInfo(m_dataModel->srcFileName(m_currentIndex.model())).baseName(); |
| m_translateDialog->setWindowTitle(tr("Search And Translate in '%1' - Qt Linguist").arg(fn)); |
| m_translateDialog->exec(); |
| } |
| |
| void MainWindow::updateTranslateHit(bool &hit) |
| { |
| MessageItem *m; |
| hit = (m = m_dataModel->messageItem(m_currentIndex)) |
| && !m->isObsolete() |
| && m->compare(m_translateDialog->findText(), false, m_translateDialog->caseSensitivity()); |
| } |
| |
| void MainWindow::translate(int mode) |
| { |
| QString findText = m_translateDialog->findText(); |
| QString replaceText = m_translateDialog->replaceText(); |
| bool markFinished = m_translateDialog->markFinished(); |
| Qt::CaseSensitivity caseSensitivity = m_translateDialog->caseSensitivity(); |
| |
| int translatedCount = 0; |
| |
| if (mode == TranslateDialog::TranslateAll) { |
| for (MultiDataModelIterator it(m_dataModel, m_currentIndex.model()); it.isValid(); ++it) { |
| MessageItem *m = it.current(); |
| if (m && !m->isObsolete() && m->compare(findText, false, caseSensitivity)) { |
| if (!translatedCount) |
| m_messageModel->blockSignals(true); |
| m_dataModel->setTranslation(it, replaceText); |
| m_dataModel->setFinished(it, markFinished); |
| ++translatedCount; |
| } |
| } |
| if (translatedCount) { |
| refreshItemViews(); |
| QMessageBox::warning(m_translateDialog, tr("Translate - Qt Linguist"), |
| tr("Translated %n entry(s)", 0, translatedCount)); |
| } |
| } else { |
| if (mode == TranslateDialog::Translate) { |
| m_dataModel->setTranslation(m_currentIndex, replaceText); |
| m_dataModel->setFinished(m_currentIndex, markFinished); |
| } |
| |
| if (findText != m_latestFindText || caseSensitivity != m_latestCaseSensitivity) { |
| m_latestFindText = findText; |
| m_latestCaseSensitivity = caseSensitivity; |
| m_remainingCount = m_dataModel->messageCount(); |
| m_hitCount = 0; |
| } |
| |
| QModelIndex index = m_messageView->currentIndex(); |
| int prevRemained = m_remainingCount; |
| forever { |
| if (--m_remainingCount <= 0) { |
| if (!m_hitCount) |
| break; |
| m_remainingCount = m_dataModel->messageCount() - 1; |
| if (QMessageBox::question(m_translateDialog, tr("Translate - Qt Linguist"), |
| tr("No more occurrences of '%1'. Start over?").arg(findText), |
| QMessageBox::Yes|QMessageBox::No) != QMessageBox::Yes) |
| return; |
| m_remainingCount -= prevRemained; |
| } |
| |
| index = nextMessage(index); |
| |
| QModelIndex realIndex = m_sortedMessagesModel->mapToSource(index); |
| MultiDataIndex dataIndex = m_messageModel->dataIndex(realIndex, m_currentIndex.model()); |
| if (MessageItem *m = m_dataModel->messageItem(dataIndex)) { |
| if (!m->isObsolete() && m->compare(findText, false, caseSensitivity)) { |
| setCurrentMessage(realIndex, m_currentIndex.model()); |
| ++translatedCount; |
| ++m_hitCount; |
| break; |
| } |
| } |
| } |
| } |
| |
| if (!translatedCount) { |
| qApp->beep(); |
| QMessageBox::warning(m_translateDialog, tr("Translate - Qt Linguist"), |
| tr("Cannot find the string '%1'.").arg(findText)); |
| } |
| } |
| |
| void MainWindow::newPhraseBook() |
| { |
| QString name = QFileDialog::getSaveFileName(this, tr("Create New Phrase Book"), |
| m_phraseBookDir, tr("Qt phrase books (*.qph)\nAll files (*)")); |
| if (!name.isEmpty()) { |
| PhraseBook pb; |
| if (!m_translationSettingsDialog) |
| m_translationSettingsDialog = new TranslationSettingsDialog(this); |
| m_translationSettingsDialog->setPhraseBook(&pb); |
| if (!m_translationSettingsDialog->exec()) |
| return; |
| m_phraseBookDir = QFileInfo(name).absolutePath(); |
| if (savePhraseBook(&name, pb)) { |
| if (openPhraseBook(name)) |
| statusBar()->showMessage(tr("Phrase book created."), MessageMS); |
| } |
| } |
| } |
| |
| bool MainWindow::isPhraseBookOpen(const QString &name) |
| { |
| foreach(const PhraseBook *pb, m_phraseBooks) { |
| if (pb->fileName() == name) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void MainWindow::openPhraseBook() |
| { |
| QString name = QFileDialog::getOpenFileName(this, tr("Open Phrase Book"), |
| m_phraseBookDir, tr("Qt phrase books (*.qph);;All files (*)")); |
| |
| if (!name.isEmpty()) { |
| m_phraseBookDir = QFileInfo(name).absolutePath(); |
| if (!isPhraseBookOpen(name)) { |
| if (PhraseBook *phraseBook = openPhraseBook(name)) { |
| int n = phraseBook->phrases().count(); |
| statusBar()->showMessage(tr("%n phrase(s) loaded.", 0, n), MessageMS); |
| } |
| } |
| } |
| } |
| |
| void MainWindow::closePhraseBook(QAction *action) |
| { |
| PhraseBook *pb = m_phraseBookMenu[PhraseCloseMenu].value(action); |
| if (!maybeSavePhraseBook(pb)) |
| return; |
| |
| m_phraseBookMenu[PhraseCloseMenu].remove(action); |
| m_ui.menuClosePhraseBook->removeAction(action); |
| |
| QAction *act = m_phraseBookMenu[PhraseEditMenu].key(pb); |
| m_phraseBookMenu[PhraseEditMenu].remove(act); |
| m_ui.menuEditPhraseBook->removeAction(act); |
| |
| act = m_phraseBookMenu[PhrasePrintMenu].key(pb); |
| m_ui.menuPrintPhraseBook->removeAction(act); |
| |
| m_phraseBooks.removeOne(pb); |
| disconnect(pb, SIGNAL(listChanged()), this, SLOT(updatePhraseDicts())); |
| updatePhraseDicts(); |
| delete pb; |
| updatePhraseBookActions(); |
| } |
| |
| void MainWindow::editPhraseBook(QAction *action) |
| { |
| PhraseBook *pb = m_phraseBookMenu[PhraseEditMenu].value(action); |
| PhraseBookBox box(pb, this); |
| box.exec(); |
| |
| updatePhraseDicts(); |
| } |
| |
| void MainWindow::printPhraseBook(QAction *action) |
| { |
| PhraseBook *phraseBook = m_phraseBookMenu[PhrasePrintMenu].value(action); |
| |
| int pageNum = 0; |
| |
| QPrintDialog dlg(printer(), this); |
| if (dlg.exec()) { |
| printer()->setDocName(phraseBook->fileName()); |
| statusBar()->showMessage(tr("Printing...")); |
| PrintOut pout(printer()); |
| pout.setRule(PrintOut::ThinRule); |
| foreach (const Phrase *p, phraseBook->phrases()) { |
| pout.setGuide(p->source()); |
| pout.addBox(29, p->source()); |
| pout.addBox(4); |
| pout.addBox(29, p->target()); |
| pout.addBox(4); |
| pout.addBox(34, p->definition(), PrintOut::Emphasis); |
| |
| if (pout.pageNum() != pageNum) { |
| pageNum = pout.pageNum(); |
| statusBar()->showMessage(tr("Printing... (page %1)") |
| .arg(pageNum)); |
| } |
| pout.setRule(PrintOut::NoRule); |
| pout.flushLine(true); |
| } |
| pout.flushLine(true); |
| statusBar()->showMessage(tr("Printing completed"), MessageMS); |
| } else { |
| statusBar()->showMessage(tr("Printing aborted"), MessageMS); |
| } |
| } |
| |
| void MainWindow::addToPhraseBook() |
| { |
| MessageItem *currentMessage = m_dataModel->messageItem(m_currentIndex); |
| Phrase *phrase = new Phrase(currentMessage->text(), currentMessage->translation(), QString()); |
| QStringList phraseBookList; |
| QHash<QString, PhraseBook *> phraseBookHash; |
| foreach (PhraseBook *pb, m_phraseBooks) { |
| if (pb->language() != QLocale::C && m_dataModel->language(m_currentIndex.model()) != QLocale::C) { |
| if (pb->language() != m_dataModel->language(m_currentIndex.model())) |
| continue; |
| if (pb->country() == m_dataModel->model(m_currentIndex.model())->country()) |
| phraseBookList.prepend(pb->friendlyPhraseBookName()); |
| else |
| phraseBookList.append(pb->friendlyPhraseBookName()); |
| } else { |
| phraseBookList.append(pb->friendlyPhraseBookName()); |
| } |
| phraseBookHash.insert(pb->friendlyPhraseBookName(), pb); |
| } |
| if (phraseBookList.isEmpty()) { |
| QMessageBox::warning(this, tr("Add to phrase book"), |
| tr("No appropriate phrasebook found.")); |
| } else if (phraseBookList.size() == 1) { |
| if (QMessageBox::information(this, tr("Add to phrase book"), |
| tr("Adding entry to phrasebook %1").arg(phraseBookList.at(0)), |
| QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) |
| == QMessageBox::Ok) |
| phraseBookHash.value(phraseBookList.at(0))->append(phrase); |
| } else { |
| bool okPressed = false; |
| QString selection = QInputDialog::getItem(this, tr("Add to phrase book"), |
| tr("Select phrase book to add to"), |
| phraseBookList, 0, false, &okPressed); |
| if (okPressed) |
| phraseBookHash.value(selection)->append(phrase); |
| } |
| } |
| |
| void MainWindow::resetSorting() |
| { |
| m_contextView->sortByColumn(-1, Qt::AscendingOrder); |
| m_messageView->sortByColumn(-1, Qt::AscendingOrder); |
| } |
| |
| void MainWindow::manual() |
| { |
| if (!m_assistantProcess) |
| m_assistantProcess = new QProcess(); |
| |
| if (m_assistantProcess->state() != QProcess::Running) { |
| QString app = QLibraryInfo::location(QLibraryInfo::BinariesPath) + QDir::separator(); |
| #if !defined(Q_OS_MAC) |
| app += QLatin1String("assistant"); |
| #else |
| app += QLatin1String("Assistant.app/Contents/MacOS/Assistant"); |
| #endif |
| |
| m_assistantProcess->start(app, QStringList() << QLatin1String("-enableRemoteControl")); |
| if (!m_assistantProcess->waitForStarted()) { |
| QMessageBox::critical(this, tr("Qt Linguist"), |
| tr("Unable to launch Qt Assistant (%1)").arg(app)); |
| return; |
| } |
| } |
| |
| QTextStream str(m_assistantProcess); |
| str << QLatin1String("SetSource qthelp://com.trolltech.linguist.") |
| << (QT_VERSION >> 16) << ((QT_VERSION >> 8) & 0xFF) |
| << (QT_VERSION & 0xFF) |
| << QLatin1String("/qdoc/linguist-manual.html") |
| << QLatin1Char('\n') << endl; |
| } |
| |
| void MainWindow::about() |
| { |
| QMessageBox box(this); |
| box.setTextFormat(Qt::RichText); |
| QString version = tr("Version %1"); |
| version = version.arg(QLatin1String(QT_VERSION_STR)); |
| |
| box.setText(tr("<center><img src=\":/images/splash.png\"/></img><p>%1</p></center>" |
| "<p>Qt Linguist is a tool for adding translations to Qt " |
| "applications.</p>" |
| "<p>Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)." |
| ).arg(version)); |
| |
| box.setWindowTitle(QApplication::translate("AboutDialog", "Qt Linguist")); |
| box.setIcon(QMessageBox::NoIcon); |
| box.exec(); |
| } |
| |
| void MainWindow::aboutQt() |
| { |
| QMessageBox::aboutQt(this, tr("Qt Linguist")); |
| } |
| |
| void MainWindow::setupPhrase() |
| { |
| bool enabled = !m_phraseBooks.isEmpty(); |
| m_ui.menuClosePhraseBook->setEnabled(enabled); |
| m_ui.menuEditPhraseBook->setEnabled(enabled); |
| m_ui.menuPrintPhraseBook->setEnabled(enabled); |
| } |
| |
| void MainWindow::closeEvent(QCloseEvent *e) |
| { |
| if (maybeSaveAll() && closePhraseBooks()) |
| e->accept(); |
| else |
| e->ignore(); |
| } |
| |
| bool MainWindow::maybeSaveAll() |
| { |
| if (!m_dataModel->isModified()) |
| return true; |
| |
| switch (QMessageBox::information(this, tr("Qt Linguist"), |
| tr("Do you want to save the modified files?"), |
| QMessageBox::Yes | QMessageBox::Default, |
| QMessageBox::No, |
| QMessageBox::Cancel | QMessageBox::Escape)) |
| { |
| case QMessageBox::Cancel: |
| return false; |
| case QMessageBox::Yes: |
| saveAll(); |
| return !m_dataModel->isModified(); |
| case QMessageBox::No: |
| break; |
| } |
| return true; |
| } |
| |
| bool MainWindow::maybeSave(int model) |
| { |
| if (!m_dataModel->isModified(model)) |
| return true; |
| |
| switch (QMessageBox::information(this, tr("Qt Linguist"), |
| tr("Do you want to save '%1'?").arg(m_dataModel->srcFileName(model, true)), |
| QMessageBox::Yes | QMessageBox::Default, |
| QMessageBox::No, |
| QMessageBox::Cancel | QMessageBox::Escape)) |
| { |
| case QMessageBox::Cancel: |
| return false; |
| case QMessageBox::Yes: |
| saveInternal(model); |
| return !m_dataModel->isModified(model); |
| case QMessageBox::No: |
| break; |
| } |
| return true; |
| } |
| |
| void MainWindow::updateCaption() |
| { |
| QString cap; |
| bool enable = false; |
| bool enableRw = false; |
| for (int i = 0; i < m_dataModel->modelCount(); ++i) { |
| enable = true; |
| if (m_dataModel->isModelWritable(i)) { |
| enableRw = true; |
| break; |
| } |
| } |
| m_ui.actionSaveAll->setEnabled(enableRw); |
| m_ui.actionReleaseAll->setEnabled(enableRw); |
| m_ui.actionCloseAll->setEnabled(enable); |
| m_ui.actionPrint->setEnabled(enable); |
| m_ui.actionAccelerators->setEnabled(enable); |
| m_ui.actionEndingPunctuation->setEnabled(enable); |
| m_ui.actionPhraseMatches->setEnabled(enable); |
| m_ui.actionPlaceMarkerMatches->setEnabled(enable); |
| m_ui.actionResetSorting->setEnabled(enable); |
| |
| updateActiveModel(m_messageEditor->activeModel()); |
| // Ensure that the action labels get updated |
| m_fileActiveModel = m_editActiveModel = -2; |
| |
| if (!enable) |
| cap = tr("Qt Linguist[*]"); |
| else |
| cap = tr("%1[*] - Qt Linguist").arg(m_dataModel->condensedSrcFileNames(true)); |
| setWindowTitle(cap); |
| } |
| |
| void MainWindow::selectedContextChanged(const QModelIndex &sortedIndex, const QModelIndex &oldIndex) |
| { |
| if (sortedIndex.isValid()) { |
| if (m_settingCurrentMessage) |
| return; // Avoid playing ping-pong with the current message |
| |
| QModelIndex sourceIndex = m_sortedContextsModel->mapToSource(sortedIndex); |
| if (m_messageModel->parent(currentMessageIndex()).row() == sourceIndex.row()) |
| return; |
| |
| QModelIndex contextIndex = setMessageViewRoot(sourceIndex); |
| const QModelIndex &firstChild = |
| m_sortedMessagesModel->index(0, sourceIndex.column(), contextIndex); |
| m_messageView->setCurrentIndex(firstChild); |
| } else if (oldIndex.isValid()) { |
| m_contextView->setCurrentIndex(oldIndex); |
| } |
| } |
| |
| /* |
| * Updates the message displayed in the message editor and related actions. |
| */ |
| void MainWindow::selectedMessageChanged(const QModelIndex &sortedIndex, const QModelIndex &oldIndex) |
| { |
| // Keep a valid selection whenever possible |
| if (!sortedIndex.isValid() && oldIndex.isValid()) { |
| m_messageView->setCurrentIndex(oldIndex); |
| return; |
| } |
| |
| QModelIndex index = m_sortedMessagesModel->mapToSource(sortedIndex); |
| if (index.isValid()) { |
| int model = (index.column() && (index.column() - 1 < m_dataModel->modelCount())) ? |
| index.column() - 1 : m_currentIndex.model(); |
| m_currentIndex = m_messageModel->dataIndex(index, model); |
| m_messageEditor->showMessage(m_currentIndex); |
| MessageItem *m = 0; |
| if (model >= 0 && (m = m_dataModel->messageItem(m_currentIndex))) { |
| if (m_dataModel->isModelWritable(model) && !m->isObsolete()) |
| m_phraseView->setSourceText(m_currentIndex.model(), m->text()); |
| else |
| m_phraseView->setSourceText(-1, QString()); |
| } else { |
| if (model < 0) { |
| model = m_dataModel->multiContextItem(m_currentIndex.context()) |
| ->firstNonobsoleteMessageIndex(m_currentIndex.message()); |
| if (model >= 0) |
| m = m_dataModel->messageItem(m_currentIndex, model); |
| } |
| m_phraseView->setSourceText(-1, QString()); |
| } |
| if (m && !m->fileName().isEmpty()) { |
| if (hasFormPreview(m->fileName())) { |
| m_sourceAndFormView->setCurrentWidget(m_formPreviewView); |
| m_formPreviewView->setSourceContext(model, m); |
| } else { |
| m_sourceAndFormView->setCurrentWidget(m_sourceCodeView); |
| QDir dir = QFileInfo(m_dataModel->srcFileName(model)).dir(); |
| QString fileName = QDir::cleanPath(dir.absoluteFilePath(m->fileName())); |
| m_sourceCodeView->setSourceContext(fileName, m->lineNumber()); |
| } |
| m_errorsView->setEnabled(true); |
| } else { |
| m_sourceAndFormView->setCurrentWidget(m_sourceCodeView); |
| m_sourceCodeView->setSourceContext(QString(), 0); |
| m_errorsView->setEnabled(false); |
| } |
| updateDanger(m_currentIndex, true); |
| } else { |
| m_currentIndex = MultiDataIndex(); |
| m_messageEditor->showNothing(); |
| m_phraseView->setSourceText(-1, QString()); |
| m_sourceAndFormView->setCurrentWidget(m_sourceCodeView); |
| m_sourceCodeView->setSourceContext(QString(), 0); |
| } |
| |
| updatePhraseBookActions(); |
| m_ui.actionSelectAll->setEnabled(index.isValid()); |
| } |
| |
| void MainWindow::translationChanged(const MultiDataIndex &index) |
| { |
| // We get that as a result of batch translation or search & translate, |
| // so the current model is known to match. |
| if (index != m_currentIndex) |
| return; |
| |
| m_messageEditor->showMessage(index); |
| updateDanger(index, true); |
| |
| MessageItem *m = m_dataModel->messageItem(index); |
| if (hasFormPreview(m->fileName())) |
| m_formPreviewView->setSourceContext(index.model(), m); |
| } |
| |
| // This and the following function operate directly on the messageitem, |
| // so the model does not emit modification notifications. |
| void MainWindow::updateTranslation(const QStringList &translations) |
| { |
| MessageItem *m = m_dataModel->messageItem(m_currentIndex); |
| if (!m) |
| return; |
| if (translations == m->translations()) |
| return; |
| |
| m->setTranslations(translations); |
| if (!m->fileName().isEmpty() && hasFormPreview(m->fileName())) |
| m_formPreviewView->setSourceContext(m_currentIndex.model(), m); |
| updateDanger(m_currentIndex, true); |
| |
| if (m->isFinished()) |
| m_dataModel->setFinished(m_currentIndex, false); |
| else |
| m_dataModel->setModified(m_currentIndex.model(), true); |
| } |
| |
| void MainWindow::updateTranslatorComment(const QString &comment) |
| { |
| MessageItem *m = m_dataModel->messageItem(m_currentIndex); |
| if (!m) |
| return; |
| if (comment == m->translatorComment()) |
| return; |
| |
| m->setTranslatorComment(comment); |
| |
| m_dataModel->setModified(m_currentIndex.model(), true); |
| } |
| |
| void MainWindow::refreshItemViews() |
| { |
| m_messageModel->blockSignals(false); |
| m_contextView->update(); |
| m_messageView->update(); |
| setWindowModified(m_dataModel->isModified()); |
| m_modifiedLabel->setVisible(m_dataModel->isModified()); |
| updateStatistics(); |
| } |
| |
| void MainWindow::doneAndNext() |
| { |
| int model = m_messageEditor->activeModel(); |
| if (model >= 0 && m_dataModel->isModelWritable(model)) |
| m_dataModel->setFinished(m_currentIndex, true); |
| |
| if (!m_messageEditor->focusNextUnfinished()) |
| nextUnfinished(); |
| } |
| |
| void MainWindow::toggleFinished(const QModelIndex &index) |
| { |
| if (!index.isValid() || index.column() - 1 >= m_dataModel->modelCount() |
| || !m_dataModel->isModelWritable(index.column() - 1) || index.parent() == QModelIndex()) |
| return; |
| |
| QModelIndex item = m_sortedMessagesModel->mapToSource(index); |
| MultiDataIndex dataIndex = m_messageModel->dataIndex(item); |
| MessageItem *m = m_dataModel->messageItem(dataIndex); |
| |
| if (!m || m->message().type() == TranslatorMessage::Obsolete) |
| return; |
| |
| m_dataModel->setFinished(dataIndex, !m->isFinished()); |
| } |
| |
| /* |
| * Receives a context index in the sorted messages model and returns the next |
| * logical context index in the same model, based on the sort order of the |
| * contexts in the sorted contexts model. |
| */ |
| QModelIndex MainWindow::nextContext(const QModelIndex &index) const |
| { |
| QModelIndex sortedContextIndex = m_sortedContextsModel->mapFromSource( |
| m_sortedMessagesModel->mapToSource(index)); |
| |
| int nextRow = sortedContextIndex.row() + 1; |
| if (nextRow >= m_sortedContextsModel->rowCount()) |
| nextRow = 0; |
| sortedContextIndex = m_sortedContextsModel->index(nextRow, index.column()); |
| |
| return m_sortedMessagesModel->mapFromSource( |
| m_sortedContextsModel->mapToSource(sortedContextIndex)); |
| } |
| |
| /* |
| * See nextContext. |
| */ |
| QModelIndex MainWindow::prevContext(const QModelIndex &index) const |
| { |
| QModelIndex sortedContextIndex = m_sortedContextsModel->mapFromSource( |
| m_sortedMessagesModel->mapToSource(index)); |
| |
| int prevRow = sortedContextIndex.row() - 1; |
| if (prevRow < 0) prevRow = m_sortedContextsModel->rowCount() - 1; |
| sortedContextIndex = m_sortedContextsModel->index(prevRow, index.column()); |
| |
| return m_sortedMessagesModel->mapFromSource( |
| m_sortedContextsModel->mapToSource(sortedContextIndex)); |
| } |
| |
| QModelIndex MainWindow::nextMessage(const QModelIndex ¤tIndex, bool checkUnfinished) const |
| { |
| QModelIndex idx = currentIndex.isValid() ? currentIndex : m_sortedMessagesModel->index(0, 0); |
| do { |
| int row = 0; |
| QModelIndex par = idx.parent(); |
| if (par.isValid()) { |
| row = idx.row() + 1; |
| } else { // In case we are located on a top-level node |
| par = idx; |
| } |
| |
| if (row >= m_sortedMessagesModel->rowCount(par)) { |
| par = nextContext(par); |
| row = 0; |
| } |
| idx = m_sortedMessagesModel->index(row, idx.column(), par); |
| |
| if (!checkUnfinished) |
| return idx; |
| |
| QModelIndex item = m_sortedMessagesModel->mapToSource(idx); |
| MultiDataIndex index = m_messageModel->dataIndex(item, -1); |
| if (m_dataModel->multiMessageItem(index)->isUnfinished()) |
| return idx; |
| } while (idx != currentIndex); |
| return QModelIndex(); |
| } |
| |
| QModelIndex MainWindow::prevMessage(const QModelIndex ¤tIndex, bool checkUnfinished) const |
| { |
| QModelIndex idx = currentIndex.isValid() ? currentIndex : m_sortedMessagesModel->index(0, 0); |
| do { |
| int row = idx.row() - 1; |
| QModelIndex par = idx.parent(); |
| if (!par.isValid()) { // In case we are located on a top-level node |
| par = idx; |
| row = -1; |
| } |
| |
| if (row < 0) { |
| par = prevContext(par); |
| row = m_sortedMessagesModel->rowCount(par) - 1; |
| } |
| idx = m_sortedMessagesModel->index(row, idx.column(), par); |
| |
| if (!checkUnfinished) |
| return idx; |
| |
| QModelIndex item = m_sortedMessagesModel->mapToSource(idx); |
| MultiDataIndex index = m_messageModel->dataIndex(item, -1); |
| if (m_dataModel->multiMessageItem(index)->isUnfinished()) |
| return idx; |
| } while (idx != currentIndex); |
| return QModelIndex(); |
| } |
| |
| void MainWindow::nextUnfinished() |
| { |
| if (m_ui.actionNextUnfinished->isEnabled()) { |
| if (!next(true)) { |
| // If no Unfinished message is left, the user has finished the job. We |
| // congratulate on a job well done with this ringing bell. |
| statusBar()->showMessage(tr("No untranslated translation units left."), MessageMS); |
| qApp->beep(); |
| } |
| } |
| } |
| |
| void MainWindow::prevUnfinished() |
| { |
| if (m_ui.actionNextUnfinished->isEnabled()) { |
| if (!prev(true)) { |
| // If no Unfinished message is left, the user has finished the job. We |
| // congratulate on a job well done with this ringing bell. |
| statusBar()->showMessage(tr("No untranslated translation units left."), MessageMS); |
| qApp->beep(); |
| } |
| } |
| } |
| |
| void MainWindow::prev() |
| { |
| prev(false); |
| } |
| |
| void MainWindow::next() |
| { |
| next(false); |
| } |
| |
| bool MainWindow::prev(bool checkUnfinished) |
| { |
| QModelIndex index = prevMessage(m_messageView->currentIndex(), checkUnfinished); |
| if (index.isValid()) |
| setCurrentMessage(m_sortedMessagesModel->mapToSource(index)); |
| if (checkUnfinished) |
| m_messageEditor->setUnfinishedEditorFocus(); |
| else |
| m_messageEditor->setEditorFocus(); |
| return index.isValid(); |
| } |
| |
| bool MainWindow::next(bool checkUnfinished) |
| { |
| QModelIndex index = nextMessage(m_messageView->currentIndex(), checkUnfinished); |
| if (index.isValid()) |
| setCurrentMessage(m_sortedMessagesModel->mapToSource(index)); |
| if (checkUnfinished) |
| m_messageEditor->setUnfinishedEditorFocus(); |
| else |
| m_messageEditor->setEditorFocus(); |
| return index.isValid(); |
| } |
| |
| void MainWindow::findNext(const QString &text, DataModel::FindLocation where, bool matchCase, bool ignoreAccelerators) |
| { |
| if (text.isEmpty()) |
| return; |
| m_findText = text; |
| m_findWhere = where; |
| m_findMatchCase = matchCase ? Qt::CaseSensitive : Qt::CaseInsensitive; |
| m_findIgnoreAccelerators = ignoreAccelerators; |
| m_ui.actionFindNext->setEnabled(true); |
| findAgain(); |
| } |
| |
| void MainWindow::revalidate() |
| { |
| for (MultiDataModelIterator it(m_dataModel, -1); it.isValid(); ++it) |
| updateDanger(it, false); |
| |
| if (m_currentIndex.isValid()) |
| updateDanger(m_currentIndex, true); |
| } |
| |
| QString MainWindow::friendlyString(const QString& str) |
| { |
| QString f = str.toLower(); |
| f.replace(QRegExp(QString(QLatin1String("[.,:;!?()-]"))), QString(QLatin1String(" "))); |
| f.remove(QLatin1Char('&')); |
| return f.simplified(); |
| } |
| |
| static inline void setThemeIcon(QAction *action, const char *name, const char *fallback) |
| { |
| const QIcon fallbackIcon(MainWindow::resourcePrefix() + QLatin1String(fallback)); |
| #ifdef Q_WS_X11 |
| action->setIcon(QIcon::fromTheme(QLatin1String(name), fallbackIcon)); |
| #else |
| Q_UNUSED(name) |
| action->setIcon(fallbackIcon); |
| #endif |
| } |
| |
| void MainWindow::setupMenuBar() |
| { |
| // There are no fallback icons for these |
| #ifdef Q_WS_X11 |
| m_ui.menuRecentlyOpenedFiles->setIcon(QIcon::fromTheme(QLatin1String("document-open-recent"))); |
| m_ui.actionCloseAll->setIcon(QIcon::fromTheme(QLatin1String("window-close"))); |
| m_ui.actionExit->setIcon(QIcon::fromTheme(QLatin1String("application-exit"))); |
| m_ui.actionSelectAll->setIcon(QIcon::fromTheme(QLatin1String("edit-select-all"))); |
| #endif |
| |
| // Prefer theme icons when available for these actions |
| setThemeIcon(m_ui.actionOpen, "document-open", "/fileopen.png"); |
| setThemeIcon(m_ui.actionOpenAux, "document-open", "/fileopen.png"); |
| setThemeIcon(m_ui.actionSave, "document-save", "/filesave.png"); |
| setThemeIcon(m_ui.actionSaveAll, "document-save", "/filesave.png"); |
| setThemeIcon(m_ui.actionPrint, "document-print", "/print.png"); |
| setThemeIcon(m_ui.actionRedo, "edit-redo", "/redo.png"); |
| setThemeIcon(m_ui.actionUndo, "edit-undo", "/undo.png"); |
| setThemeIcon(m_ui.actionCut, "edit-cut", "/editcut.png"); |
| setThemeIcon(m_ui.actionCopy, "edit-copy", "/editcopy.png"); |
| setThemeIcon(m_ui.actionPaste, "edit-paste", "/editpaste.png"); |
| setThemeIcon(m_ui.actionFind, "edit-find", "/searchfind.png"); |
| |
| // No well defined theme icons for these actions |
| m_ui.actionAccelerators->setIcon(QIcon(resourcePrefix() + QLatin1String("/accelerator.png"))); |
| m_ui.actionOpenPhraseBook->setIcon(QIcon(resourcePrefix() + QLatin1String("/book.png"))); |
| m_ui.actionDoneAndNext->setIcon(QIcon(resourcePrefix() + QLatin1String("/doneandnext.png"))); |
| m_ui.actionNext->setIcon(QIcon(resourcePrefix() + QLatin1String("/next.png"))); |
| m_ui.actionNextUnfinished->setIcon(QIcon(resourcePrefix() + QLatin1String("/nextunfinished.png"))); |
| m_ui.actionPhraseMatches->setIcon(QIcon(resourcePrefix() + QLatin1String("/phrase.png"))); |
| m_ui.actionEndingPunctuation->setIcon(QIcon(resourcePrefix() + QLatin1String("/punctuation.png"))); |
| m_ui.actionPrev->setIcon(QIcon(resourcePrefix() + QLatin1String("/prev.png"))); |
| m_ui.actionPrevUnfinished->setIcon(QIcon(resourcePrefix() + QLatin1String("/prevunfinished.png"))); |
| m_ui.actionPlaceMarkerMatches->setIcon(QIcon(resourcePrefix() + QLatin1String("/validateplacemarkers.png"))); |
| m_ui.actionWhatsThis->setIcon(QIcon(resourcePrefix() + QLatin1String("/whatsthis.png"))); |
| |
| // File menu |
| connect(m_ui.menuFile, SIGNAL(aboutToShow()), SLOT(fileAboutToShow())); |
| connect(m_ui.actionOpen, SIGNAL(triggered()), this, SLOT(open())); |
| connect(m_ui.actionOpenAux, SIGNAL(triggered()), this, SLOT(openAux())); |
| connect(m_ui.actionSaveAll, SIGNAL(triggered()), this, SLOT(saveAll())); |
| connect(m_ui.actionSave, SIGNAL(triggered()), this, SLOT(save())); |
| connect(m_ui.actionSaveAs, SIGNAL(triggered()), this, SLOT(saveAs())); |
| connect(m_ui.actionReleaseAll, SIGNAL(triggered()), this, SLOT(releaseAll())); |
| connect(m_ui.actionRelease, SIGNAL(triggered()), this, SLOT(release())); |
| connect(m_ui.actionReleaseAs, SIGNAL(triggered()), this, SLOT(releaseAs())); |
| connect(m_ui.actionPrint, SIGNAL(triggered()), this, SLOT(print())); |
| connect(m_ui.actionClose, SIGNAL(triggered()), this, SLOT(closeFile())); |
| connect(m_ui.actionCloseAll, SIGNAL(triggered()), this, SLOT(closeAll())); |
| connect(m_ui.actionExit, SIGNAL(triggered()), this, SLOT(close())); |
| |
| // Edit menu |
| connect(m_ui.menuEdit, SIGNAL(aboutToShow()), SLOT(editAboutToShow())); |
| |
| connect(m_ui.actionUndo, SIGNAL(triggered()), m_messageEditor, SLOT(undo())); |
| connect(m_messageEditor, SIGNAL(undoAvailable(bool)), m_ui.actionUndo, SLOT(setEnabled(bool))); |
| |
| connect(m_ui.actionRedo, SIGNAL(triggered()), m_messageEditor, SLOT(redo())); |
| connect(m_messageEditor, SIGNAL(redoAvailable(bool)), m_ui.actionRedo, SLOT(setEnabled(bool))); |
| |
| connect(m_ui.actionCopy, SIGNAL(triggered()), m_messageEditor, SLOT(copy())); |
| connect(m_messageEditor, SIGNAL(copyAvailable(bool)), m_ui.actionCopy, SLOT(setEnabled(bool))); |
| |
| connect(m_messageEditor, SIGNAL(cutAvailable(bool)), m_ui.actionCut, SLOT(setEnabled(bool))); |
| connect(m_ui.actionCut, SIGNAL(triggered()), m_messageEditor, SLOT(cut())); |
| |
| connect(m_messageEditor, SIGNAL(pasteAvailable(bool)), m_ui.actionPaste, SLOT(setEnabled(bool))); |
| connect(m_ui.actionPaste, SIGNAL(triggered()), m_messageEditor, SLOT(paste())); |
| |
| connect(m_ui.actionSelectAll, SIGNAL(triggered()), m_messageEditor, SLOT(selectAll())); |
| connect(m_ui.actionFind, SIGNAL(triggered()), m_findDialog, SLOT(find())); |
| connect(m_ui.actionFindNext, SIGNAL(triggered()), this, SLOT(findAgain())); |
| connect(m_ui.actionSearchAndTranslate, SIGNAL(triggered()), this, SLOT(showTranslateDialog())); |
| connect(m_ui.actionBatchTranslation, SIGNAL(triggered()), this, SLOT(showBatchTranslateDialog())); |
| connect(m_ui.actionTranslationFileSettings, SIGNAL(triggered()), this, SLOT(showTranslationSettings())); |
| |
| connect(m_batchTranslateDialog, SIGNAL(finished()), SLOT(refreshItemViews())); |
| |
| // Translation menu |
| // when updating the accelerators, remember the status bar |
| connect(m_ui.actionPrevUnfinished, SIGNAL(triggered()), this, SLOT(prevUnfinished())); |
| connect(m_ui.actionNextUnfinished, SIGNAL(triggered()), this, SLOT(nextUnfinished())); |
| connect(m_ui.actionNext, SIGNAL(triggered()), this, SLOT(next())); |
| connect(m_ui.actionPrev, SIGNAL(triggered()), this, SLOT(prev())); |
| connect(m_ui.actionDoneAndNext, SIGNAL(triggered()), this, SLOT(doneAndNext())); |
| connect(m_ui.actionBeginFromSource, SIGNAL(triggered()), m_messageEditor, SLOT(beginFromSource())); |
| connect(m_messageEditor, SIGNAL(beginFromSourceAvailable(bool)), m_ui.actionBeginFromSource, SLOT(setEnabled(bool))); |
| |
| // Phrasebook menu |
| connect(m_ui.actionNewPhraseBook, SIGNAL(triggered()), this, SLOT(newPhraseBook())); |
| connect(m_ui.actionOpenPhraseBook, SIGNAL(triggered()), this, SLOT(openPhraseBook())); |
| connect(m_ui.menuClosePhraseBook, SIGNAL(triggered(QAction*)), |
| this, SLOT(closePhraseBook(QAction*))); |
| connect(m_ui.menuEditPhraseBook, SIGNAL(triggered(QAction*)), |
| this, SLOT(editPhraseBook(QAction*))); |
| connect(m_ui.menuPrintPhraseBook, SIGNAL(triggered(QAction*)), |
| this, SLOT(printPhraseBook(QAction*))); |
| connect(m_ui.actionAddToPhraseBook, SIGNAL(triggered()), this, SLOT(addToPhraseBook())); |
| |
| // Validation menu |
| connect(m_ui.actionAccelerators, SIGNAL(triggered()), this, SLOT(revalidate())); |
| connect(m_ui.actionEndingPunctuation, SIGNAL(triggered()), this, SLOT(revalidate())); |
| connect(m_ui.actionPhraseMatches, SIGNAL(triggered()), this, SLOT(revalidate())); |
| connect(m_ui.actionPlaceMarkerMatches, SIGNAL(triggered()), this, SLOT(revalidate())); |
| |
| // View menu |
| connect(m_ui.actionResetSorting, SIGNAL(triggered()), this, SLOT(resetSorting())); |
| connect(m_ui.actionDisplayGuesses, SIGNAL(triggered()), m_phraseView, SLOT(toggleGuessing())); |
| connect(m_ui.actionStatistics, SIGNAL(triggered()), this, SLOT(toggleStatistics())); |
| connect(m_ui.menuView, SIGNAL(aboutToShow()), this, SLOT(updateViewMenu())); |
| m_ui.menuViewViews->addAction(m_contextDock->toggleViewAction()); |
| m_ui.menuViewViews->addAction(m_messagesDock->toggleViewAction()); |
| m_ui.menuViewViews->addAction(m_phrasesDock->toggleViewAction()); |
| m_ui.menuViewViews->addAction(m_sourceAndFormDock->toggleViewAction()); |
| m_ui.menuViewViews->addAction(m_errorsDock->toggleViewAction()); |
| |
| #if defined(Q_WS_MAC) |
| // Window menu |
| QMenu *windowMenu = new QMenu(tr("&Window"), this); |
| menuBar()->insertMenu(m_ui.menuHelp->menuAction(), windowMenu); |
| windowMenu->addAction(tr("Minimize"), this, |
| SLOT(showMinimized()), QKeySequence(tr("Ctrl+M"))); |
| #endif |
| |
| // Help |
| connect(m_ui.actionManual, SIGNAL(triggered()), this, SLOT(manual())); |
| connect(m_ui.actionAbout, SIGNAL(triggered()), this, SLOT(about())); |
| connect(m_ui.actionAboutQt, SIGNAL(triggered()), this, SLOT(aboutQt())); |
| connect(m_ui.actionWhatsThis, SIGNAL(triggered()), this, SLOT(onWhatsThis())); |
| |
| connect(m_ui.menuRecentlyOpenedFiles, SIGNAL(triggered(QAction*)), this, |
| SLOT(recentFileActivated(QAction*))); |
| |
| m_ui.actionManual->setWhatsThis(tr("Display the manual for %1.").arg(tr("Qt Linguist"))); |
| m_ui.actionAbout->setWhatsThis(tr("Display information about %1.").arg(tr("Qt Linguist"))); |
| m_ui.actionDoneAndNext->setShortcuts(QList<QKeySequence>() |
| << QKeySequence(QLatin1String("Ctrl+Return")) |
| << QKeySequence(QLatin1String("Ctrl+Enter"))); |
| |
| // Disable the Close/Edit/Print phrasebook menuitems if they are not loaded |
| connect(m_ui.menuPhrases, SIGNAL(aboutToShow()), this, SLOT(setupPhrase())); |
| |
| connect(m_ui.menuRecentlyOpenedFiles, SIGNAL(aboutToShow()), SLOT(setupRecentFilesMenu())); |
| } |
| |
| void MainWindow::updateActiveModel(int model) |
| { |
| if (model >= 0) |
| updateLatestModel(model); |
| } |
| |
| // Arriving here implies that the messageEditor does not have focus |
| void MainWindow::updateLatestModel(const QModelIndex &index) |
| { |
| if (index.column() && (index.column() - 1 < m_dataModel->modelCount())) |
| updateLatestModel(index.column() - 1); |
| } |
| |
| void MainWindow::updateLatestModel(int model) |
| { |
| m_currentIndex = MultiDataIndex(model, m_currentIndex.context(), m_currentIndex.message()); |
| bool enable = false; |
| bool enableRw = false; |
| if (model >= 0) { |
| enable = true; |
| if (m_dataModel->isModelWritable(model)) |
| enableRw = true; |
| |
| if (m_currentIndex.isValid()) { |
| if (MessageItem *item = m_dataModel->messageItem(m_currentIndex)) { |
| if (!item->fileName().isEmpty() && hasFormPreview(item->fileName())) |
| m_formPreviewView->setSourceContext(model, item); |
| if (enableRw && !item->isObsolete()) |
| m_phraseView->setSourceText(model, item->text()); |
| else |
| m_phraseView->setSourceText(-1, QString()); |
| } else { |
| m_phraseView->setSourceText(-1, QString()); |
| } |
| } |
| } |
| m_ui.actionSave->setEnabled(enableRw); |
| m_ui.actionSaveAs->setEnabled(enableRw); |
| m_ui.actionRelease->setEnabled(enableRw); |
| m_ui.actionReleaseAs->setEnabled(enableRw); |
| m_ui.actionClose->setEnabled(enable); |
| m_ui.actionTranslationFileSettings->setEnabled(enableRw); |
| m_ui.actionSearchAndTranslate->setEnabled(enableRw); |
| // cut & paste - edit only |
| updatePhraseBookActions(); |
| updateStatistics(); |
| } |
| |
| // Note for *AboutToShow: Due to the delayed nature, only actions without shortcuts |
| // and representations outside the menu may be setEnabled()/setVisible() here. |
| |
| void MainWindow::fileAboutToShow() |
| { |
| if (m_fileActiveModel != m_currentIndex.model()) { |
| // We rename the actions so the shortcuts need not be reassigned. |
| bool en; |
| if (m_dataModel->modelCount() > 1) { |
| if (m_currentIndex.model() >= 0) { |
| QString fn = QFileInfo(m_dataModel->srcFileName(m_currentIndex.model())).baseName(); |
| m_ui.actionSave->setText(tr("&Save '%1'").arg(fn)); |
| m_ui.actionSaveAs->setText(tr("Save '%1' &As...").arg(fn)); |
| m_ui.actionRelease->setText(tr("Release '%1'").arg(fn)); |
| m_ui.actionReleaseAs->setText(tr("Release '%1' As...").arg(fn)); |
| m_ui.actionClose->setText(tr("&Close '%1'").arg(fn)); |
| } else { |
| m_ui.actionSave->setText(tr("&Save")); |
| m_ui.actionSaveAs->setText(tr("Save &As...")); |
| m_ui.actionRelease->setText(tr("Release")); |
| m_ui.actionReleaseAs->setText(tr("Release As...")); |
| m_ui.actionClose->setText(tr("&Close")); |
| } |
| |
| m_ui.actionSaveAll->setText(tr("Save All")); |
| m_ui.actionReleaseAll->setText(tr("&Release All")); |
| m_ui.actionCloseAll->setText(tr("Close All")); |
| en = true; |
| } else { |
| m_ui.actionSaveAs->setText(tr("Save &As...")); |
| m_ui.actionReleaseAs->setText(tr("Release As...")); |
| |
| m_ui.actionSaveAll->setText(tr("&Save")); |
| m_ui.actionReleaseAll->setText(tr("&Release")); |
| m_ui.actionCloseAll->setText(tr("&Close")); |
| en = false; |
| } |
| m_ui.actionSave->setVisible(en); |
| m_ui.actionRelease->setVisible(en); |
| m_ui.actionClose->setVisible(en); |
| m_fileActiveModel = m_currentIndex.model(); |
| } |
| } |
| |
| void MainWindow::editAboutToShow() |
| { |
| if (m_editActiveModel != m_currentIndex.model()) { |
| if (m_currentIndex.model() >= 0 && m_dataModel->modelCount() > 1) { |
| QString fn = QFileInfo(m_dataModel->srcFileName(m_currentIndex.model())).baseName(); |
| m_ui.actionTranslationFileSettings->setText(tr("Translation File &Settings for '%1'...").arg(fn)); |
| m_ui.actionBatchTranslation->setText(tr("&Batch Translation of '%1'...").arg(fn)); |
| m_ui.actionSearchAndTranslate->setText(tr("Search And &Translate in '%1'...").arg(fn)); |
| } else { |
| m_ui.actionTranslationFileSettings->setText(tr("Translation File &Settings...")); |
| m_ui.actionBatchTranslation->setText(tr("&Batch Translation...")); |
| m_ui.actionSearchAndTranslate->setText(tr("Search And &Translate...")); |
| } |
| m_editActiveModel = m_currentIndex.model(); |
| } |
| } |
| |
| void MainWindow::updateViewMenu() |
| { |
| bool check = m_statistics ? m_statistics->isVisible() : false; |
| m_ui.actionStatistics->setChecked(check); |
| } |
| |
| void MainWindow::showContextDock() |
| { |
| m_contextDock->show(); |
| m_contextDock->raise(); |
| } |
| |
| void MainWindow::showMessagesDock() |
| { |
| m_messagesDock->show(); |
| m_messagesDock->raise(); |
| } |
| |
| void MainWindow::showPhrasesDock() |
| { |
| m_phrasesDock->show(); |
| m_phrasesDock->raise(); |
| } |
| |
| void MainWindow::showSourceCodeDock() |
| { |
| m_sourceAndFormDock->show(); |
| m_sourceAndFormDock->raise(); |
| } |
| |
| void MainWindow::showErrorDock() |
| { |
| m_errorsDock->show(); |
| m_errorsDock->raise(); |
| } |
| |
| void MainWindow::onWhatsThis() |
| { |
| QWhatsThis::enterWhatsThisMode(); |
| } |
| |
| void MainWindow::setupToolBars() |
| { |
| QToolBar *filet = new QToolBar(this); |
| filet->setObjectName(QLatin1String("FileToolbar")); |
| filet->setWindowTitle(tr("File")); |
| this->addToolBar(filet); |
| m_ui.menuToolbars->addAction(filet->toggleViewAction()); |
| |
| QToolBar *editt = new QToolBar(this); |
| editt->setVisible(false); |
| editt->setObjectName(QLatin1String("EditToolbar")); |
| editt->setWindowTitle(tr("Edit")); |
| this->addToolBar(editt); |
| m_ui.menuToolbars->addAction(editt->toggleViewAction()); |
| |
| QToolBar *translationst = new QToolBar(this); |
| translationst->setObjectName(QLatin1String("TranslationToolbar")); |
| translationst->setWindowTitle(tr("Translation")); |
| this->addToolBar(translationst); |
| m_ui.menuToolbars->addAction(translationst->toggleViewAction()); |
| |
| QToolBar *validationt = new QToolBar(this); |
| validationt->setObjectName(QLatin1String("ValidationToolbar")); |
| validationt->setWindowTitle(tr("Validation")); |
| this->addToolBar(validationt); |
| m_ui.menuToolbars->addAction(validationt->toggleViewAction()); |
| |
| QToolBar *helpt = new QToolBar(this); |
| helpt->setVisible(false); |
| helpt->setObjectName(QLatin1String("HelpToolbar")); |
| helpt->setWindowTitle(tr("Help")); |
| this->addToolBar(helpt); |
| m_ui.menuToolbars->addAction(helpt->toggleViewAction()); |
| |
| |
| filet->addAction(m_ui.actionOpen); |
| filet->addAction(m_ui.actionSaveAll); |
| filet->addAction(m_ui.actionPrint); |
| filet->addSeparator(); |
| filet->addAction(m_ui.actionOpenPhraseBook); |
| |
| editt->addAction(m_ui.actionUndo); |
| editt->addAction(m_ui.actionRedo); |
| editt->addSeparator(); |
| editt->addAction(m_ui.actionCut); |
| editt->addAction(m_ui.actionCopy); |
| editt->addAction(m_ui.actionPaste); |
| editt->addSeparator(); |
| editt->addAction(m_ui.actionFind); |
| |
| translationst->addAction(m_ui.actionPrev); |
| translationst->addAction(m_ui.actionNext); |
| translationst->addAction(m_ui.actionPrevUnfinished); |
| translationst->addAction(m_ui.actionNextUnfinished); |
| translationst->addAction(m_ui.actionDoneAndNext); |
| |
| validationt->addAction(m_ui.actionAccelerators); |
| validationt->addAction(m_ui.actionEndingPunctuation); |
| validationt->addAction(m_ui.actionPhraseMatches); |
| validationt->addAction(m_ui.actionPlaceMarkerMatches); |
| |
| helpt->addAction(m_ui.actionWhatsThis); |
| } |
| |
| QModelIndex MainWindow::setMessageViewRoot(const QModelIndex &index) |
| { |
| const QModelIndex &sortedContextIndex = m_sortedMessagesModel->mapFromSource(index); |
| const QModelIndex &trueContextIndex = m_sortedMessagesModel->index(sortedContextIndex.row(), 0); |
| if (m_messageView->rootIndex() != trueContextIndex) |
| m_messageView->setRootIndex(trueContextIndex); |
| return trueContextIndex; |
| } |
| |
| /* |
| * Updates the selected entries in the context and message views. |
| */ |
| void MainWindow::setCurrentMessage(const QModelIndex &index) |
| { |
| const QModelIndex &contextIndex = m_messageModel->parent(index); |
| if (!contextIndex.isValid()) |
| return; |
| |
| const QModelIndex &trueIndex = m_messageModel->index(contextIndex.row(), index.column(), QModelIndex()); |
| m_settingCurrentMessage = true; |
| m_contextView->setCurrentIndex(m_sortedContextsModel->mapFromSource(trueIndex)); |
| m_settingCurrentMessage = false; |
| |
| setMessageViewRoot(contextIndex); |
| m_messageView->setCurrentIndex(m_sortedMessagesModel->mapFromSource(index)); |
| } |
| |
| void MainWindow::setCurrentMessage(const QModelIndex &index, int model) |
| { |
| const QModelIndex &theIndex = m_messageModel->index(index.row(), model + 1, index.parent()); |
| setCurrentMessage(theIndex); |
| m_messageEditor->setEditorFocus(model); |
| } |
| |
| QModelIndex MainWindow::currentContextIndex() const |
| { |
| return m_sortedContextsModel->mapToSource(m_contextView->currentIndex()); |
| } |
| |
| QModelIndex MainWindow::currentMessageIndex() const |
| { |
| return m_sortedMessagesModel->mapToSource(m_messageView->currentIndex()); |
| } |
| |
| PhraseBook *MainWindow::openPhraseBook(const QString& name) |
| { |
| PhraseBook *pb = new PhraseBook(); |
| bool langGuessed; |
| if (!pb->load(name, &langGuessed)) { |
| QMessageBox::warning(this, tr("Qt Linguist"), |
| tr("Cannot read from phrase book '%1'.").arg(name)); |
| delete pb; |
| return 0; |
| } |
| if (langGuessed) { |
| if (!m_translationSettingsDialog) |
| m_translationSettingsDialog = new TranslationSettingsDialog(this); |
| m_translationSettingsDialog->setPhraseBook(pb); |
| m_translationSettingsDialog->exec(); |
| } |
| |
| m_phraseBooks.append(pb); |
| |
| QAction *a = m_ui.menuClosePhraseBook->addAction(pb->friendlyPhraseBookName()); |
| m_phraseBookMenu[PhraseCloseMenu].insert(a, pb); |
| a->setWhatsThis(tr("Close this phrase book.")); |
| |
| a = m_ui.menuEditPhraseBook->addAction(pb->friendlyPhraseBookName()); |
| m_phraseBookMenu[PhraseEditMenu].insert(a, pb); |
| a->setWhatsThis(tr("Enables you to add, modify, or delete" |
| " entries in this phrase book.")); |
| |
| a = m_ui.menuPrintPhraseBook->addAction(pb->friendlyPhraseBookName()); |
| m_phraseBookMenu[PhrasePrintMenu].insert(a, pb); |
| a->setWhatsThis(tr("Print the entries in this phrase book.")); |
| |
| connect(pb, SIGNAL(listChanged()), this, SLOT(updatePhraseDicts())); |
| updatePhraseDicts(); |
| updatePhraseBookActions(); |
| |
| return pb; |
| } |
| |
| bool MainWindow::savePhraseBook(QString *name, PhraseBook &pb) |
| { |
| if (!name->contains(QLatin1Char('.'))) |
| *name += QLatin1String(".qph"); |
| |
| if (!pb.save(*name)) { |
| QMessageBox::warning(this, tr("Qt Linguist"), |
| tr("Cannot create phrase book '%1'.").arg(*name)); |
| return false; |
| } |
| return true; |
| } |
| |
| bool MainWindow::maybeSavePhraseBook(PhraseBook *pb) |
| { |
| if (pb->isModified()) |
| switch (QMessageBox::information(this, tr("Qt Linguist"), |
| tr("Do you want to save phrase book '%1'?").arg(pb->friendlyPhraseBookName()), |
| QMessageBox::Yes | QMessageBox::Default, |
| QMessageBox::No, |
| QMessageBox::Cancel | QMessageBox::Escape)) |
| { |
| case QMessageBox::Cancel: |
| return false; |
| case QMessageBox::Yes: |
| if (!pb->save(pb->fileName())) |
| return false; |
| break; |
| case QMessageBox::No: |
| break; |
| } |
| return true; |
| } |
| |
| bool MainWindow::closePhraseBooks() |
| { |
| foreach(PhraseBook *phraseBook, m_phraseBooks) |
| if (!maybeSavePhraseBook(phraseBook)) |
| return false; |
| return true; |
| } |
| |
| void MainWindow::updateProgress() |
| { |
| int numEditable = m_dataModel->getNumEditable(); |
| int numFinished = m_dataModel->getNumFinished(); |
| if (!m_dataModel->modelCount()) |
| m_progressLabel->setText(QString(QLatin1String(" "))); |
| else |
| m_progressLabel->setText(QString(QLatin1String(" %1/%2 ")) |
| .arg(numFinished).arg(numEditable)); |
| bool enable = numFinished != numEditable; |
| m_ui.actionPrevUnfinished->setEnabled(enable); |
| m_ui.actionNextUnfinished->setEnabled(enable); |
| m_ui.actionDoneAndNext->setEnabled(enable); |
| |
| m_ui.actionPrev->setEnabled(m_dataModel->contextCount() > 0); |
| m_ui.actionNext->setEnabled(m_dataModel->contextCount() > 0); |
| } |
| |
| void MainWindow::updatePhraseBookActions() |
| { |
| bool phraseBookLoaded = (m_currentIndex.model() >= 0) && !m_phraseBooks.isEmpty(); |
| m_ui.actionBatchTranslation->setEnabled(m_dataModel->contextCount() > 0 && phraseBookLoaded |
| && m_dataModel->isModelWritable(m_currentIndex.model())); |
| m_ui.actionAddToPhraseBook->setEnabled(currentMessageIndex().isValid() && phraseBookLoaded); |
| } |
| |
| void MainWindow::updatePhraseDictInternal(int model) |
| { |
| QHash<QString, QList<Phrase *> > &pd = m_phraseDict[model]; |
| |
| pd.clear(); |
| foreach (PhraseBook *pb, m_phraseBooks) { |
| bool before; |
| if (pb->language() != QLocale::C && m_dataModel->language(model) != QLocale::C) { |
| if (pb->language() != m_dataModel->language(model)) |
| continue; |
| before = (pb->country() == m_dataModel->model(model)->country()); |
| } else { |
| before = false; |
| } |
| foreach (Phrase *p, pb->phrases()) { |
| QString f = friendlyString(p->source()); |
| if (f.length() > 0) { |
| f = f.split(QLatin1Char(' ')).first(); |
| if (!pd.contains(f)) { |
| pd.insert(f, QList<Phrase *>()); |
| } |
| if (before) |
| pd[f].prepend(p); |
| else |
| pd[f].append(p); |
| } |
| } |
| } |
| } |
| |
| void MainWindow::updatePhraseDict(int model) |
| { |
| updatePhraseDictInternal(model); |
| m_phraseView->update(); |
| } |
| |
| void MainWindow::updatePhraseDicts() |
| { |
| for (int i = 0; i < m_phraseDict.size(); ++i) |
| if (!m_dataModel->isModelWritable(i)) |
| m_phraseDict[i].clear(); |
| else |
| updatePhraseDictInternal(i); |
| revalidate(); |
| m_phraseView->update(); |
| } |
| |
| static bool haveMnemonic(const QString &str) |
| { |
| for (const ushort *p = (ushort *)str.constData();; ) { // Assume null-termination |
| ushort c = *p++; |
| if (!c) |
| break; |
| if (c == '&') { |
| c = *p++; |
| if (!c) |
| return false; |
| // "Nobody" ever really uses these alt-space, and they are highly annoying |
| // because we get a lot of false positives. |
| if (c != '&' && c != ' ' && QChar(c).isPrint()) { |
| const ushort *pp = p; |
| for (; *p < 256 && isalpha(*p); p++) ; |
| if (pp == p || *p != ';') |
| return true; |
| // This looks like a HTML &entity;, so ignore it. As a HTML string |
| // won't contain accels anyway, we can stop scanning here. |
| break; |
| } |
| } |
| } |
| return false; |
| } |
| |
| void MainWindow::updateDanger(const MultiDataIndex &index, bool verbose) |
| { |
| MultiDataIndex curIdx = index; |
| m_errorsView->clear(); |
| |
| QString source; |
| for (int mi = 0; mi < m_dataModel->modelCount(); ++mi) { |
| if (!m_dataModel->isModelWritable(mi)) |
| continue; |
| curIdx.setModel(mi); |
| MessageItem *m = m_dataModel->messageItem(curIdx); |
| if (!m || m->isObsolete()) |
| continue; |
| |
| bool danger = false; |
| if (m->message().isTranslated()) { |
| if (source.isEmpty()) { |
| source = m->pluralText(); |
| if (source.isEmpty()) |
| source = m->text(); |
| } |
| QStringList translations = m->translations(); |
| |
| // Truncated variants are permitted to be "denormalized" |
| for (int i = 0; i < translations.count(); ++i) { |
| int sep = translations.at(i).indexOf(QChar(Translator::BinaryVariantSeparator)); |
| if (sep >= 0) |
| translations[i].truncate(sep); |
| } |
| |
| if (m_ui.actionAccelerators->isChecked()) { |
| bool sk = haveMnemonic(source); |
| bool tk = true; |
| for (int i = 0; i < translations.count() && tk; ++i) { |
| tk &= haveMnemonic(translations[i]); |
| } |
| |
| if (!sk && tk) { |
| if (verbose) |
| m_errorsView->addError(mi, ErrorsView::SuperfluousAccelerator); |
| danger = true; |
| } else if (sk && !tk) { |
| if (verbose) |
| m_errorsView->addError(mi, ErrorsView::MissingAccelerator); |
| danger = true; |
| } |
| } |
| if (m_ui.actionEndingPunctuation->isChecked()) { |
| bool endingok = true; |
| for (int i = 0; i < translations.count() && endingok; ++i) { |
| endingok &= (ending(source, m_dataModel->sourceLanguage(mi)) == |
| ending(translations[i], m_dataModel->language(mi))); |
| } |
| |
| if (!endingok) { |
| if (verbose) |
| m_errorsView->addError(mi, ErrorsView::PunctuationDiffer); |
| danger = true; |
| } |
| } |
| if (m_ui.actionPhraseMatches->isChecked()) { |
| QString fsource = friendlyString(source); |
| QString ftranslation = friendlyString(translations.first()); |
| QStringList lookupWords = fsource.split(QLatin1Char(' ')); |
| |
| bool phraseFound; |
| foreach (const QString &s, lookupWords) { |
| if (m_phraseDict[mi].contains(s)) { |
| phraseFound = true; |
| foreach (const Phrase *p, m_phraseDict[mi].value(s)) { |
| if (fsource == friendlyString(p->source())) { |
| if (ftranslation.indexOf(friendlyString(p->target())) >= 0) { |
| phraseFound = true; |
| break; |
| } else { |
| phraseFound = false; |
| } |
| } |
| } |
| if (!phraseFound) { |
| if (verbose) |
| m_errorsView->addError(mi, ErrorsView::IgnoredPhrasebook, s); |
| danger = true; |
| } |
| } |
| } |
| } |
| |
| if (m_ui.actionPlaceMarkerMatches->isChecked()) { |
| // Stores the occurrence count of the place markers in the map placeMarkerIndexes. |
| // i.e. the occurrence count of %1 is stored at placeMarkerIndexes[1], |
| // count of %2 is stored at placeMarkerIndexes[2] etc. |
| // In the first pass, it counts all place markers in the sourcetext. |
| // In the second pass it (de)counts all place markers in the translation. |
| // When finished, all elements should have returned to a count of 0, |
| // if not there is a mismatch |
| // between place markers in the source text and the translation text. |
| QHash<int, int> placeMarkerIndexes; |
| QString translation; |
| int numTranslations = translations.count(); |
| for (int pass = 0; pass < numTranslations + 1; ++pass) { |
| const QChar *uc_begin = source.unicode(); |
| const QChar *uc_end = uc_begin + source.length(); |
| if (pass >= 1) { |
| translation = translations[pass - 1]; |
| uc_begin = translation.unicode(); |
| uc_end = uc_begin + translation.length(); |
| } |
| const QChar *c = uc_begin; |
| while (c < uc_end) { |
| if (c->unicode() == '%') { |
| const QChar *escape_start = ++c; |
| while (c->isDigit()) |
| ++c; |
| const QChar *escape_end = c; |
| bool ok = true; |
| int markerIndex = QString::fromRawData( |
| escape_start, escape_end - escape_start).toInt(&ok); |
| if (ok) |
| placeMarkerIndexes[markerIndex] += (pass == 0 ? numTranslations : -1); |
| } |
| ++c; |
| } |
| } |
| |
| foreach (int i, placeMarkerIndexes) { |
| if (i != 0) { |
| if (verbose) |
| m_errorsView->addError(mi, ErrorsView::PlaceMarkersDiffer); |
| danger = true; |
| break; |
| } |
| } |
| |
| // Piggy-backed on the general place markers, we check the plural count marker. |
| if (m->message().isPlural()) { |
| for (int i = 0; i < numTranslations; ++i) |
| if (m_dataModel->model(mi)->countRefNeeds().at(i) |
| && !translations[i].contains(QLatin1String("%n"))) { |
| if (verbose) |
| m_errorsView->addError(mi, ErrorsView::NumerusMarkerMissing); |
| danger = true; |
| break; |
| } |
| } |
| } |
| } |
| |
| if (danger != m->danger()) |
| m_dataModel->setDanger(curIdx, danger); |
| } |
| |
| if (verbose) |
| statusBar()->showMessage(m_errorsView->firstError()); |
| } |
| |
| void MainWindow::readConfig() |
| { |
| QSettings config; |
| |
| QRect r(pos(), size()); |
| restoreGeometry(config.value(settingPath("Geometry/WindowGeometry")).toByteArray()); |
| restoreState(config.value(settingPath("MainWindowState")).toByteArray()); |
| |
| m_ui.actionAccelerators->setChecked( |
| config.value(settingPath("Validators/Accelerator"), true).toBool()); |
| m_ui.actionEndingPunctuation->setChecked( |
| config.value(settingPath("Validators/EndingPunctuation"), true).toBool()); |
| m_ui.actionPhraseMatches->setChecked( |
| config.value(settingPath("Validators/PhraseMatch"), true).toBool()); |
| m_ui.actionPlaceMarkerMatches->setChecked( |
| config.value(settingPath("Validators/PlaceMarkers"), true).toBool()); |
| m_ui.actionLengthVariants->setChecked( |
| config.value(settingPath("Options/LengthVariants"), false).toBool()); |
| |
| recentFiles().readConfig(); |
| |
| int size = config.beginReadArray(settingPath("OpenedPhraseBooks")); |
| for (int i = 0; i < size; ++i) { |
| config.setArrayIndex(i); |
| openPhraseBook(config.value(QLatin1String("FileName")).toString()); |
| } |
| config.endArray(); |
| } |
| |
| void MainWindow::writeConfig() |
| { |
| QSettings config; |
| config.setValue(settingPath("Geometry/WindowGeometry"), |
| saveGeometry()); |
| config.setValue(settingPath("Validators/Accelerator"), |
| m_ui.actionAccelerators->isChecked()); |
| config.setValue(settingPath("Validators/EndingPunctuation"), |
| m_ui.actionEndingPunctuation->isChecked()); |
| config.setValue(settingPath("Validators/PhraseMatch"), |
| m_ui.actionPhraseMatches->isChecked()); |
| config.setValue(settingPath("Validators/PlaceMarkers"), |
| m_ui.actionPlaceMarkerMatches->isChecked()); |
| config.setValue(settingPath("Options/LengthVariants"), |
| m_ui.actionLengthVariants->isChecked()); |
| config.setValue(settingPath("MainWindowState"), |
| saveState()); |
| recentFiles().writeConfig(); |
| |
| config.beginWriteArray(settingPath("OpenedPhraseBooks"), |
| m_phraseBooks.size()); |
| for (int i = 0; i < m_phraseBooks.size(); ++i) { |
| config.setArrayIndex(i); |
| config.setValue(QLatin1String("FileName"), m_phraseBooks.at(i)->fileName()); |
| } |
| config.endArray(); |
| } |
| |
| void MainWindow::setupRecentFilesMenu() |
| { |
| m_ui.menuRecentlyOpenedFiles->clear(); |
| foreach (const QStringList &strList, recentFiles().filesLists()) |
| if (strList.size() == 1) { |
| const QString &str = strList.first(); |
| m_ui.menuRecentlyOpenedFiles->addAction( |
| DataModel::prettifyFileName(str))->setData(str); |
| } else { |
| QMenu *menu = m_ui.menuRecentlyOpenedFiles->addMenu( |
| MultiDataModel::condenseFileNames( |
| MultiDataModel::prettifyFileNames(strList))); |
| menu->addAction(tr("All"))->setData(strList); |
| foreach (const QString &str, strList) |
| menu->addAction(DataModel::prettifyFileName(str))->setData(str); |
| } |
| } |
| |
| void MainWindow::recentFileActivated(QAction *action) |
| { |
| openFiles(action->data().toStringList()); |
| } |
| |
| void MainWindow::toggleStatistics() |
| { |
| if (m_ui.actionStatistics->isChecked()) { |
| if (!m_statistics) { |
| m_statistics = new Statistics(this); |
| connect(m_dataModel, SIGNAL(statsChanged(int,int,int,int,int,int)), |
| m_statistics, SLOT(updateStats(int,int,int,int,int,int))); |
| } |
| m_statistics->show(); |
| updateStatistics(); |
| } |
| else if (m_statistics) { |
| m_statistics->close(); |
| } |
| } |
| |
| void MainWindow::maybeUpdateStatistics(const MultiDataIndex &index) |
| { |
| if (index.model() == m_currentIndex.model()) |
| updateStatistics(); |
| } |
| |
| void MainWindow::updateStatistics() |
| { |
| // don't call this if stats dialog is not open |
| // because this can be slow... |
| if (!m_statistics || !m_statistics->isVisible() || m_currentIndex.model() < 0) |
| return; |
| |
| m_dataModel->model(m_currentIndex.model())->updateStatistics(); |
| } |
| |
| void MainWindow::showTranslationSettings(int model) |
| { |
| if (!m_translationSettingsDialog) |
| m_translationSettingsDialog = new TranslationSettingsDialog(this); |
| m_translationSettingsDialog->setDataModel(m_dataModel->model(model)); |
| m_translationSettingsDialog->exec(); |
| } |
| |
| void MainWindow::showTranslationSettings() |
| { |
| showTranslationSettings(m_currentIndex.model()); |
| } |
| |
| bool MainWindow::eventFilter(QObject *object, QEvent *event) |
| { |
| if (event->type() == QEvent::DragEnter) { |
| QDragEnterEvent *e = static_cast<QDragEnterEvent*>(event); |
| if (e->mimeData()->hasFormat(QLatin1String("text/uri-list"))) { |
| e->acceptProposedAction(); |
| return true; |
| } |
| } else if (event->type() == QEvent::Drop) { |
| QDropEvent *e = static_cast<QDropEvent*>(event); |
| if (!e->mimeData()->hasFormat(QLatin1String("text/uri-list"))) |
| return false; |
| QStringList urls; |
| foreach (QUrl url, e->mimeData()->urls()) |
| if (!url.toLocalFile().isEmpty()) |
| urls << url.toLocalFile(); |
| if (!urls.isEmpty()) |
| openFiles(urls); |
| e->acceptProposedAction(); |
| return true; |
| } else if (event->type() == QEvent::KeyPress) { |
| if (static_cast<QKeyEvent *>(event)->key() == Qt::Key_Escape) { |
| if (object == m_messageEditor) |
| m_messageView->setFocus(); |
| else if (object == m_messagesDock) |
| m_contextView->setFocus(); |
| } |
| } |
| return false; |
| } |
| |
| QT_END_NAMESPACE |