summaryrefslogtreecommitdiffstats
path: root/src/linguist/linguist
diff options
context:
space:
mode:
Diffstat (limited to 'src/linguist/linguist')
-rw-r--r--src/linguist/linguist/Info_mac.plist18
-rw-r--r--src/linguist/linguist/batchtranslation.ui260
-rw-r--r--src/linguist/linguist/batchtranslationdialog.cpp194
-rw-r--r--src/linguist/linguist/batchtranslationdialog.h87
-rw-r--r--src/linguist/linguist/errorsview.cpp118
-rw-r--r--src/linguist/linguist/errorsview.h78
-rw-r--r--src/linguist/linguist/finddialog.cpp94
-rw-r--r--src/linguist/linguist/finddialog.h69
-rw-r--r--src/linguist/linguist/finddialog.ui266
-rw-r--r--src/linguist/linguist/formpreviewview.cpp537
-rw-r--r--src/linguist/linguist/formpreviewview.h128
-rw-r--r--src/linguist/linguist/globals.cpp55
-rw-r--r--src/linguist/linguist/globals.h50
-rw-r--r--src/linguist/linguist/images/appicon.pngbin0 -> 1382 bytes
-rw-r--r--src/linguist/linguist/images/down.pngbin0 -> 594 bytes
-rw-r--r--src/linguist/linguist/images/editdelete.pngbin0 -> 831 bytes
-rw-r--r--src/linguist/linguist/images/icons/linguist-128-32.pngbin0 -> 5960 bytes
-rw-r--r--src/linguist/linguist/images/icons/linguist-128-8.pngbin0 -> 5947 bytes
-rw-r--r--src/linguist/linguist/images/icons/linguist-16-32.pngbin0 -> 537 bytes
-rw-r--r--src/linguist/linguist/images/icons/linguist-16-8.pngbin0 -> 608 bytes
-rw-r--r--src/linguist/linguist/images/icons/linguist-32-32.pngbin0 -> 1382 bytes
-rw-r--r--src/linguist/linguist/images/icons/linguist-32-8.pngbin0 -> 1369 bytes
-rw-r--r--src/linguist/linguist/images/icons/linguist-48-32.pngbin0 -> 2017 bytes
-rw-r--r--src/linguist/linguist/images/icons/linguist-48-8.pngbin0 -> 1972 bytes
-rw-r--r--src/linguist/linguist/images/icons/linguist-64-32.pngbin0 -> 2773 bytes
-rw-r--r--src/linguist/linguist/images/icons/linguist-64-8.pngbin0 -> 2664 bytes
-rw-r--r--src/linguist/linguist/images/mac/accelerator.pngbin0 -> 1921 bytes
-rw-r--r--src/linguist/linguist/images/mac/book.pngbin0 -> 1477 bytes
-rw-r--r--src/linguist/linguist/images/mac/doneandnext.pngbin0 -> 1590 bytes
-rw-r--r--src/linguist/linguist/images/mac/editcopy.pngbin0 -> 1468 bytes
-rw-r--r--src/linguist/linguist/images/mac/editcut.pngbin0 -> 1512 bytes
-rw-r--r--src/linguist/linguist/images/mac/editpaste.pngbin0 -> 1906 bytes
-rw-r--r--src/linguist/linguist/images/mac/filenew.pngbin0 -> 1172 bytes
-rw-r--r--src/linguist/linguist/images/mac/fileopen.pngbin0 -> 2168 bytes
-rw-r--r--src/linguist/linguist/images/mac/fileprint.pngbin0 -> 741 bytes
-rw-r--r--src/linguist/linguist/images/mac/filesave.pngbin0 -> 1206 bytes
-rw-r--r--src/linguist/linguist/images/mac/next.pngbin0 -> 1056 bytes
-rw-r--r--src/linguist/linguist/images/mac/nextunfinished.pngbin0 -> 1756 bytes
-rw-r--r--src/linguist/linguist/images/mac/phrase.pngbin0 -> 1932 bytes
-rw-r--r--src/linguist/linguist/images/mac/prev.pngbin0 -> 1080 bytes
-rw-r--r--src/linguist/linguist/images/mac/prevunfinished.pngbin0 -> 1682 bytes
-rw-r--r--src/linguist/linguist/images/mac/print.pngbin0 -> 2087 bytes
-rw-r--r--src/linguist/linguist/images/mac/punctuation.pngbin0 -> 1593 bytes
-rw-r--r--src/linguist/linguist/images/mac/redo.pngbin0 -> 1752 bytes
-rw-r--r--src/linguist/linguist/images/mac/searchfind.pngbin0 -> 1836 bytes
-rw-r--r--src/linguist/linguist/images/mac/undo.pngbin0 -> 1746 bytes
-rw-r--r--src/linguist/linguist/images/mac/validateplacemarkers.pngbin0 -> 1452 bytes
-rw-r--r--src/linguist/linguist/images/mac/whatsthis.pngbin0 -> 1586 bytes
-rw-r--r--src/linguist/linguist/images/minus.pngbin0 -> 296 bytes
-rw-r--r--src/linguist/linguist/images/plus.pngbin0 -> 383 bytes
-rw-r--r--src/linguist/linguist/images/s_check_danger.pngbin0 -> 304 bytes
-rw-r--r--src/linguist/linguist/images/s_check_empty.pngbin0 -> 404 bytes
-rw-r--r--src/linguist/linguist/images/s_check_obsolete.pngbin0 -> 192 bytes
-rw-r--r--src/linguist/linguist/images/s_check_off.pngbin0 -> 434 bytes
-rw-r--r--src/linguist/linguist/images/s_check_on.pngbin0 -> 192 bytes
-rw-r--r--src/linguist/linguist/images/s_check_warning.pngbin0 -> 192 bytes
-rw-r--r--src/linguist/linguist/images/splash.pngbin0 -> 15637 bytes
-rw-r--r--src/linguist/linguist/images/up.pngbin0 -> 692 bytes
-rw-r--r--src/linguist/linguist/images/win/accelerator.pngbin0 -> 1335 bytes
-rw-r--r--src/linguist/linguist/images/win/book.pngbin0 -> 1109 bytes
-rw-r--r--src/linguist/linguist/images/win/doneandnext.pngbin0 -> 1233 bytes
-rw-r--r--src/linguist/linguist/images/win/editcopy.pngbin0 -> 1325 bytes
-rw-r--r--src/linguist/linguist/images/win/editcut.pngbin0 -> 1384 bytes
-rw-r--r--src/linguist/linguist/images/win/editpaste.pngbin0 -> 1482 bytes
-rw-r--r--src/linguist/linguist/images/win/filenew.pngbin0 -> 768 bytes
-rw-r--r--src/linguist/linguist/images/win/fileopen.pngbin0 -> 1662 bytes
-rw-r--r--src/linguist/linguist/images/win/filesave.pngbin0 -> 1205 bytes
-rw-r--r--src/linguist/linguist/images/win/next.pngbin0 -> 1038 bytes
-rw-r--r--src/linguist/linguist/images/win/nextunfinished.pngbin0 -> 1257 bytes
-rw-r--r--src/linguist/linguist/images/win/phrase.pngbin0 -> 1371 bytes
-rw-r--r--src/linguist/linguist/images/win/prev.pngbin0 -> 898 bytes
-rw-r--r--src/linguist/linguist/images/win/prevunfinished.pngbin0 -> 1260 bytes
-rw-r--r--src/linguist/linguist/images/win/print.pngbin0 -> 1456 bytes
-rw-r--r--src/linguist/linguist/images/win/punctuation.pngbin0 -> 1508 bytes
-rw-r--r--src/linguist/linguist/images/win/redo.pngbin0 -> 1212 bytes
-rw-r--r--src/linguist/linguist/images/win/searchfind.pngbin0 -> 1944 bytes
-rw-r--r--src/linguist/linguist/images/win/undo.pngbin0 -> 1181 bytes
-rw-r--r--src/linguist/linguist/images/win/validateplacemarkers.pngbin0 -> 1994 bytes
-rw-r--r--src/linguist/linguist/images/win/whatsthis.pngbin0 -> 1040 bytes
-rw-r--r--src/linguist/linguist/linguist.icnsbin0 -> 152596 bytes
-rw-r--r--src/linguist/linguist/linguist.icobin0 -> 355574 bytes
-rw-r--r--src/linguist/linguist/linguist.pro96
-rw-r--r--src/linguist/linguist/linguist.qrc57
-rw-r--r--src/linguist/linguist/linguist.rc32
-rw-r--r--src/linguist/linguist/main.cpp121
-rw-r--r--src/linguist/linguist/mainwindow.cpp2724
-rw-r--r--src/linguist/linguist/mainwindow.h267
-rw-r--r--src/linguist/linguist/mainwindow.ui892
-rw-r--r--src/linguist/linguist/messageeditor.cpp880
-rw-r--r--src/linguist/linguist/messageeditor.h180
-rw-r--r--src/linguist/linguist/messageeditorwidgets.cpp456
-rw-r--r--src/linguist/linguist/messageeditorwidgets.h184
-rw-r--r--src/linguist/linguist/messagehighlighter.cpp210
-rw-r--r--src/linguist/linguist/messagehighlighter.h83
-rw-r--r--src/linguist/linguist/messagemodel.cpp1426
-rw-r--r--src/linguist/linguist/messagemodel.h535
-rw-r--r--src/linguist/linguist/phrase.cpp355
-rw-r--r--src/linguist/linguist/phrase.h138
-rw-r--r--src/linguist/linguist/phrasebookbox.cpp242
-rw-r--r--src/linguist/linguist/phrasebookbox.h89
-rw-r--r--src/linguist/linguist/phrasebookbox.ui236
-rw-r--r--src/linguist/linguist/phrasemodel.cpp200
-rw-r--r--src/linguist/linguist/phrasemodel.h94
-rw-r--r--src/linguist/linguist/phraseview.cpp272
-rw-r--r--src/linguist/linguist/phraseview.h120
-rw-r--r--src/linguist/linguist/printout.cpp210
-rw-r--r--src/linguist/linguist/printout.h120
-rw-r--r--src/linguist/linguist/recentfiles.cpp146
-rw-r--r--src/linguist/linguist/recentfiles.h83
-rw-r--r--src/linguist/linguist/sourcecodeview.cpp145
-rw-r--r--src/linguist/linguist/sourcecodeview.h74
-rw-r--r--src/linguist/linguist/statistics.cpp67
-rw-r--r--src/linguist/linguist/statistics.h67
-rw-r--r--src/linguist/linguist/statistics.ui211
-rw-r--r--src/linguist/linguist/translatedialog.cpp90
-rw-r--r--src/linguist/linguist/translatedialog.h89
-rw-r--r--src/linguist/linguist/translatedialog.ui260
-rw-r--r--src/linguist/linguist/translationsettings.ui137
-rw-r--r--src/linguist/linguist/translationsettingsdialog.cpp166
-rw-r--r--src/linguist/linguist/translationsettingsdialog.h81
120 files changed, 13789 insertions, 0 deletions
diff --git a/src/linguist/linguist/Info_mac.plist b/src/linguist/linguist/Info_mac.plist
new file mode 100644
index 000000000..b11f493bd
--- /dev/null
+++ b/src/linguist/linguist/Info_mac.plist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.1">
+<dict>
+ <key>CFBundleIconFile</key>
+ <string>linguist.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.trolltech.Linguist</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleGetInfoString</key>
+ <string>Created by Qt/QMake</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleExecutable</key>
+ <string>Linguist</string>
+</dict>
+</plist>
diff --git a/src/linguist/linguist/batchtranslation.ui b/src/linguist/linguist/batchtranslation.ui
new file mode 100644
index 000000000..659595622
--- /dev/null
+++ b/src/linguist/linguist/batchtranslation.ui
@@ -0,0 +1,260 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <comment>*********************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+*********************************************************************</comment>
+ <class>BatchTranslationDialog</class>
+ <widget class="QDialog" name="batchTranslationDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>437</width>
+ <height>492</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Qt Linguist - Batch Translation</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Options</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="ckMarkFinished">
+ <property name="toolTip">
+ <string/>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Set translated entries to finished</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="ckTranslateTranslated">
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Retranslate entries with existing translation</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="ckTranslateFinished">
+ <property name="toolTip">
+ <string>Note that the modified entries will be reset to unfinished if 'Set translated entries to finished' above is unchecked</string>
+ </property>
+ <property name="text">
+ <string>Translate also finished entries</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Phrase book preference</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QListView" name="phrasebookList">
+ <property name="uniformItemSizes">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="moveUpButton">
+ <property name="text">
+ <string>Move up</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="moveDownButton">
+ <property name="text">
+ <string>Move down</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>The batch translator will search through the selected phrase books in the order given above</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="runButton">
+ <property name="text">
+ <string>&amp;Run</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancelButton">
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>cancelButton</sender>
+ <signal>clicked()</signal>
+ <receiver>batchTranslationDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>388</x>
+ <y>461</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>188</x>
+ <y>465</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/linguist/linguist/batchtranslationdialog.cpp b/src/linguist/linguist/batchtranslationdialog.cpp
new file mode 100644
index 000000000..e58316ff0
--- /dev/null
+++ b/src/linguist/linguist/batchtranslationdialog.cpp
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "batchtranslationdialog.h"
+#include "phrase.h"
+#include "messagemodel.h"
+
+#include <QtGui/QMessageBox>
+#include <QtGui/QProgressDialog>
+
+QT_BEGIN_NAMESPACE
+
+CheckableListModel::CheckableListModel(QObject *parent)
+ : QStandardItemModel(parent)
+{
+}
+
+Qt::ItemFlags CheckableListModel::flags(const QModelIndex &index) const
+{
+ Q_UNUSED(index);
+ return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+}
+
+BatchTranslationDialog::BatchTranslationDialog(MultiDataModel *dataModel, QWidget *w)
+ : QDialog(w), m_model(this), m_dataModel(dataModel)
+{
+ m_ui.setupUi(this);
+ connect(m_ui.runButton, SIGNAL(clicked()), this, SLOT(startTranslation()));
+ connect(m_ui.moveUpButton, SIGNAL(clicked()), this, SLOT(movePhraseBookUp()));
+ connect(m_ui.moveDownButton, SIGNAL(clicked()), this, SLOT(movePhraseBookDown()));
+
+ m_ui.phrasebookList->setModel(&m_model);
+ m_ui.phrasebookList->setSelectionBehavior(QAbstractItemView::SelectItems);
+ m_ui.phrasebookList->setSelectionMode(QAbstractItemView::SingleSelection);
+}
+
+
+void BatchTranslationDialog::setPhraseBooks(const QList<PhraseBook *> &phrasebooks, int modelIndex)
+{
+ QString fn = QFileInfo(m_dataModel->srcFileName(modelIndex)).baseName();
+ setWindowTitle(tr("Batch Translation of '%1' - Qt Linguist").arg(fn));
+ m_model.clear();
+ m_model.insertColumn(0);
+ m_phrasebooks = phrasebooks;
+ m_modelIndex = modelIndex;
+ int count = phrasebooks.count();
+ m_model.insertRows(0, count);
+ for (int i = 0; i < count; ++i) {
+ QModelIndex idx(m_model.index(i, 0));
+ m_model.setData(idx, phrasebooks[i]->friendlyPhraseBookName());
+ int sortOrder;
+ if (phrasebooks[i]->language() != QLocale::C
+ && m_dataModel->language(m_modelIndex) != QLocale::C) {
+ if (phrasebooks[i]->language() != m_dataModel->language(m_modelIndex))
+ sortOrder = 3;
+ else
+ sortOrder = (phrasebooks[i]->country()
+ == m_dataModel->model(m_modelIndex)->country()) ? 0 : 1;
+ } else {
+ sortOrder = 2;
+ }
+ m_model.setData(idx, sortOrder == 3 ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole);
+ m_model.setData(idx, sortOrder, Qt::UserRole + 1);
+ m_model.setData(idx, i, Qt::UserRole);
+ }
+ m_model.setSortRole(Qt::UserRole + 1);
+ m_model.sort(0);
+}
+
+void BatchTranslationDialog::startTranslation()
+{
+ int translatedcount = 0;
+ QCursor oldCursor = cursor();
+ setCursor(Qt::BusyCursor);
+ int messageCount = m_dataModel->messageCount();
+
+ QProgressDialog *dlgProgress;
+ dlgProgress = new QProgressDialog(tr("Searching, please wait..."), tr("&Cancel"), 0, messageCount, this);
+ dlgProgress->show();
+
+ int msgidx = 0;
+ const bool translateTranslated = m_ui.ckTranslateTranslated->isChecked();
+ const bool translateFinished = m_ui.ckTranslateFinished->isChecked();
+ for (MultiDataModelIterator it(m_dataModel, m_modelIndex); it.isValid(); ++it) {
+ if (MessageItem *m = it.current()) {
+ if (!m->isObsolete()
+ && (translateTranslated || m->translation().isEmpty())
+ && (translateFinished || !m->isFinished())) {
+
+ // Go through them in the order the user specified in the phrasebookList
+ for (int b = 0; b < m_model.rowCount(); ++b) {
+ QModelIndex idx(m_model.index(b, 0));
+ QVariant checkState = m_model.data(idx, Qt::CheckStateRole);
+ if (checkState == Qt::Checked) {
+ PhraseBook *pb = m_phrasebooks[m_model.data(idx, Qt::UserRole).toInt()];
+ foreach (const Phrase *ph, pb->phrases()) {
+ if (ph->source() == m->text()) {
+ m_dataModel->setTranslation(it, ph->target());
+ m_dataModel->setFinished(it, m_ui.ckMarkFinished->isChecked());
+ ++translatedcount;
+ goto done; // break 2;
+ }
+ }
+ }
+ }
+ }
+ }
+ done:
+ ++msgidx;
+ if (!(msgidx & 15))
+ dlgProgress->setValue(msgidx);
+ qApp->processEvents();
+ if (dlgProgress->wasCanceled())
+ break;
+ }
+ dlgProgress->hide();
+
+ setCursor(oldCursor);
+ emit finished();
+ QMessageBox::information(this, tr("Linguist batch translator"),
+ tr("Batch translated %n entries", "", translatedcount), QMessageBox::Ok);
+}
+
+void BatchTranslationDialog::movePhraseBookUp()
+{
+ QModelIndexList indexes = m_ui.phrasebookList->selectionModel()->selectedIndexes();
+ if (indexes.count() <= 0) return;
+
+ QModelIndex sel = indexes[0];
+ int row = sel.row();
+ if (row > 0) {
+ QModelIndex other = m_model.index(row - 1, 0);
+ QMap<int, QVariant> seldata = m_model.itemData(sel);
+ m_model.setItemData(sel, m_model.itemData(other));
+ m_model.setItemData(other, seldata);
+ m_ui.phrasebookList->selectionModel()->setCurrentIndex(other, QItemSelectionModel::ClearAndSelect);
+ }
+}
+
+void BatchTranslationDialog::movePhraseBookDown()
+{
+ QModelIndexList indexes = m_ui.phrasebookList->selectionModel()->selectedIndexes();
+ if (indexes.count() <= 0) return;
+
+ QModelIndex sel = indexes[0];
+ int row = sel.row();
+ if (row < m_model.rowCount() - 1) {
+ QModelIndex other = m_model.index(row + 1, 0);
+ QMap<int, QVariant> seldata = m_model.itemData(sel);
+ m_model.setItemData(sel, m_model.itemData(other));
+ m_model.setItemData(other, seldata);
+ m_ui.phrasebookList->selectionModel()->setCurrentIndex(other, QItemSelectionModel::ClearAndSelect);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/batchtranslationdialog.h b/src/linguist/linguist/batchtranslationdialog.h
new file mode 100644
index 000000000..16d70cafe
--- /dev/null
+++ b/src/linguist/linguist/batchtranslationdialog.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef BATCHTRANSLATIONDIALOG_H
+#define BATCHTRANSLATIONDIALOG_H
+
+#include "ui_batchtranslation.h"
+#include "phrase.h"
+
+#include <QtGui/QDialog>
+#include <QtGui/QStandardItemModel>
+
+QT_BEGIN_NAMESPACE
+
+class MultiDataModel;
+
+class CheckableListModel : public QStandardItemModel
+{
+public:
+ CheckableListModel(QObject *parent = 0);
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const;
+};
+
+class BatchTranslationDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ BatchTranslationDialog(MultiDataModel *model, QWidget *w = 0);
+ void setPhraseBooks(const QList<PhraseBook *> &phrasebooks, int modelIndex);
+
+signals:
+ void finished();
+
+private slots:
+ void startTranslation();
+ void movePhraseBookUp();
+ void movePhraseBookDown();
+
+private:
+ Ui::BatchTranslationDialog m_ui;
+ CheckableListModel m_model;
+ MultiDataModel *m_dataModel;
+ QList<PhraseBook *> m_phrasebooks;
+ int m_modelIndex;
+};
+
+QT_END_NAMESPACE
+
+#endif // BATCHTRANSLATIONDIALOG_H
diff --git a/src/linguist/linguist/errorsview.cpp b/src/linguist/linguist/errorsview.cpp
new file mode 100644
index 000000000..f24b3bede
--- /dev/null
+++ b/src/linguist/linguist/errorsview.cpp
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "errorsview.h"
+
+#include "messagemodel.h"
+
+#include <QtCore/QList>
+#include <QtCore/QString>
+#include <QtCore/QUrl>
+
+#include <QtGui/QListView>
+#include <QtGui/QStandardItem>
+#include <QtGui/QStandardItemModel>
+#include <QtGui/QTextEdit>
+#include <QtGui/QVBoxLayout>
+
+QT_BEGIN_NAMESPACE
+
+ErrorsView::ErrorsView(MultiDataModel *dataModel, QWidget *parent) :
+ QListView(parent),
+ m_dataModel(dataModel)
+{
+ m_list = new QStandardItemModel(this);
+ setModel(m_list);
+}
+
+void ErrorsView::clear()
+{
+ m_list->clear();
+}
+
+void ErrorsView::addError(int model, const ErrorType type, const QString &arg)
+{
+ QString error;
+ switch (type) {
+ case SuperfluousAccelerator:
+ addError(model, tr("Accelerator possibly superfluous in translation."));
+ break;
+ case MissingAccelerator:
+ addError(model, tr("Accelerator possibly missing in translation."));
+ break;
+ case PunctuationDiffer:
+ addError(model, tr("Translation does not end with the same punctuation as the source text."));
+ break;
+ case IgnoredPhrasebook:
+ addError(model, tr("A phrase book suggestion for '%1' was ignored.").arg(arg));
+ break;
+ case PlaceMarkersDiffer:
+ addError(model, tr("Translation does not refer to the same place markers as in the source text."));
+ break;
+ case NumerusMarkerMissing:
+ addError(model, tr("Translation does not contain the necessary %n place marker."));
+ break;
+ default:
+ addError(model, tr("Unknown error"));
+ break;
+ }
+}
+
+QString ErrorsView::firstError()
+{
+ return (m_list->rowCount() == 0) ? QString() : m_list->item(0)->text();
+}
+
+void ErrorsView::addError(int model, const QString &error)
+{
+ // NOTE: Three statics instead of one just for GCC 3.3.5
+ static QLatin1String imageLocation(":/images/s_check_danger.png");
+ static QPixmap image(imageLocation);
+ static QIcon pxDanger(image);
+ QString lang;
+ if (m_dataModel->modelCount() > 1)
+ lang = m_dataModel->model(model)->localizedLanguage() + QLatin1String(": ");
+ QStandardItem *item = new QStandardItem(pxDanger, lang + error);
+ item->setEditable(false);
+ m_list->appendRow(QList<QStandardItem*>() << item);
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/errorsview.h b/src/linguist/linguist/errorsview.h
new file mode 100644
index 000000000..a7a7dc1b7
--- /dev/null
+++ b/src/linguist/linguist/errorsview.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ERRORSVIEW_H
+#define ERRORSVIEW_H
+
+#include <QListView>
+
+QT_BEGIN_NAMESPACE
+
+class QStandardItemModel;
+
+class MultiDataModel;
+
+class ErrorsView : public QListView
+{
+ Q_OBJECT
+public:
+ enum ErrorType {
+ SuperfluousAccelerator,
+ MissingAccelerator,
+ PunctuationDiffer,
+ IgnoredPhrasebook,
+ PlaceMarkersDiffer,
+ NumerusMarkerMissing
+ };
+
+ ErrorsView(MultiDataModel *dataModel, QWidget *parent = 0);
+ void clear();
+ void addError(int model, const ErrorType type, const QString &arg = QString());
+ QString firstError();
+private:
+ void addError(int model, const QString &error);
+ QStandardItemModel *m_list;
+ MultiDataModel *m_dataModel;
+};
+
+QT_END_NAMESPACE
+
+#endif // ERRORSVIEW_H
diff --git a/src/linguist/linguist/finddialog.cpp b/src/linguist/linguist/finddialog.cpp
new file mode 100644
index 000000000..f05b710aa
--- /dev/null
+++ b/src/linguist/linguist/finddialog.cpp
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/* TRANSLATOR FindDialog
+
+ Choose Edit|Find from the menu bar or press Ctrl+F to pop up the
+ Find dialog
+*/
+
+#include "finddialog.h"
+
+QT_BEGIN_NAMESPACE
+
+FindDialog::FindDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ setupUi(this);
+
+ findNxt->setEnabled(false);
+
+ connect(findNxt, SIGNAL(clicked()), this, SLOT(emitFindNext()));
+ connect(led, SIGNAL(textChanged(QString)), this, SLOT(verifyText(QString)));
+
+ led->setFocus();
+}
+
+void FindDialog::verifyText(const QString &text)
+{
+ findNxt->setEnabled(!text.isEmpty());
+}
+
+void FindDialog::emitFindNext()
+{
+ DataModel::FindLocation where;
+ if (sourceText != 0)
+ where =
+ DataModel::FindLocation(
+ (sourceText->isChecked() ? DataModel::SourceText : 0) |
+ (translations->isChecked() ? DataModel::Translations : 0) |
+ (comments->isChecked() ? DataModel::Comments : 0));
+ else
+ where = DataModel::Translations;
+ emit findNext(led->text(), where, matchCase->isChecked(), ignoreAccelerators->isChecked());
+ led->selectAll();
+}
+
+void FindDialog::find()
+{
+ led->setFocus();
+
+ show();
+ activateWindow();
+ raise();
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/finddialog.h b/src/linguist/linguist/finddialog.h
new file mode 100644
index 000000000..afacd5b7d
--- /dev/null
+++ b/src/linguist/linguist/finddialog.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FINDDIALOG_H
+#define FINDDIALOG_H
+
+#include "ui_finddialog.h"
+#include "messagemodel.h"
+
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+
+class FindDialog : public QDialog, public Ui::FindDialog
+{
+ Q_OBJECT
+public:
+ FindDialog(QWidget *parent = 0);
+
+signals:
+ void findNext(const QString& text, DataModel::FindLocation where, bool matchCase, bool ignoreAccelerators);
+
+private slots:
+ void emitFindNext();
+ void verifyText(const QString &);
+ void find();
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/linguist/linguist/finddialog.ui b/src/linguist/linguist/finddialog.ui
new file mode 100644
index 000000000..2ecb110d7
--- /dev/null
+++ b/src/linguist/linguist/finddialog.ui
@@ -0,0 +1,266 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <comment>*********************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+*********************************************************************</comment>
+ <class>FindDialog</class>
+ <widget class="QDialog" name="FindDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>414</width>
+ <height>175</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Find</string>
+ </property>
+ <property name="whatsThis">
+ <string>This window allows you to search for some text in the translation source file.</string>
+ </property>
+ <layout class="QHBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="findWhat">
+ <property name="text">
+ <string>&amp;Find what:</string>
+ </property>
+ <property name="buddy">
+ <cstring>led</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="led">
+ <property name="whatsThis">
+ <string>Type in the text to search for.</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Options</string>
+ </property>
+ <layout class="QGridLayout">
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="sourceText">
+ <property name="whatsThis">
+ <string>Source texts are searched when checked.</string>
+ </property>
+ <property name="text">
+ <string>&amp;Source texts</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="translations">
+ <property name="whatsThis">
+ <string>Translations are searched when checked.</string>
+ </property>
+ <property name="text">
+ <string>&amp;Translations</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QCheckBox" name="matchCase">
+ <property name="whatsThis">
+ <string>Texts such as 'TeX' and 'tex' are considered as different when checked.</string>
+ </property>
+ <property name="text">
+ <string>&amp;Match case</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="comments">
+ <property name="whatsThis">
+ <string>Comments and contexts are searched when checked.</string>
+ </property>
+ <property name="text">
+ <string>&amp;Comments</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="ignoreAccelerators">
+ <property name="text">
+ <string>Ignore &amp;accelerators</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="findNxt">
+ <property name="whatsThis">
+ <string>Click here to find the next occurrence of the text you typed in.</string>
+ </property>
+ <property name="text">
+ <string>Find Next</string>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancel">
+ <property name="whatsThis">
+ <string>Click here to close this window.</string>
+ </property>
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>51</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <tabstops>
+ <tabstop>led</tabstop>
+ <tabstop>findNxt</tabstop>
+ <tabstop>cancel</tabstop>
+ <tabstop>comments</tabstop>
+ <tabstop>sourceText</tabstop>
+ <tabstop>translations</tabstop>
+ <tabstop>matchCase</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>cancel</sender>
+ <signal>clicked()</signal>
+ <receiver>FindDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>372</x>
+ <y>58</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>373</x>
+ <y>109</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/linguist/linguist/formpreviewview.cpp b/src/linguist/linguist/formpreviewview.cpp
new file mode 100644
index 000000000..a2a600257
--- /dev/null
+++ b/src/linguist/linguist/formpreviewview.cpp
@@ -0,0 +1,537 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "formpreviewview.h"
+#include "messagemodel.h"
+
+#include <quiloader.h>
+#include <abstractformbuilder.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QTime>
+
+#include <QtGui/QAction>
+#include <QtGui/QApplication>
+#include <QtGui/QFontComboBox>
+#include <QtGui/QFrame>
+#include <QtGui/QGridLayout>
+#include <QtGui/QListWidget>
+#include <QtGui/QMdiArea>
+#include <QtGui/QMdiSubWindow>
+#include <QtGui/QMenu>
+#include <QtGui/QTableWidget>
+#include <QtGui/QTabWidget>
+#include <QtGui/QToolBox>
+#include <QtGui/QTreeWidget>
+
+QT_BEGIN_NAMESPACE
+
+#if defined(Q_CC_SUN) || defined(Q_CC_HPACC) || defined(Q_CC_XLC)
+int qHash(const QUiTranslatableStringValue &tsv)
+#else
+static int qHash(const QUiTranslatableStringValue &tsv)
+#endif
+{
+ return qHash(tsv.value()) ^ qHash(tsv.comment());
+}
+
+static bool operator==(const QUiTranslatableStringValue &tsv1, const QUiTranslatableStringValue &tsv2)
+{
+ return tsv1.value() == tsv2.value() && tsv1.comment() == tsv2.comment();
+}
+
+#define INSERT_TARGET(_tsv, _type, _target, _prop) \
+ do { \
+ target.type = _type; \
+ target.target._target; \
+ target.prop._prop; \
+ (*targets)[qvariant_cast<QUiTranslatableStringValue>(_tsv)].append(target); \
+ } while (0)
+
+static void registerTreeItem(QTreeWidgetItem *item, TargetsHash *targets)
+{
+ const QUiItemRolePair *irs = QFormInternal::qUiItemRoles;
+
+ int cnt = item->columnCount();
+ for (int i = 0; i < cnt; ++i) {
+ for (unsigned j = 0; irs[j].shadowRole >= 0; j++) {
+ QVariant v = item->data(i, irs[j].shadowRole);
+ if (v.isValid()) {
+ TranslatableEntry target;
+ target.prop.treeIndex.column = i;
+ INSERT_TARGET(v, TranslatableTreeWidgetItem, treeWidgetItem = item, treeIndex.index = j);
+ }
+ }
+ }
+
+ cnt = item->childCount();
+ for (int j = 0; j < cnt; ++j)
+ registerTreeItem(item->child(j), targets);
+}
+
+#define REGISTER_ITEM_CORE(item, propType, targetName) \
+ const QUiItemRolePair *irs = QFormInternal::qUiItemRoles; \
+ for (unsigned j = 0; irs[j].shadowRole >= 0; j++) { \
+ QVariant v = item->data(irs[j].shadowRole); \
+ if (v.isValid()) \
+ INSERT_TARGET(v, propType, targetName = item, index = j); \
+ }
+
+static void registerListItem(QListWidgetItem *item, TargetsHash *targets)
+{
+ TranslatableEntry target;
+ REGISTER_ITEM_CORE(item, TranslatableListWidgetItem, listWidgetItem);
+}
+
+static void registerTableItem(QTableWidgetItem *item, TargetsHash *targets)
+{
+ if (!item)
+ return;
+
+ TranslatableEntry target;
+ REGISTER_ITEM_CORE(item, TranslatableTableWidgetItem, tableWidgetItem);
+}
+
+#define REGISTER_SUBWIDGET_PROP(mainWidget, propType, propName) \
+ do { \
+ QVariant v = mainWidget->widget(i)->property(propName); \
+ if (v.isValid()) \
+ INSERT_TARGET(v, propType, object = mainWidget, index = i); \
+ } while (0)
+
+static void buildTargets(QObject *o, TargetsHash *targets)
+{
+ TranslatableEntry target;
+
+ foreach (const QByteArray &prop, o->dynamicPropertyNames()) {
+ if (prop.startsWith(PROP_GENERIC_PREFIX)) {
+ const QByteArray propName = prop.mid(sizeof(PROP_GENERIC_PREFIX) - 1);
+ INSERT_TARGET(o->property(prop),
+ TranslatableProperty, object = o, name = qstrdup(propName.data()));
+ }
+ }
+ if (0) {
+#ifndef QT_NO_TABWIDGET
+ } else if (QTabWidget *tabw = qobject_cast<QTabWidget*>(o)) {
+ const int cnt = tabw->count();
+ for (int i = 0; i < cnt; ++i) {
+ REGISTER_SUBWIDGET_PROP(tabw, TranslatableTabPageText, PROP_TABPAGETEXT);
+# ifndef QT_NO_TOOLTIP
+ REGISTER_SUBWIDGET_PROP(tabw, TranslatableTabPageToolTip, PROP_TABPAGETOOLTIP);
+# endif
+# ifndef QT_NO_WHATSTHIS
+ REGISTER_SUBWIDGET_PROP(tabw, TranslatableTabPageWhatsThis, PROP_TABPAGEWHATSTHIS);
+# endif
+ }
+#endif
+#ifndef QT_NO_TOOLBOX
+ } else if (QToolBox *toolw = qobject_cast<QToolBox*>(o)) {
+ const int cnt = toolw->count();
+ for (int i = 0; i < cnt; ++i) {
+ REGISTER_SUBWIDGET_PROP(toolw, TranslatableToolItemText, PROP_TOOLITEMTEXT);
+# ifndef QT_NO_TOOLTIP
+ REGISTER_SUBWIDGET_PROP(toolw, TranslatableToolItemToolTip, PROP_TOOLITEMTOOLTIP);
+# endif
+ }
+#endif
+#ifndef QT_NO_COMBOBOX
+ } else if (QComboBox *combow = qobject_cast<QComboBox*>(o)) {
+ if (!qobject_cast<QFontComboBox*>(o)) {
+ const int cnt = combow->count();
+ for (int i = 0; i < cnt; ++i) {
+ const QVariant v = combow->itemData(i, Qt::DisplayPropertyRole);
+ if (v.isValid())
+ INSERT_TARGET(v, TranslatableComboBoxItem, comboBox = combow, index = i);
+ }
+ }
+#endif
+#ifndef QT_NO_LISTWIDGET
+ } else if (QListWidget *listw = qobject_cast<QListWidget*>(o)) {
+ const int cnt = listw->count();
+ for (int i = 0; i < cnt; ++i)
+ registerListItem(listw->item(i), targets);
+#endif
+#ifndef QT_NO_TABLEWIDGET
+ } else if (QTableWidget *tablew = qobject_cast<QTableWidget*>(o)) {
+ const int row_cnt = tablew->rowCount();
+ const int col_cnt = tablew->columnCount();
+ for (int j = 0; j < col_cnt; ++j)
+ registerTableItem(tablew->verticalHeaderItem(j), targets);
+ for (int i = 0; i < row_cnt; ++i) {
+ registerTableItem(tablew->horizontalHeaderItem(i), targets);
+ for (int j = 0; j < col_cnt; ++j)
+ registerTableItem(tablew->item(i, j), targets);
+ }
+#endif
+#ifndef QT_NO_TREEWIDGET
+ } else if (QTreeWidget *treew = qobject_cast<QTreeWidget*>(o)) {
+ if (QTreeWidgetItem *item = treew->headerItem())
+ registerTreeItem(item, targets);
+ const int cnt = treew->topLevelItemCount();
+ for (int i = 0; i < cnt; ++i)
+ registerTreeItem(treew->topLevelItem(i), targets);
+#endif
+ }
+ foreach (QObject *co, o->children())
+ buildTargets(co, targets);
+}
+
+static void destroyTargets(TargetsHash *targets)
+{
+ for (TargetsHash::Iterator it = targets->begin(), end = targets->end(); it != end; ++it)
+ foreach (const TranslatableEntry &target, *it)
+ if (target.type == TranslatableProperty)
+ delete target.prop.name;
+ targets->clear();
+}
+
+static void retranslateTarget(const TranslatableEntry &target, const QString &text)
+{
+ switch (target.type) {
+ case TranslatableProperty:
+ target.target.object->setProperty(target.prop.name, text);
+ break;
+#ifndef QT_NO_TABWIDGET
+ case TranslatableTabPageText:
+ target.target.tabWidget->setTabText(target.prop.index, text);
+ break;
+# ifndef QT_NO_TOOLTIP
+ case TranslatableTabPageToolTip:
+ target.target.tabWidget->setTabToolTip(target.prop.index, text);
+ break;
+# endif
+# ifndef QT_NO_WHATSTHIS
+ case TranslatableTabPageWhatsThis:
+ target.target.tabWidget->setTabWhatsThis(target.prop.index, text);
+ break;
+# endif
+#endif // QT_NO_TABWIDGET
+#ifndef QT_NO_TOOLBOX
+ case TranslatableToolItemText:
+ target.target.toolBox->setItemText(target.prop.index, text);
+ break;
+# ifndef QT_NO_TOOLTIP
+ case TranslatableToolItemToolTip:
+ target.target.toolBox->setItemToolTip(target.prop.index, text);
+ break;
+# endif
+#endif // QT_NO_TOOLBOX
+#ifndef QT_NO_COMBOBOX
+ case TranslatableComboBoxItem:
+ target.target.comboBox->setItemText(target.prop.index, text);
+ break;
+#endif
+#ifndef QT_NO_LISTWIDGET
+ case TranslatableListWidgetItem:
+ target.target.listWidgetItem->setData(target.prop.index, text);
+ break;
+#endif
+#ifndef QT_NO_TABLEWIDGET
+ case TranslatableTableWidgetItem:
+ target.target.tableWidgetItem->setData(target.prop.index, text);
+ break;
+#endif
+#ifndef QT_NO_TREEWIDGET
+ case TranslatableTreeWidgetItem:
+ target.target.treeWidgetItem->setData(target.prop.treeIndex.column, target.prop.treeIndex.index, text);
+ break;
+#endif
+ }
+}
+
+static void retranslateTargets(
+ const QList<TranslatableEntry> &targets, const QUiTranslatableStringValue &tsv,
+ const DataModel *dataModel, const QString &className)
+{
+ QString sourceText = QString::fromUtf8(tsv.value());
+ QString text;
+ if (MessageItem *msg = dataModel->findMessage(
+ className, sourceText, QString::fromUtf8(tsv.comment())))
+ text = msg->translation();
+ if (text.isEmpty() && !tsv.value().isEmpty())
+ text = QLatin1Char('#') + sourceText;
+
+ foreach (const TranslatableEntry &target, targets)
+ retranslateTarget(target, text);
+}
+
+static void highlightTreeWidgetItem(QTreeWidgetItem *item, int col, bool on)
+{
+ QVariant br = item->data(col, Qt::BackgroundRole + 500);
+ QVariant fr = item->data(col, Qt::ForegroundRole + 500);
+ if (on) {
+ if (!br.isValid() && !fr.isValid()) {
+ item->setData(col, Qt::BackgroundRole + 500, item->data(col, Qt::BackgroundRole));
+ item->setData(col, Qt::ForegroundRole + 500, item->data(col, Qt::ForegroundRole));
+ QPalette pal = qApp->palette();
+ item->setData(col, Qt::BackgroundRole, pal.color(QPalette::Dark));
+ item->setData(col, Qt::ForegroundRole, pal.color(QPalette::Light));
+ }
+ } else {
+ if (br.isValid() || fr.isValid()) {
+ item->setData(col, Qt::BackgroundRole, br);
+ item->setData(col, Qt::ForegroundRole, fr);
+ item->setData(col, Qt::BackgroundRole + 500, QVariant());
+ item->setData(col, Qt::ForegroundRole + 500, QVariant());
+ }
+ }
+}
+
+template <class T>
+static void highlightWidgetItem(T *item, bool on)
+{
+ QVariant br = item->data(Qt::BackgroundRole + 500);
+ QVariant fr = item->data(Qt::ForegroundRole + 500);
+ if (on) {
+ if (!br.isValid() && !fr.isValid()) {
+ item->setData(Qt::BackgroundRole + 500, item->data(Qt::BackgroundRole));
+ item->setData(Qt::ForegroundRole + 500, item->data(Qt::ForegroundRole));
+ QPalette pal = qApp->palette();
+ item->setData(Qt::BackgroundRole, pal.color(QPalette::Dark));
+ item->setData(Qt::ForegroundRole, pal.color(QPalette::Light));
+ }
+ } else {
+ if (br.isValid() || fr.isValid()) {
+ item->setData(Qt::BackgroundRole, br);
+ item->setData(Qt::ForegroundRole, fr);
+ item->setData(Qt::BackgroundRole + 500, QVariant());
+ item->setData(Qt::ForegroundRole + 500, QVariant());
+ }
+ }
+}
+
+#define AUTOFILL_BACKUP_PROP "_q_linguist_autoFillBackup"
+#define PALETTE_BACKUP_PROP "_q_linguist_paletteBackup"
+#define FONT_BACKUP_PROP "_q_linguist_fontBackup"
+
+static void highlightWidget(QWidget *w, bool on);
+
+static void highlightAction(QAction *a, bool on)
+{
+ QVariant bak = a->property(FONT_BACKUP_PROP);
+ if (on) {
+ if (!bak.isValid()) {
+ QFont fnt = qApp->font();
+ a->setProperty(FONT_BACKUP_PROP, QVariant::fromValue(a->font().resolve(fnt)));
+ fnt.setBold(true);
+ fnt.setItalic(true);
+ a->setFont(fnt);
+ }
+ } else {
+ if (bak.isValid()) {
+ a->setFont(qvariant_cast<QFont>(bak));
+ a->setProperty(FONT_BACKUP_PROP, QVariant());
+ }
+ }
+ foreach (QWidget *w, a->associatedWidgets())
+ highlightWidget(w, on);
+}
+
+static void highlightWidget(QWidget *w, bool on)
+{
+ QVariant bak = w->property(PALETTE_BACKUP_PROP);
+ if (on) {
+ if (!bak.isValid()) {
+ QPalette pal = qApp->palette();
+ foreach (QObject *co, w->children())
+ if (QWidget *cw = qobject_cast<QWidget *>(co))
+ cw->setPalette(cw->palette().resolve(pal));
+ w->setProperty(PALETTE_BACKUP_PROP, QVariant::fromValue(w->palette().resolve(pal)));
+ w->setProperty(AUTOFILL_BACKUP_PROP, QVariant::fromValue(w->autoFillBackground()));
+ QColor col1 = pal.color(QPalette::Dark);
+ QColor col2 = pal.color(QPalette::Light);
+ pal.setColor(QPalette::Base, col1);
+ pal.setColor(QPalette::Window, col1);
+ pal.setColor(QPalette::Button, col1);
+ pal.setColor(QPalette::Text, col2);
+ pal.setColor(QPalette::WindowText, col2);
+ pal.setColor(QPalette::ButtonText, col2);
+ pal.setColor(QPalette::BrightText, col2);
+ w->setPalette(pal);
+ w->setAutoFillBackground(true);
+ }
+ } else {
+ if (bak.isValid()) {
+ w->setPalette(qvariant_cast<QPalette>(bak));
+ w->setAutoFillBackground(qvariant_cast<bool>(w->property(AUTOFILL_BACKUP_PROP)));
+ w->setProperty(PALETTE_BACKUP_PROP, QVariant());
+ w->setProperty(AUTOFILL_BACKUP_PROP, QVariant());
+ }
+ }
+ if (QMenu *m = qobject_cast<QMenu *>(w))
+ if (m->menuAction())
+ highlightAction(m->menuAction(), on);
+}
+
+static void highlightTarget(const TranslatableEntry &target, bool on)
+{
+ switch (target.type) {
+ case TranslatableProperty:
+ if (QAction *a = qobject_cast<QAction *>(target.target.object)) {
+ highlightAction(a, on);
+ break;
+ }
+ // fallthrough
+#ifndef QT_NO_TABWIDGET
+ case TranslatableTabPageText:
+# ifndef QT_NO_TOOLTIP
+ case TranslatableTabPageToolTip:
+# endif
+# ifndef QT_NO_WHATSTHIS
+ case TranslatableTabPageWhatsThis:
+# endif
+#endif // QT_NO_TABWIDGET
+#ifndef QT_NO_TOOLBOX
+ case TranslatableToolItemText:
+# ifndef QT_NO_TOOLTIP
+ case TranslatableToolItemToolTip:
+# endif
+#endif // QT_NO_TOOLBOX
+#ifndef QT_NO_COMBOBOX
+ case TranslatableComboBoxItem:
+#endif
+ if (QWidget *w = qobject_cast<QWidget *>(target.target.object))
+ highlightWidget(w, on);
+ break;
+#ifndef QT_NO_LISTWIDGET
+ case TranslatableListWidgetItem:
+ highlightWidgetItem(target.target.listWidgetItem, on);
+ break;
+#endif
+#ifndef QT_NO_TABLEWIDGET
+ case TranslatableTableWidgetItem:
+ highlightWidgetItem(target.target.tableWidgetItem, on);
+ break;
+#endif
+#ifndef QT_NO_TREEWIDGET
+ case TranslatableTreeWidgetItem:
+ highlightTreeWidgetItem(target.target.treeWidgetItem, target.prop.treeIndex.column, on);
+ break;
+#endif
+ }
+}
+
+static void highlightTargets(const QList<TranslatableEntry> &targets, bool on)
+{
+ foreach (const TranslatableEntry &target, targets)
+ highlightTarget(target, on);
+}
+
+FormPreviewView::FormPreviewView(QWidget *parent, MultiDataModel *dataModel)
+ : QMainWindow(parent), m_form(0), m_dataModel(dataModel)
+{
+ m_mdiSubWindow = new QMdiSubWindow;
+ m_mdiSubWindow->setWindowFlags(m_mdiSubWindow->windowFlags() & ~Qt::WindowSystemMenuHint);
+ m_mdiArea = new QMdiArea(this);
+ m_mdiArea->addSubWindow(m_mdiSubWindow);
+ setCentralWidget(m_mdiArea);
+ m_mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ m_mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+}
+
+void FormPreviewView::setSourceContext(int model, MessageItem *messageItem)
+{
+ if (model < 0 || !messageItem) {
+ m_lastModel = -1;
+ return;
+ }
+
+ QDir dir = QFileInfo(m_dataModel->srcFileName(model)).dir();
+ QString fileName = QDir::cleanPath(dir.absoluteFilePath(messageItem->fileName()));
+ if (m_lastFormName != fileName) {
+ delete m_form;
+ m_form = 0;
+ m_lastFormName.clear();
+ m_highlights.clear();
+ destroyTargets(&m_targets);
+
+ static QUiLoader *uiLoader;
+ if (!uiLoader) {
+ uiLoader = new QUiLoader(this);
+ uiLoader->setLanguageChangeEnabled(true);
+ uiLoader->setTranslationEnabled(false);
+ }
+
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qDebug() << "CANNOT OPEN FORM" << fileName;
+ m_mdiSubWindow->hide();
+ return;
+ }
+ m_form = uiLoader->load(&file, m_mdiSubWindow);
+ if (!m_form) {
+ qDebug() << "CANNOT LOAD FORM" << fileName;
+ m_mdiSubWindow->hide();
+ return;
+ }
+ file.close();
+ buildTargets(m_form, &m_targets);
+
+ setToolTip(fileName);
+
+ m_form->setWindowFlags(Qt::Widget);
+ m_form->setWindowModality(Qt::NonModal);
+ m_form->setFocusPolicy(Qt::NoFocus);
+ m_form->show(); // needed, otherwide the Qt::NoFocus is not propagated.
+ m_mdiSubWindow->setWidget(m_form);
+ m_mdiSubWindow->show();
+ m_mdiArea->cascadeSubWindows();
+ m_lastFormName = fileName;
+ m_lastClassName = messageItem->context();
+ m_lastModel = -1;
+ } else {
+ highlightTargets(m_highlights, false);
+ }
+ QUiTranslatableStringValue tsv;
+ tsv.setValue(messageItem->text().toUtf8());
+ tsv.setComment(messageItem->comment().toUtf8());
+ m_highlights = m_targets.value(tsv);
+ if (m_lastModel != model) {
+ for (TargetsHash::Iterator it = m_targets.begin(), end = m_targets.end(); it != end; ++it)
+ retranslateTargets(*it, it.key(), m_dataModel->model(model), m_lastClassName);
+ m_lastModel = model;
+ } else {
+ retranslateTargets(m_highlights, tsv, m_dataModel->model(model), m_lastClassName);
+ }
+ highlightTargets(m_highlights, true);
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/formpreviewview.h b/src/linguist/linguist/formpreviewview.h
new file mode 100644
index 000000000..e30160670
--- /dev/null
+++ b/src/linguist/linguist/formpreviewview.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FORMPREVIEWVIEW_H
+#define FORMPREVIEWVIEW_H
+
+#include <quiloader_p.h>
+
+#include <QtCore/QHash>
+#include <QtCore/QList>
+
+#include <QtGui/QMainWindow>
+
+QT_BEGIN_NAMESPACE
+
+class MultiDataModel;
+class FormFrame;
+class MessageItem;
+
+class QComboBox;
+class QListWidgetItem;
+class QGridLayout;
+class QMdiArea;
+class QMdiSubWindow;
+class QToolBox;
+class QTableWidgetItem;
+class QTreeWidgetItem;
+
+enum TranslatableEntryType {
+ TranslatableProperty,
+ TranslatableToolItemText,
+ TranslatableToolItemToolTip,
+ TranslatableTabPageText,
+ TranslatableTabPageToolTip,
+ TranslatableTabPageWhatsThis,
+ TranslatableListWidgetItem,
+ TranslatableTableWidgetItem,
+ TranslatableTreeWidgetItem,
+ TranslatableComboBoxItem
+};
+
+struct TranslatableEntry {
+ TranslatableEntryType type;
+ union {
+ QObject *object;
+ QComboBox *comboBox;
+ QTabWidget *tabWidget;
+ QToolBox *toolBox;
+ QListWidgetItem *listWidgetItem;
+ QTableWidgetItem *tableWidgetItem;
+ QTreeWidgetItem *treeWidgetItem;
+ } target;
+ union {
+ char *name;
+ int index;
+ struct {
+ short index; // Known to be below 1000
+ short column;
+ } treeIndex;
+ } prop;
+};
+
+typedef QHash<QUiTranslatableStringValue, QList<TranslatableEntry> > TargetsHash;
+
+class FormPreviewView : public QMainWindow
+{
+ Q_OBJECT
+public:
+ FormPreviewView(QWidget *parent, MultiDataModel *dataModel);
+
+ void setSourceContext(int model, MessageItem *messageItem);
+
+private:
+ bool m_isActive;
+ QString m_currentFileName;
+ QMdiArea *m_mdiArea;
+ QMdiSubWindow *m_mdiSubWindow;
+ QWidget *m_form;
+ TargetsHash m_targets;
+ QList<TranslatableEntry> m_highlights;
+ MultiDataModel *m_dataModel;
+
+ QString m_lastFormName;
+ QString m_lastClassName;
+ int m_lastModel;
+};
+
+QT_END_NAMESPACE
+
+#endif // FORMPREVIEWVIEW_H
diff --git a/src/linguist/linguist/globals.cpp b/src/linguist/linguist/globals.cpp
new file mode 100644
index 000000000..f2a8cd248
--- /dev/null
+++ b/src/linguist/linguist/globals.cpp
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "globals.h"
+
+const QString &settingsPrefix()
+{
+ static QString prefix = QString(QLatin1String("%1.%2/"))
+ .arg((QT_VERSION >> 16) & 0xff)
+ .arg((QT_VERSION >> 8) & 0xff);
+ return prefix;
+}
+
+QString settingPath(const char *path)
+{
+ return settingsPrefix() + QLatin1String(path);
+}
diff --git a/src/linguist/linguist/globals.h b/src/linguist/linguist/globals.h
new file mode 100644
index 000000000..7d82f5b59
--- /dev/null
+++ b/src/linguist/linguist/globals.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GLOBALS_H
+#define GLOBALS_H
+
+#include <QString>
+
+const QString &settingsPrefix();
+QString settingPath(const char *path);
+
+#endif // GLOBALS_H
diff --git a/src/linguist/linguist/images/appicon.png b/src/linguist/linguist/images/appicon.png
new file mode 100644
index 000000000..d388cbd0b
--- /dev/null
+++ b/src/linguist/linguist/images/appicon.png
Binary files differ
diff --git a/src/linguist/linguist/images/down.png b/src/linguist/linguist/images/down.png
new file mode 100644
index 000000000..29d1d4439
--- /dev/null
+++ b/src/linguist/linguist/images/down.png
Binary files differ
diff --git a/src/linguist/linguist/images/editdelete.png b/src/linguist/linguist/images/editdelete.png
new file mode 100644
index 000000000..df2a147d2
--- /dev/null
+++ b/src/linguist/linguist/images/editdelete.png
Binary files differ
diff --git a/src/linguist/linguist/images/icons/linguist-128-32.png b/src/linguist/linguist/images/icons/linguist-128-32.png
new file mode 100644
index 000000000..d108208fa
--- /dev/null
+++ b/src/linguist/linguist/images/icons/linguist-128-32.png
Binary files differ
diff --git a/src/linguist/linguist/images/icons/linguist-128-8.png b/src/linguist/linguist/images/icons/linguist-128-8.png
new file mode 100644
index 000000000..6678dd2ae
--- /dev/null
+++ b/src/linguist/linguist/images/icons/linguist-128-8.png
Binary files differ
diff --git a/src/linguist/linguist/images/icons/linguist-16-32.png b/src/linguist/linguist/images/icons/linguist-16-32.png
new file mode 100644
index 000000000..8dfc97406
--- /dev/null
+++ b/src/linguist/linguist/images/icons/linguist-16-32.png
Binary files differ
diff --git a/src/linguist/linguist/images/icons/linguist-16-8.png b/src/linguist/linguist/images/icons/linguist-16-8.png
new file mode 100644
index 000000000..4f4e83955
--- /dev/null
+++ b/src/linguist/linguist/images/icons/linguist-16-8.png
Binary files differ
diff --git a/src/linguist/linguist/images/icons/linguist-32-32.png b/src/linguist/linguist/images/icons/linguist-32-32.png
new file mode 100644
index 000000000..d388cbd0b
--- /dev/null
+++ b/src/linguist/linguist/images/icons/linguist-32-32.png
Binary files differ
diff --git a/src/linguist/linguist/images/icons/linguist-32-8.png b/src/linguist/linguist/images/icons/linguist-32-8.png
new file mode 100644
index 000000000..3db4bc5c0
--- /dev/null
+++ b/src/linguist/linguist/images/icons/linguist-32-8.png
Binary files differ
diff --git a/src/linguist/linguist/images/icons/linguist-48-32.png b/src/linguist/linguist/images/icons/linguist-48-32.png
new file mode 100644
index 000000000..ceb738759
--- /dev/null
+++ b/src/linguist/linguist/images/icons/linguist-48-32.png
Binary files differ
diff --git a/src/linguist/linguist/images/icons/linguist-48-8.png b/src/linguist/linguist/images/icons/linguist-48-8.png
new file mode 100644
index 000000000..9a13c201d
--- /dev/null
+++ b/src/linguist/linguist/images/icons/linguist-48-8.png
Binary files differ
diff --git a/src/linguist/linguist/images/icons/linguist-64-32.png b/src/linguist/linguist/images/icons/linguist-64-32.png
new file mode 100644
index 000000000..0cce805e3
--- /dev/null
+++ b/src/linguist/linguist/images/icons/linguist-64-32.png
Binary files differ
diff --git a/src/linguist/linguist/images/icons/linguist-64-8.png b/src/linguist/linguist/images/icons/linguist-64-8.png
new file mode 100644
index 000000000..05c833dc9
--- /dev/null
+++ b/src/linguist/linguist/images/icons/linguist-64-8.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/accelerator.png b/src/linguist/linguist/images/mac/accelerator.png
new file mode 100644
index 000000000..9a684c104
--- /dev/null
+++ b/src/linguist/linguist/images/mac/accelerator.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/book.png b/src/linguist/linguist/images/mac/book.png
new file mode 100644
index 000000000..7a3204c87
--- /dev/null
+++ b/src/linguist/linguist/images/mac/book.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/doneandnext.png b/src/linguist/linguist/images/mac/doneandnext.png
new file mode 100644
index 000000000..512fea884
--- /dev/null
+++ b/src/linguist/linguist/images/mac/doneandnext.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/editcopy.png b/src/linguist/linguist/images/mac/editcopy.png
new file mode 100644
index 000000000..f55136446
--- /dev/null
+++ b/src/linguist/linguist/images/mac/editcopy.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/editcut.png b/src/linguist/linguist/images/mac/editcut.png
new file mode 100644
index 000000000..a784fd570
--- /dev/null
+++ b/src/linguist/linguist/images/mac/editcut.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/editpaste.png b/src/linguist/linguist/images/mac/editpaste.png
new file mode 100644
index 000000000..64c0b2d6a
--- /dev/null
+++ b/src/linguist/linguist/images/mac/editpaste.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/filenew.png b/src/linguist/linguist/images/mac/filenew.png
new file mode 100644
index 000000000..d3882c7b3
--- /dev/null
+++ b/src/linguist/linguist/images/mac/filenew.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/fileopen.png b/src/linguist/linguist/images/mac/fileopen.png
new file mode 100644
index 000000000..fc06c5ec6
--- /dev/null
+++ b/src/linguist/linguist/images/mac/fileopen.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/fileprint.png b/src/linguist/linguist/images/mac/fileprint.png
new file mode 100644
index 000000000..808c97ea3
--- /dev/null
+++ b/src/linguist/linguist/images/mac/fileprint.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/filesave.png b/src/linguist/linguist/images/mac/filesave.png
new file mode 100644
index 000000000..b41ecf531
--- /dev/null
+++ b/src/linguist/linguist/images/mac/filesave.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/next.png b/src/linguist/linguist/images/mac/next.png
new file mode 100644
index 000000000..ba0792c36
--- /dev/null
+++ b/src/linguist/linguist/images/mac/next.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/nextunfinished.png b/src/linguist/linguist/images/mac/nextunfinished.png
new file mode 100644
index 000000000..bffbf0bb1
--- /dev/null
+++ b/src/linguist/linguist/images/mac/nextunfinished.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/phrase.png b/src/linguist/linguist/images/mac/phrase.png
new file mode 100644
index 000000000..651234109
--- /dev/null
+++ b/src/linguist/linguist/images/mac/phrase.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/prev.png b/src/linguist/linguist/images/mac/prev.png
new file mode 100644
index 000000000..612fb34dc
--- /dev/null
+++ b/src/linguist/linguist/images/mac/prev.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/prevunfinished.png b/src/linguist/linguist/images/mac/prevunfinished.png
new file mode 100644
index 000000000..4d937b2a2
--- /dev/null
+++ b/src/linguist/linguist/images/mac/prevunfinished.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/print.png b/src/linguist/linguist/images/mac/print.png
new file mode 100644
index 000000000..10ca56c82
--- /dev/null
+++ b/src/linguist/linguist/images/mac/print.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/punctuation.png b/src/linguist/linguist/images/mac/punctuation.png
new file mode 100644
index 000000000..4719fc68f
--- /dev/null
+++ b/src/linguist/linguist/images/mac/punctuation.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/redo.png b/src/linguist/linguist/images/mac/redo.png
new file mode 100644
index 000000000..8875bf246
--- /dev/null
+++ b/src/linguist/linguist/images/mac/redo.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/searchfind.png b/src/linguist/linguist/images/mac/searchfind.png
new file mode 100644
index 000000000..3561745f0
--- /dev/null
+++ b/src/linguist/linguist/images/mac/searchfind.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/undo.png b/src/linguist/linguist/images/mac/undo.png
new file mode 100644
index 000000000..a3bd5e0bf
--- /dev/null
+++ b/src/linguist/linguist/images/mac/undo.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/validateplacemarkers.png b/src/linguist/linguist/images/mac/validateplacemarkers.png
new file mode 100644
index 000000000..18ccc4cbc
--- /dev/null
+++ b/src/linguist/linguist/images/mac/validateplacemarkers.png
Binary files differ
diff --git a/src/linguist/linguist/images/mac/whatsthis.png b/src/linguist/linguist/images/mac/whatsthis.png
new file mode 100644
index 000000000..5b7078fff
--- /dev/null
+++ b/src/linguist/linguist/images/mac/whatsthis.png
Binary files differ
diff --git a/src/linguist/linguist/images/minus.png b/src/linguist/linguist/images/minus.png
new file mode 100644
index 000000000..745b44572
--- /dev/null
+++ b/src/linguist/linguist/images/minus.png
Binary files differ
diff --git a/src/linguist/linguist/images/plus.png b/src/linguist/linguist/images/plus.png
new file mode 100644
index 000000000..ef43788e6
--- /dev/null
+++ b/src/linguist/linguist/images/plus.png
Binary files differ
diff --git a/src/linguist/linguist/images/s_check_danger.png b/src/linguist/linguist/images/s_check_danger.png
new file mode 100644
index 000000000..e10157768
--- /dev/null
+++ b/src/linguist/linguist/images/s_check_danger.png
Binary files differ
diff --git a/src/linguist/linguist/images/s_check_empty.png b/src/linguist/linguist/images/s_check_empty.png
new file mode 100644
index 000000000..759a41b6c
--- /dev/null
+++ b/src/linguist/linguist/images/s_check_empty.png
Binary files differ
diff --git a/src/linguist/linguist/images/s_check_obsolete.png b/src/linguist/linguist/images/s_check_obsolete.png
new file mode 100644
index 000000000..b852b639f
--- /dev/null
+++ b/src/linguist/linguist/images/s_check_obsolete.png
Binary files differ
diff --git a/src/linguist/linguist/images/s_check_off.png b/src/linguist/linguist/images/s_check_off.png
new file mode 100644
index 000000000..640b68972
--- /dev/null
+++ b/src/linguist/linguist/images/s_check_off.png
Binary files differ
diff --git a/src/linguist/linguist/images/s_check_on.png b/src/linguist/linguist/images/s_check_on.png
new file mode 100644
index 000000000..afcaf634d
--- /dev/null
+++ b/src/linguist/linguist/images/s_check_on.png
Binary files differ
diff --git a/src/linguist/linguist/images/s_check_warning.png b/src/linguist/linguist/images/s_check_warning.png
new file mode 100644
index 000000000..f689c3303
--- /dev/null
+++ b/src/linguist/linguist/images/s_check_warning.png
Binary files differ
diff --git a/src/linguist/linguist/images/splash.png b/src/linguist/linguist/images/splash.png
new file mode 100644
index 000000000..0e99a1cf0
--- /dev/null
+++ b/src/linguist/linguist/images/splash.png
Binary files differ
diff --git a/src/linguist/linguist/images/up.png b/src/linguist/linguist/images/up.png
new file mode 100644
index 000000000..e43731221
--- /dev/null
+++ b/src/linguist/linguist/images/up.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/accelerator.png b/src/linguist/linguist/images/win/accelerator.png
new file mode 100644
index 000000000..c423c12cf
--- /dev/null
+++ b/src/linguist/linguist/images/win/accelerator.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/book.png b/src/linguist/linguist/images/win/book.png
new file mode 100644
index 000000000..09ec4d33f
--- /dev/null
+++ b/src/linguist/linguist/images/win/book.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/doneandnext.png b/src/linguist/linguist/images/win/doneandnext.png
new file mode 100644
index 000000000..9d1d58d6a
--- /dev/null
+++ b/src/linguist/linguist/images/win/doneandnext.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/editcopy.png b/src/linguist/linguist/images/win/editcopy.png
new file mode 100644
index 000000000..1121b47d8
--- /dev/null
+++ b/src/linguist/linguist/images/win/editcopy.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/editcut.png b/src/linguist/linguist/images/win/editcut.png
new file mode 100644
index 000000000..4b6c82c7a
--- /dev/null
+++ b/src/linguist/linguist/images/win/editcut.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/editpaste.png b/src/linguist/linguist/images/win/editpaste.png
new file mode 100644
index 000000000..ffab15aaf
--- /dev/null
+++ b/src/linguist/linguist/images/win/editpaste.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/filenew.png b/src/linguist/linguist/images/win/filenew.png
new file mode 100644
index 000000000..af5d12214
--- /dev/null
+++ b/src/linguist/linguist/images/win/filenew.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/fileopen.png b/src/linguist/linguist/images/win/fileopen.png
new file mode 100644
index 000000000..fc6f17e97
--- /dev/null
+++ b/src/linguist/linguist/images/win/fileopen.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/filesave.png b/src/linguist/linguist/images/win/filesave.png
new file mode 100644
index 000000000..8feec99be
--- /dev/null
+++ b/src/linguist/linguist/images/win/filesave.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/next.png b/src/linguist/linguist/images/win/next.png
new file mode 100644
index 000000000..8df4127a0
--- /dev/null
+++ b/src/linguist/linguist/images/win/next.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/nextunfinished.png b/src/linguist/linguist/images/win/nextunfinished.png
new file mode 100644
index 000000000..636b9213b
--- /dev/null
+++ b/src/linguist/linguist/images/win/nextunfinished.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/phrase.png b/src/linguist/linguist/images/win/phrase.png
new file mode 100644
index 000000000..8ff952c51
--- /dev/null
+++ b/src/linguist/linguist/images/win/phrase.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/prev.png b/src/linguist/linguist/images/win/prev.png
new file mode 100644
index 000000000..0780bc23d
--- /dev/null
+++ b/src/linguist/linguist/images/win/prev.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/prevunfinished.png b/src/linguist/linguist/images/win/prevunfinished.png
new file mode 100644
index 000000000..139d11b03
--- /dev/null
+++ b/src/linguist/linguist/images/win/prevunfinished.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/print.png b/src/linguist/linguist/images/win/print.png
new file mode 100644
index 000000000..ba7c02dc1
--- /dev/null
+++ b/src/linguist/linguist/images/win/print.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/punctuation.png b/src/linguist/linguist/images/win/punctuation.png
new file mode 100644
index 000000000..e2372a2ef
--- /dev/null
+++ b/src/linguist/linguist/images/win/punctuation.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/redo.png b/src/linguist/linguist/images/win/redo.png
new file mode 100644
index 000000000..686ad141c
--- /dev/null
+++ b/src/linguist/linguist/images/win/redo.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/searchfind.png b/src/linguist/linguist/images/win/searchfind.png
new file mode 100644
index 000000000..6ea35e930
--- /dev/null
+++ b/src/linguist/linguist/images/win/searchfind.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/undo.png b/src/linguist/linguist/images/win/undo.png
new file mode 100644
index 000000000..c3b8c5136
--- /dev/null
+++ b/src/linguist/linguist/images/win/undo.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/validateplacemarkers.png b/src/linguist/linguist/images/win/validateplacemarkers.png
new file mode 100644
index 000000000..cc127fde9
--- /dev/null
+++ b/src/linguist/linguist/images/win/validateplacemarkers.png
Binary files differ
diff --git a/src/linguist/linguist/images/win/whatsthis.png b/src/linguist/linguist/images/win/whatsthis.png
new file mode 100644
index 000000000..623cad687
--- /dev/null
+++ b/src/linguist/linguist/images/win/whatsthis.png
Binary files differ
diff --git a/src/linguist/linguist/linguist.icns b/src/linguist/linguist/linguist.icns
new file mode 100644
index 000000000..5918e001c
--- /dev/null
+++ b/src/linguist/linguist/linguist.icns
Binary files differ
diff --git a/src/linguist/linguist/linguist.ico b/src/linguist/linguist/linguist.ico
new file mode 100644
index 000000000..5bbdc485b
--- /dev/null
+++ b/src/linguist/linguist/linguist.ico
Binary files differ
diff --git a/src/linguist/linguist/linguist.pro b/src/linguist/linguist/linguist.pro
new file mode 100644
index 000000000..ce8d5854f
--- /dev/null
+++ b/src/linguist/linguist/linguist.pro
@@ -0,0 +1,96 @@
+TEMPLATE = app
+LANGUAGE = C++
+DESTDIR = ../../../bin
+
+QT += xml
+
+CONFIG += qt \
+ warn_on \
+ uitools
+
+DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII
+build_all:!build_pass {
+ CONFIG -= build_all
+ CONFIG += release
+}
+
+include(../shared/formats.pri)
+
+DEFINES += QFORMINTERNAL_NAMESPACE
+INCLUDEPATH += ../../designer/src/uitools
+INCLUDEPATH += ../../designer/src/lib/uilib
+
+SOURCES += \
+ batchtranslationdialog.cpp \
+ errorsview.cpp \
+ finddialog.cpp \
+ formpreviewview.cpp \
+ globals.cpp \
+ main.cpp \
+ mainwindow.cpp \
+ messageeditor.cpp \
+ messageeditorwidgets.cpp \
+ messagehighlighter.cpp \
+ messagemodel.cpp \
+ phrasebookbox.cpp \
+ phrase.cpp \
+ phrasemodel.cpp \
+ phraseview.cpp \
+ printout.cpp \
+ recentfiles.cpp \
+ sourcecodeview.cpp \
+ statistics.cpp \
+ translatedialog.cpp \
+ translationsettingsdialog.cpp \
+ ../shared/simtexth.cpp
+
+HEADERS += \
+ batchtranslationdialog.h \
+ errorsview.h \
+ finddialog.h \
+ formpreviewview.h \
+ globals.h \
+ mainwindow.h \
+ messageeditor.h \
+ messageeditorwidgets.h \
+ messagehighlighter.h \
+ messagemodel.h \
+ phrasebookbox.h \
+ phrase.h \
+ phrasemodel.h \
+ phraseview.h \
+ printout.h \
+ recentfiles.h \
+ sourcecodeview.h \
+ statistics.h \
+ translatedialog.h \
+ translationsettingsdialog.h \
+ ../shared/simtexth.h
+
+contains(QT_PRODUCT, OpenSource.*):DEFINES *= QT_OPENSOURCE
+DEFINES += QT_KEYWORDS
+TARGET = linguist
+win32:RC_FILE = linguist.rc
+mac {
+ static:CONFIG -= global_init_link_order
+ ICON = linguist.icns
+ TARGET = Linguist
+ QMAKE_INFO_PLIST=Info_mac.plist
+}
+PROJECTNAME = Qt \
+ Linguist
+target.path = $$[QT_INSTALL_BINS]
+INSTALLS += target
+phrasebooks.path = $$[QT_INSTALL_DATA]/phrasebooks
+
+# ## will this work on windows?
+phrasebooks.files = $$QT_SOURCE_TREE/tools/linguist/phrasebooks/*
+INSTALLS += phrasebooks
+FORMS += statistics.ui \
+ phrasebookbox.ui \
+ batchtranslation.ui \
+ translatedialog.ui \
+ mainwindow.ui \
+ translationsettings.ui \
+ finddialog.ui
+RESOURCES += linguist.qrc
diff --git a/src/linguist/linguist/linguist.qrc b/src/linguist/linguist/linguist.qrc
new file mode 100644
index 000000000..b70b9cd52
--- /dev/null
+++ b/src/linguist/linguist/linguist.qrc
@@ -0,0 +1,57 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/appicon.png</file>
+ <file>images/mac/accelerator.png</file>
+ <file>images/mac/book.png</file>
+ <file>images/mac/doneandnext.png</file>
+ <file>images/mac/editcopy.png</file>
+ <file>images/mac/editcut.png</file>
+ <file>images/mac/editpaste.png</file>
+ <file>images/mac/fileopen.png</file>
+ <file>images/mac/filesave.png</file>
+ <file>images/mac/next.png</file>
+ <file>images/mac/nextunfinished.png</file>
+ <file>images/mac/phrase.png</file>
+ <file>images/mac/prev.png</file>
+ <file>images/mac/prevunfinished.png</file>
+ <file>images/mac/print.png</file>
+ <file>images/mac/punctuation.png</file>
+ <file>images/mac/redo.png</file>
+ <file>images/mac/searchfind.png</file>
+ <file>images/mac/undo.png</file>
+ <file>images/mac/validateplacemarkers.png</file>
+ <file>images/mac/whatsthis.png</file>
+ <file>images/s_check_danger.png</file>
+ <file>images/s_check_empty.png</file>
+ <file>images/s_check_obsolete.png</file>
+ <file>images/s_check_off.png</file>
+ <file>images/s_check_on.png</file>
+ <file>images/s_check_warning.png</file>
+ <file>images/splash.png</file>
+ <file>images/up.png</file>
+ <file>images/down.png</file>
+ <file>images/editdelete.png</file>
+ <file>images/minus.png</file>
+ <file>images/plus.png</file>
+ <file>images/win/accelerator.png</file>
+ <file>images/win/book.png</file>
+ <file>images/win/doneandnext.png</file>
+ <file>images/win/editcopy.png</file>
+ <file>images/win/editcut.png</file>
+ <file>images/win/editpaste.png</file>
+ <file>images/win/fileopen.png</file>
+ <file>images/win/filesave.png</file>
+ <file>images/win/next.png</file>
+ <file>images/win/nextunfinished.png</file>
+ <file>images/win/phrase.png</file>
+ <file>images/win/prev.png</file>
+ <file>images/win/prevunfinished.png</file>
+ <file>images/win/print.png</file>
+ <file>images/win/punctuation.png</file>
+ <file>images/win/redo.png</file>
+ <file>images/win/searchfind.png</file>
+ <file>images/win/undo.png</file>
+ <file>images/win/validateplacemarkers.png</file>
+ <file>images/win/whatsthis.png</file>
+ </qresource>
+</RCC>
diff --git a/src/linguist/linguist/linguist.rc b/src/linguist/linguist/linguist.rc
new file mode 100644
index 000000000..375f02a27
--- /dev/null
+++ b/src/linguist/linguist/linguist.rc
@@ -0,0 +1,32 @@
+#include "winver.h"
+
+IDI_ICON1 ICON DISCARDABLE "linguist.ico"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGS 0x0L
+ FILEFLAGSMASK 0x3fL
+ FILEOS 0x00040004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "CompanyName", "Nokia Corporation and/or its subsidiary(-ies)"
+ VALUE "FileDescription", "Qt Linguist"
+ VALUE "FileVersion", "1.0.0.0"
+ VALUE "LegalCopyright", "Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)."
+ VALUE "InternalName", "linguist"
+ VALUE "OriginalFilename", "linguist.exe"
+ VALUE "ProductName", "Qt Linguist"
+ VALUE "ProductVersion", "1.0.0.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0, 1200
+ END
+END
diff --git a/src/linguist/linguist/main.cpp b/src/linguist/linguist/main.cpp
new file mode 100644
index 000000000..c1388e198
--- /dev/null
+++ b/src/linguist/linguist/main.cpp
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mainwindow.h"
+#include "globals.h"
+
+#include <QtCore/QFile>
+#include <QtCore/QLibraryInfo>
+#include <QtCore/QLocale>
+#include <QtCore/QSettings>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTranslator>
+
+#include <QtGui/QApplication>
+#include <QtGui/QDesktopWidget>
+#include <QtGui/QPixmap>
+#include <QtGui/QSplashScreen>
+
+QT_USE_NAMESPACE
+
+int main(int argc, char **argv)
+{
+ Q_INIT_RESOURCE(linguist);
+
+ QApplication app(argc, argv);
+ QApplication::setOverrideCursor(Qt::WaitCursor);
+
+ QStringList files;
+ QString resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
+ QStringList args = app.arguments();
+
+ for (int i = 1; i < args.count(); ++i) {
+ QString argument = args.at(i);
+ if (argument == QLatin1String("-resourcedir")) {
+ if (i + 1 < args.count()) {
+ resourceDir = QFile::decodeName(args.at(++i).toLocal8Bit());
+ } else {
+ // issue a warning
+ }
+ } else if (!files.contains(argument)) {
+ files.append(argument);
+ }
+ }
+
+ QTranslator translator;
+ QTranslator qtTranslator;
+ QString sysLocale = QLocale::system().name();
+ if (translator.load(QLatin1String("linguist_") + sysLocale, resourceDir)) {
+ app.installTranslator(&translator);
+ if (qtTranslator.load(QLatin1String("qt_") + sysLocale, resourceDir))
+ app.installTranslator(&qtTranslator);
+ else
+ app.removeTranslator(&translator);
+ }
+
+ app.setOrganizationName(QLatin1String("Trolltech"));
+ app.setApplicationName(QLatin1String("Linguist"));
+
+ QSettings config;
+
+ QWidget tmp;
+ tmp.restoreGeometry(config.value(settingPath("Geometry/WindowGeometry")).toByteArray());
+
+ QSplashScreen *splash = 0;
+ int screenId = QApplication::desktop()->screenNumber(tmp.geometry().center());
+ splash = new QSplashScreen(QApplication::desktop()->screen(screenId),
+ QPixmap(QLatin1String(":/images/splash.png")));
+ if (QApplication::desktop()->isVirtualDesktop()) {
+ QRect srect(0, 0, splash->width(), splash->height());
+ splash->move(QApplication::desktop()->availableGeometry(screenId).center() - srect.center());
+ }
+ splash->setAttribute(Qt::WA_DeleteOnClose);
+ splash->show();
+
+ MainWindow mw;
+ mw.show();
+ splash->finish(&mw);
+ QApplication::restoreOverrideCursor();
+
+ mw.openFiles(files, true);
+
+ return app.exec();
+}
diff --git a/src/linguist/linguist/mainwindow.cpp b/src/linguist/linguist/mainwindow.cpp
new file mode 100644
index 000000000..e6fad1b38
--- /dev/null
+++ b/src/linguist/linguist/mainwindow.cpp
@@ -0,0 +1,2724 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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 =
+ QVariant::fromValue(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 &currentIndex, 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 &currentIndex, 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
diff --git a/src/linguist/linguist/mainwindow.h b/src/linguist/linguist/mainwindow.h
new file mode 100644
index 000000000..fe9daf264
--- /dev/null
+++ b/src/linguist/linguist/mainwindow.h
@@ -0,0 +1,267 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include "phrase.h"
+#include "ui_mainwindow.h"
+#include "recentfiles.h"
+#include "messagemodel.h"
+
+#include <QtCore/QHash>
+#include <QtCore/QLocale>
+
+#include <QtGui/QMainWindow>
+
+QT_BEGIN_NAMESPACE
+
+class QPixmap;
+class QAction;
+class QDialog;
+class QLabel;
+class QMenu;
+class QPrinter;
+class QProcess;
+class QIcon;
+class QSortFilterProxyModel;
+class QStackedWidget;
+class QTableView;
+class QTreeView;
+
+class BatchTranslationDialog;
+class ErrorsView;
+class FindDialog;
+class FocusWatcher;
+class FormPreviewView;
+class MessageEditor;
+class PhraseView;
+class SourceCodeView;
+class Statistics;
+class TranslateDialog;
+class TranslationSettingsDialog;
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+public:
+ enum {PhraseCloseMenu, PhraseEditMenu, PhrasePrintMenu};
+
+ MainWindow();
+ ~MainWindow();
+
+ bool openFiles(const QStringList &names, bool readWrite = true);
+ static RecentFiles &recentFiles();
+ static const QString &resourcePrefix();
+ static QString friendlyString(const QString &str);
+
+protected:
+ void readConfig();
+ void writeConfig();
+ void closeEvent(QCloseEvent *);
+ bool eventFilter(QObject *object, QEvent *event);
+
+private slots:
+ void doneAndNext();
+ void prev();
+ void next();
+ void recentFileActivated(QAction *action);
+ void setupRecentFilesMenu();
+ void open();
+ void openAux();
+ void saveAll();
+ void save();
+ void saveAs();
+ void releaseAll();
+ void release();
+ void releaseAs();
+ void print();
+ void closeFile();
+ bool closeAll();
+ void findAgain();
+ void showTranslateDialog();
+ void showBatchTranslateDialog();
+ void showTranslationSettings();
+ void updateTranslateHit(bool &hit);
+ void translate(int mode);
+ void newPhraseBook();
+ void openPhraseBook();
+ void closePhraseBook(QAction *action);
+ void editPhraseBook(QAction *action);
+ void printPhraseBook(QAction *action);
+ void addToPhraseBook();
+ void manual();
+ void resetSorting();
+ void about();
+ void aboutQt();
+
+ void updateViewMenu();
+ void fileAboutToShow();
+ void editAboutToShow();
+
+ void showContextDock();
+ void showMessagesDock();
+ void showPhrasesDock();
+ void showSourceCodeDock();
+ void showErrorDock();
+
+ void setupPhrase();
+ bool maybeSaveAll();
+ bool maybeSave(int model);
+ void updateProgress();
+ void maybeUpdateStatistics(const MultiDataIndex &);
+ void translationChanged(const MultiDataIndex &);
+ void updateCaption();
+ void updateLatestModel(const QModelIndex &index);
+ void selectedContextChanged(const QModelIndex &sortedIndex, const QModelIndex &oldIndex);
+ void selectedMessageChanged(const QModelIndex &sortedIndex, const QModelIndex &oldIndex);
+
+ // To synchronize from the message editor to the model ...
+ void updateTranslation(const QStringList &translations);
+ void updateTranslatorComment(const QString &comment);
+
+ void updateActiveModel(int);
+ void refreshItemViews();
+ void toggleFinished(const QModelIndex &index);
+ void prevUnfinished();
+ void nextUnfinished();
+ void findNext(const QString &text, DataModel::FindLocation where, bool matchCase, bool ignoreAccelerators);
+ void revalidate();
+ void toggleStatistics();
+ void onWhatsThis();
+ void updatePhraseDicts();
+ void updatePhraseDict(int model);
+
+private:
+ QModelIndex nextContext(const QModelIndex &index) const;
+ QModelIndex prevContext(const QModelIndex &index) const;
+ QModelIndex nextMessage(const QModelIndex &currentIndex, bool checkUnfinished = false) const;
+ QModelIndex prevMessage(const QModelIndex &currentIndex, bool checkUnfinished = false) const;
+ bool next(bool checkUnfinished);
+ bool prev(bool checkUnfinished);
+
+ void updateStatistics();
+ void initViewHeaders();
+ void modelCountChanged();
+ void setupMenuBar();
+ void setupToolBars();
+ void setCurrentMessage(const QModelIndex &index);
+ void setCurrentMessage(const QModelIndex &index, int model);
+ QModelIndex setMessageViewRoot(const QModelIndex &index);
+ QModelIndex currentContextIndex() const;
+ QModelIndex currentMessageIndex() const;
+ PhraseBook *openPhraseBook(const QString &name);
+ bool isPhraseBookOpen(const QString &name);
+ bool savePhraseBook(QString *name, PhraseBook &pb);
+ bool maybeSavePhraseBook(PhraseBook *phraseBook);
+ bool closePhraseBooks();
+ QStringList pickTranslationFiles();
+ void showTranslationSettings(int model);
+ void updateLatestModel(int model);
+ void updatePhraseBookActions();
+ void updatePhraseDictInternal(int model);
+ void releaseInternal(int model);
+ void saveInternal(int model);
+
+ QPrinter *printer();
+
+ // FIXME: move to DataModel
+ void updateDanger(const MultiDataIndex &index, bool verbose);
+
+ bool searchItem(const QString &searchWhat);
+
+ QProcess *m_assistantProcess;
+ QTreeView *m_contextView;
+ QTreeView *m_messageView;
+ MultiDataModel *m_dataModel;
+ MessageModel *m_messageModel;
+ QSortFilterProxyModel *m_sortedContextsModel;
+ QSortFilterProxyModel *m_sortedMessagesModel;
+ MessageEditor *m_messageEditor;
+ PhraseView *m_phraseView;
+ QStackedWidget *m_sourceAndFormView;
+ SourceCodeView *m_sourceCodeView;
+ FormPreviewView *m_formPreviewView;
+ ErrorsView *m_errorsView;
+ QLabel *m_progressLabel;
+ QLabel *m_modifiedLabel;
+ FocusWatcher *m_focusWatcher;
+ QString m_phraseBookDir;
+ // model : keyword -> list of appropriate phrases in the phrasebooks
+ QList<QHash<QString, QList<Phrase *> > > m_phraseDict;
+ QList<PhraseBook *> m_phraseBooks;
+ QMap<QAction *, PhraseBook *> m_phraseBookMenu[3];
+ QPrinter *m_printer;
+
+ FindDialog *m_findDialog;
+ QString m_findText;
+ Qt::CaseSensitivity m_findMatchCase;
+ bool m_findIgnoreAccelerators;
+ DataModel::FindLocation m_findWhere;
+ DataModel::FindLocation m_foundWhere;
+
+ TranslateDialog *m_translateDialog;
+ QString m_latestFindText;
+ int m_latestCaseSensitivity;
+ int m_remainingCount;
+ int m_hitCount;
+
+ BatchTranslationDialog *m_batchTranslateDialog;
+ TranslationSettingsDialog *m_translationSettingsDialog;
+
+ bool m_settingCurrentMessage;
+ int m_fileActiveModel;
+ int m_editActiveModel;
+ MultiDataIndex m_currentIndex;
+
+ QDockWidget *m_contextDock;
+ QDockWidget *m_messagesDock;
+ QDockWidget *m_phrasesDock;
+ QDockWidget *m_sourceAndFormDock;
+ QDockWidget *m_errorsDock;
+
+ Ui::MainWindow m_ui; // menus and actions
+ Statistics *m_statistics;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/linguist/linguist/mainwindow.ui b/src/linguist/linguist/mainwindow.ui
new file mode 100644
index 000000000..3453740ec
--- /dev/null
+++ b/src/linguist/linguist/mainwindow.ui
@@ -0,0 +1,892 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <comment>*********************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+*********************************************************************</comment>
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>673</width>
+ <height>461</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>MainWindow</string>
+ </property>
+ <widget class="QWidget" name="centralwidget"/>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>673</width>
+ <height>28</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menuPhrases">
+ <property name="title">
+ <string>&amp;Phrases</string>
+ </property>
+ <widget class="QMenu" name="menuClosePhraseBook">
+ <property name="title">
+ <string>&amp;Close Phrase Book</string>
+ </property>
+ </widget>
+ <widget class="QMenu" name="menuEditPhraseBook">
+ <property name="title">
+ <string>&amp;Edit Phrase Book</string>
+ </property>
+ </widget>
+ <widget class="QMenu" name="menuPrintPhraseBook">
+ <property name="title">
+ <string>&amp;Print Phrase Book</string>
+ </property>
+ </widget>
+ <addaction name="actionNewPhraseBook"/>
+ <addaction name="actionOpenPhraseBook"/>
+ <addaction name="menuClosePhraseBook"/>
+ <addaction name="separator"/>
+ <addaction name="menuEditPhraseBook"/>
+ <addaction name="menuPrintPhraseBook"/>
+ <addaction name="actionAddToPhraseBook"/>
+ </widget>
+ <widget class="QMenu" name="menuValidation">
+ <property name="title">
+ <string>V&amp;alidation</string>
+ </property>
+ <addaction name="actionAccelerators"/>
+ <addaction name="actionEndingPunctuation"/>
+ <addaction name="actionPhraseMatches"/>
+ <addaction name="actionPlaceMarkerMatches"/>
+ </widget>
+ <widget class="QMenu" name="menuView">
+ <property name="title">
+ <string>&amp;View</string>
+ </property>
+ <widget class="QMenu" name="menuViewViews">
+ <property name="title">
+ <string>Vie&amp;ws</string>
+ </property>
+ </widget>
+ <widget class="QMenu" name="menuToolbars">
+ <property name="title">
+ <string>&amp;Toolbars</string>
+ </property>
+ </widget>
+ <addaction name="actionResetSorting"/>
+ <addaction name="actionDisplayGuesses"/>
+ <addaction name="actionStatistics"/>
+ <addaction name="actionLengthVariants"/>
+ <addaction name="separator"/>
+ <addaction name="menuToolbars"/>
+ <addaction name="menuViewViews"/>
+ </widget>
+ <widget class="QMenu" name="menuHelp">
+ <property name="title">
+ <string>&amp;Help</string>
+ </property>
+ <addaction name="actionManual"/>
+ <addaction name="actionAbout"/>
+ <addaction name="actionAboutQt"/>
+ <addaction name="actionWhatsThis"/>
+ </widget>
+ <widget class="QMenu" name="menuTranslation">
+ <property name="title">
+ <string>&amp;Translation</string>
+ </property>
+ <addaction name="actionPrevUnfinished"/>
+ <addaction name="actionNextUnfinished"/>
+ <addaction name="actionPrev"/>
+ <addaction name="actionNext"/>
+ <addaction name="actionDoneAndNext"/>
+ <addaction name="actionBeginFromSource"/>
+ </widget>
+ <widget class="QMenu" name="menuFile">
+ <property name="title">
+ <string>&amp;File</string>
+ </property>
+ <widget class="QMenu" name="menuRecentlyOpenedFiles">
+ <property name="title">
+ <string>Recently Opened &amp;Files</string>
+ </property>
+ </widget>
+ <addaction name="actionOpen"/>
+ <addaction name="actionOpenAux"/>
+ <addaction name="menuRecentlyOpenedFiles"/>
+ <addaction name="separator"/>
+ <addaction name="actionSaveAll"/>
+ <addaction name="actionSave"/>
+ <addaction name="actionSaveAs"/>
+ <addaction name="actionReleaseAll"/>
+ <addaction name="actionRelease"/>
+ <addaction name="actionReleaseAs"/>
+ <addaction name="separator"/>
+ <addaction name="actionPrint"/>
+ <addaction name="separator"/>
+ <addaction name="actionCloseAll"/>
+ <addaction name="actionClose"/>
+ <addaction name="separator"/>
+ <addaction name="actionExit"/>
+ </widget>
+ <widget class="QMenu" name="menuEdit">
+ <property name="title">
+ <string>&amp;Edit</string>
+ </property>
+ <addaction name="actionUndo"/>
+ <addaction name="actionRedo"/>
+ <addaction name="separator"/>
+ <addaction name="actionCut"/>
+ <addaction name="actionCopy"/>
+ <addaction name="actionPaste"/>
+ <addaction name="actionSelectAll"/>
+ <addaction name="separator"/>
+ <addaction name="actionFind"/>
+ <addaction name="actionFindNext"/>
+ <addaction name="actionSearchAndTranslate"/>
+ <addaction name="actionBatchTranslation"/>
+ <addaction name="actionTranslationFileSettings"/>
+ </widget>
+ <addaction name="menuFile"/>
+ <addaction name="menuEdit"/>
+ <addaction name="menuTranslation"/>
+ <addaction name="menuValidation"/>
+ <addaction name="menuPhrases"/>
+ <addaction name="menuView"/>
+ <addaction name="menuHelp"/>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ <action name="actionOpen">
+ <property name="text">
+ <string>&amp;Open...</string>
+ </property>
+ <property name="whatsThis">
+ <string>Open a Qt translation source file (TS file) for editing</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+O</string>
+ </property>
+ </action>
+ <action name="actionExit">
+ <property name="text">
+ <string>E&amp;xit</string>
+ </property>
+ <property name="statusTip">
+ <string/>
+ </property>
+ <property name="whatsThis">
+ <string>Close this window and exit.</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Q</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::QuitRole</enum>
+ </property>
+ </action>
+ <action name="actionSave">
+ <property name="text">
+ <string>Save</string>
+ </property>
+ <property name="whatsThis">
+ <string>Save changes made to this Qt translation source file</string>
+ </property>
+ </action>
+ <action name="actionSaveAs">
+ <property name="text">
+ <string>Save &amp;As...</string>
+ </property>
+ <property name="iconText">
+ <string>Save As...</string>
+ </property>
+ <property name="whatsThis">
+ <string>Save changes made to this Qt translation source file into a new file.</string>
+ </property>
+ </action>
+ <action name="actionRelease">
+ <property name="text">
+ <string>Release</string>
+ </property>
+ <property name="whatsThis">
+ <string>Create a Qt message file suitable for released applications from the current message file.</string>
+ </property>
+ </action>
+ <action name="actionPrint">
+ <property name="text">
+ <string>&amp;Print...</string>
+ </property>
+ <property name="whatsThis">
+ <string>Print a list of all the translation units in the current translation source file.</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+P</string>
+ </property>
+ </action>
+ <action name="actionUndo">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Undo</string>
+ </property>
+ <property name="whatsThis">
+ <string>Undo the last editing operation performed on the current translation.</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Z</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionRedo">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Redo</string>
+ </property>
+ <property name="whatsThis">
+ <string>Redo an undone editing operation performed on the translation.</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Y</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionCut">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Cu&amp;t</string>
+ </property>
+ <property name="whatsThis">
+ <string>Copy the selected translation text to the clipboard and deletes it.</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+X</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionCopy">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Copy</string>
+ </property>
+ <property name="whatsThis">
+ <string>Copy the selected translation text to the clipboard.</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+C</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionPaste">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Paste</string>
+ </property>
+ <property name="whatsThis">
+ <string>Paste the clipboard text into the translation.</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+V</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionSelectAll">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Select &amp;All</string>
+ </property>
+ <property name="whatsThis">
+ <string>Select the whole translation text.</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+A</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionFind">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Find...</string>
+ </property>
+ <property name="whatsThis">
+ <string>Search for some text in the translation source file.</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+F</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionFindNext">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Find &amp;Next</string>
+ </property>
+ <property name="whatsThis">
+ <string>Continue the search where it was left.</string>
+ </property>
+ <property name="shortcut">
+ <string>F3</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionPrevUnfinished">
+ <property name="text">
+ <string>&amp;Prev Unfinished</string>
+ </property>
+ <property name="toolTip">
+ <string>Previous unfinished item</string>
+ </property>
+ <property name="whatsThis">
+ <string>Move to the previous unfinished item.</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+K</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionNextUnfinished">
+ <property name="text">
+ <string>&amp;Next Unfinished</string>
+ </property>
+ <property name="toolTip">
+ <string>Next unfinished item</string>
+ </property>
+ <property name="whatsThis">
+ <string>Move to the next unfinished item.</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+J</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionPrev">
+ <property name="text">
+ <string>P&amp;rev</string>
+ </property>
+ <property name="toolTip">
+ <string>Move to previous item</string>
+ </property>
+ <property name="whatsThis">
+ <string>Move to the previous item.</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Shift+K</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionNext">
+ <property name="text">
+ <string>Ne&amp;xt</string>
+ </property>
+ <property name="toolTip">
+ <string>Next item</string>
+ </property>
+ <property name="whatsThis">
+ <string>Move to the next item.</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Shift+J</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionDoneAndNext">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Done and Next</string>
+ </property>
+ <property name="toolTip">
+ <string>Mark item as done and move to the next unfinished item</string>
+ </property>
+ <property name="whatsThis">
+ <string>Mark this item as done and move to the next unfinished item.</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionBeginFromSource">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Copy from source text</string>
+ </property>
+ <property name="iconText">
+ <string>Copy from source text</string>
+ </property>
+ <property name="toolTip">
+ <string>Copies the source text into the translation field</string>
+ </property>
+ <property name="whatsThis">
+ <string>Copies the source text into the translation field.</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+B</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionAccelerators">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Accelerators</string>
+ </property>
+ <property name="toolTip">
+ <string>Toggle the validity check of accelerators</string>
+ </property>
+ <property name="whatsThis">
+ <string>Toggle the validity check of accelerators, i.e. whether the number of ampersands in the source and translation text is the same. If the check fails, a message is shown in the warnings window.</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionEndingPunctuation">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Ending Punctuation</string>
+ </property>
+ <property name="toolTip">
+ <string>Toggle the validity check of ending punctuation</string>
+ </property>
+ <property name="whatsThis">
+ <string>Toggle the validity check of ending punctuation. If the check fails, a message is shown in the warnings window.</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionPhraseMatches">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Phrase matches</string>
+ </property>
+ <property name="toolTip">
+ <string>Toggle checking that phrase suggestions are used</string>
+ </property>
+ <property name="whatsThis">
+ <string>Toggle checking that phrase suggestions are used. If the check fails, a message is shown in the warnings window.</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionPlaceMarkerMatches">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Place &amp;Marker Matches</string>
+ </property>
+ <property name="toolTip">
+ <string>Toggle the validity check of place markers</string>
+ </property>
+ <property name="whatsThis">
+ <string>Toggle the validity check of place markers, i.e. whether %1, %2, ... are used consistently in the source text and translation text. If the check fails, a message is shown in the warnings window.</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionNewPhraseBook">
+ <property name="text">
+ <string>&amp;New Phrase Book...</string>
+ </property>
+ <property name="whatsThis">
+ <string>Create a new phrase book.</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+N</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionOpenPhraseBook">
+ <property name="text">
+ <string>&amp;Open Phrase Book...</string>
+ </property>
+ <property name="whatsThis">
+ <string>Open a phrase book to assist translation.</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+H</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionResetSorting">
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Reset Sorting</string>
+ </property>
+ <property name="whatsThis">
+ <string>Sort the items back in the same order as in the message file.</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionDisplayGuesses">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Display guesses</string>
+ </property>
+ <property name="whatsThis">
+ <string>Set whether or not to display translation guesses.</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionStatistics">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Statistics</string>
+ </property>
+ <property name="whatsThis">
+ <string>Display translation statistics.</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionManual">
+ <property name="text">
+ <string>&amp;Manual</string>
+ </property>
+ <property name="whatsThis">
+ <string/>
+ </property>
+ <property name="shortcut">
+ <string>F1</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionAbout">
+ <property name="text">
+ <string>About Qt Linguist</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::AboutRole</enum>
+ </property>
+ </action>
+ <action name="actionAboutQt">
+ <property name="text">
+ <string>About Qt</string>
+ </property>
+ <property name="whatsThis">
+ <string>Display information about the Qt toolkit by Nokia.</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::AboutQtRole</enum>
+ </property>
+ </action>
+ <action name="actionWhatsThis">
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;What's This?</string>
+ </property>
+ <property name="iconText">
+ <string>What's This?</string>
+ </property>
+ <property name="toolTip">
+ <string>What's This?</string>
+ </property>
+ <property name="whatsThis">
+ <string>Enter What's This? mode.</string>
+ </property>
+ <property name="shortcut">
+ <string>Shift+F1</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionSearchAndTranslate">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Search And Translate...</string>
+ </property>
+ <property name="whatsThis">
+ <string>Replace the translation on all entries that matches the search source text.</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionBatchTranslation">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Batch Translation...</string>
+ </property>
+ <property name="whatsThis">
+ <string>Batch translate all entries using the information in the phrase books.</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionReleaseAs">
+ <property name="text">
+ <string>Release As...</string>
+ </property>
+ <property name="iconText">
+ <string>Release As...</string>
+ </property>
+ <property name="whatsThis">
+ <string>Create a Qt message file suitable for released applications from the current message file. The filename will automatically be determined from the name of the TS file.</string>
+ </property>
+ </action>
+ <action name="actionFile">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>File</string>
+ </property>
+ </action>
+ <action name="actionEdit">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Edit</string>
+ </property>
+ </action>
+ <action name="actionTranslation">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Translation</string>
+ </property>
+ </action>
+ <action name="actionValidation">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Validation</string>
+ </property>
+ </action>
+ <action name="actionHelp">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Help</string>
+ </property>
+ </action>
+ <action name="actionPreviewForm">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Open/Refresh Form &amp;Preview</string>
+ </property>
+ <property name="iconText">
+ <string>Form Preview Tool</string>
+ </property>
+ <property name="toolTip">
+ <string>Form Preview Tool</string>
+ </property>
+ <property name="shortcut">
+ <string>F5</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionTranslationFileSettings">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Translation File &amp;Settings...</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
+ <action name="actionAddToPhraseBook">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Add to Phrase Book</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+T</string>
+ </property>
+ </action>
+ <action name="actionOpenAux">
+ <property name="text">
+ <string>Open Read-O&amp;nly...</string>
+ </property>
+ </action>
+ <action name="actionSaveAll">
+ <property name="text">
+ <string>&amp;Save All</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+S</string>
+ </property>
+ </action>
+ <action name="actionReleaseAll">
+ <property name="text">
+ <string>&amp;Release All</string>
+ </property>
+ </action>
+ <action name="actionClose">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ </action>
+ <action name="actionCloseAll">
+ <property name="text">
+ <string>&amp;Close All</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+W</string>
+ </property>
+ </action>
+ <action name="actionLengthVariants">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Length Variants</string>
+ </property>
+ </action>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/linguist/linguist/messageeditor.cpp b/src/linguist/linguist/messageeditor.cpp
new file mode 100644
index 000000000..3a705c1a7
--- /dev/null
+++ b/src/linguist/linguist/messageeditor.cpp
@@ -0,0 +1,880 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/* TRANSLATOR MessageEditor
+
+ This is the right panel of the main window.
+*/
+
+#include "messageeditor.h"
+#include "messageeditorwidgets.h"
+#include "simtexth.h"
+#include "phrasemodel.h"
+
+#include <QApplication>
+#include <QBoxLayout>
+#include <QClipboard>
+#include <QDebug>
+#include <QDockWidget>
+#include <QHeaderView>
+#include <QKeyEvent>
+#include <QMainWindow>
+#include <QPainter>
+#include <QTreeView>
+#include <QVBoxLayout>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef NEVER_TRUE
+// Allow translators to provide localized names for QLocale::languageToString
+// At least the own language should be translated ... This is a "hack" until
+// functionality is provided within Qt (see task 196275).
+static const char * language_strings[] =
+{
+ QT_TRANSLATE_NOOP("MessageEditor", "Russian"),
+ QT_TRANSLATE_NOOP("MessageEditor", "German"),
+ QT_TRANSLATE_NOOP("MessageEditor", "Japanese"),
+ QT_TRANSLATE_NOOP("MessageEditor", "French"),
+ QT_TRANSLATE_NOOP("MessageEditor", "Polish"),
+ QT_TRANSLATE_NOOP("MessageEditor", "Chinese")
+};
+#endif
+
+/*
+ MessageEditor class impl.
+
+ Handles layout of dock windows and the editor page.
+*/
+MessageEditor::MessageEditor(MultiDataModel *dataModel, QMainWindow *parent)
+ : QScrollArea(parent->centralWidget()),
+ m_dataModel(dataModel),
+ m_currentModel(-1),
+ m_currentNumerus(-1),
+ m_lengthVariants(false),
+ m_undoAvail(false),
+ m_redoAvail(false),
+ m_cutAvail(false),
+ m_copyAvail(false),
+ m_selectionHolder(0),
+ m_focusWidget(0)
+{
+ setObjectName(QLatin1String("scroll area"));
+
+ QPalette p;
+ p.setBrush(QPalette::Window, p.brush(QPalette::Active, QPalette::Base));
+ setPalette(p);
+
+ setupEditorPage();
+
+ // Signals
+ connect(qApp->clipboard(), SIGNAL(dataChanged()),
+ SLOT(clipboardChanged()));
+ connect(m_dataModel, SIGNAL(modelAppended()),
+ SLOT(messageModelAppended()));
+ connect(m_dataModel, SIGNAL(modelDeleted(int)),
+ SLOT(messageModelDeleted(int)));
+ connect(m_dataModel, SIGNAL(allModelsDeleted()),
+ SLOT(allModelsDeleted()));
+ connect(m_dataModel, SIGNAL(languageChanged(int)),
+ SLOT(setTargetLanguage(int)));
+
+ m_tabOrderTimer.setSingleShot(true);
+ connect(&m_tabOrderTimer, SIGNAL(timeout()), SLOT(reallyFixTabOrder()));
+
+ clipboardChanged();
+
+ setWhatsThis(tr("This whole panel allows you to view and edit "
+ "the translation of some source text."));
+ showNothing();
+}
+
+void MessageEditor::setupEditorPage()
+{
+ QFrame *editorPage = new QFrame;
+ editorPage->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
+
+ m_source = new FormWidget(tr("Source text"), false);
+ m_source->setHideWhenEmpty(true);
+ m_source->setWhatsThis(tr("This area shows the source text."));
+ connect(m_source, SIGNAL(selectionChanged(QTextEdit*)),
+ SLOT(selectionChanged(QTextEdit*)));
+
+ m_pluralSource = new FormWidget(tr("Source text (Plural)"), false);
+ m_pluralSource->setHideWhenEmpty(true);
+ m_pluralSource->setWhatsThis(tr("This area shows the plural form of the source text."));
+ connect(m_pluralSource, SIGNAL(selectionChanged(QTextEdit*)),
+ SLOT(selectionChanged(QTextEdit*)));
+
+ m_commentText = new FormWidget(tr("Developer comments"), false);
+ m_commentText->setHideWhenEmpty(true);
+ m_commentText->setObjectName(QLatin1String("comment/context view"));
+ m_commentText->setWhatsThis(tr("This area shows a comment that"
+ " may guide you, and the context in which the text"
+ " occurs.") );
+
+ QBoxLayout *subLayout = new QVBoxLayout;
+
+ subLayout->setMargin(5);
+ subLayout->addWidget(m_source);
+ subLayout->addWidget(m_pluralSource);
+ subLayout->addWidget(m_commentText);
+
+ m_layout = new QVBoxLayout;
+ m_layout->setSpacing(2);
+ m_layout->setMargin(2);
+ m_layout->addLayout(subLayout);
+ m_layout->addStretch(1);
+ editorPage->setLayout(m_layout);
+
+ setWidget(editorPage);
+ setWidgetResizable(true);
+}
+
+QPalette MessageEditor::paletteForModel(int model) const
+{
+ QBrush brush = m_dataModel->brushForModel(model);
+ QPalette pal;
+
+ if (m_dataModel->isModelWritable(model)) {
+ pal.setBrush(QPalette::Window, brush);
+ } else {
+ QPixmap pm(brush.texture().size());
+ pm.fill();
+ QPainter p(&pm);
+ p.fillRect(brush.texture().rect(), brush);
+ pal.setBrush(QPalette::Window, pm);
+ }
+ return pal;
+}
+
+void MessageEditor::messageModelAppended()
+{
+ int model = m_editors.size();
+ m_editors.append(MessageEditorData());
+ MessageEditorData &ed = m_editors.last();
+ ed.pluralEditMode = false;
+ ed.fontSize = font().pointSize();
+ ed.container = new QWidget;
+ if (model > 0) {
+ ed.container->setPalette(paletteForModel(model));
+ ed.container->setAutoFillBackground(true);
+ if (model == 1) {
+ m_editors[0].container->setPalette(paletteForModel(0));
+ m_editors[0].container->setAutoFillBackground(true);
+ }
+ }
+ bool writable = m_dataModel->isModelWritable(model);
+ ed.transCommentText = new FormWidget(QString(), true);
+ ed.transCommentText->setEditingEnabled(writable);
+ ed.transCommentText->setHideWhenEmpty(!writable);
+ ed.transCommentText->setWhatsThis(tr("Here you can enter comments for your own use."
+ " They have no effect on the translated applications.") );
+ ed.transCommentText->getEditor()->installEventFilter(this);
+ connect(ed.transCommentText, SIGNAL(selectionChanged(QTextEdit*)),
+ SLOT(selectionChanged(QTextEdit*)));
+ connect(ed.transCommentText, SIGNAL(textChanged(QTextEdit*)),
+ SLOT(emitTranslatorCommentChanged(QTextEdit*)));
+ connect(ed.transCommentText, SIGNAL(textChanged(QTextEdit*)), SLOT(resetHoverSelection()));
+ connect(ed.transCommentText, SIGNAL(cursorPositionChanged()), SLOT(resetHoverSelection()));
+ fixTabOrder();
+ QBoxLayout *box = new QVBoxLayout(ed.container);
+ box->setMargin(5);
+ box->addWidget(ed.transCommentText);
+ box->addSpacing(ed.transCommentText->getEditor()->fontMetrics().height() / 2);
+ m_layout->addWidget(ed.container);
+ setTargetLanguage(model);
+}
+
+void MessageEditor::allModelsDeleted()
+{
+ foreach (const MessageEditorData &med, m_editors)
+ med.container->deleteLater();
+ m_editors.clear();
+ m_currentModel = -1;
+ // Do not emit activeModelChanged() - the main window will refresh anyway
+ m_currentNumerus = -1;
+ showNothing();
+}
+
+void MessageEditor::messageModelDeleted(int model)
+{
+ m_editors[model].container->deleteLater();
+ m_editors.removeAt(model);
+ if (model <= m_currentModel) {
+ if (model < m_currentModel || m_currentModel == m_editors.size())
+ --m_currentModel;
+ // Do not emit activeModelChanged() - the main window will refresh anyway
+ if (m_currentModel >= 0) {
+ if (m_currentNumerus >= m_editors[m_currentModel].transTexts.size())
+ m_currentNumerus = m_editors[m_currentModel].transTexts.size() - 1;
+ activeEditor()->setFocus();
+ } else {
+ m_currentNumerus = -1;
+ }
+ }
+ if (m_editors.size() == 1) {
+ m_editors[0].container->setAutoFillBackground(false);
+ } else {
+ for (int i = model; i < m_editors.size(); ++i)
+ m_editors[i].container->setPalette(paletteForModel(i));
+ }
+}
+
+void MessageEditor::addPluralForm(int model, const QString &label, bool writable)
+{
+ FormMultiWidget *transEditor = new FormMultiWidget(label);
+ connect(transEditor, SIGNAL(editorCreated(QTextEdit*)), SLOT(editorCreated(QTextEdit*)));
+ transEditor->setEditingEnabled(writable);
+ transEditor->setHideWhenEmpty(!writable);
+ if (!m_editors[model].transTexts.isEmpty())
+ transEditor->setVisible(false);
+ transEditor->setMultiEnabled(m_lengthVariants);
+ static_cast<QBoxLayout *>(m_editors[model].container->layout())->insertWidget(
+ m_editors[model].transTexts.count(), transEditor);
+
+ connect(transEditor, SIGNAL(selectionChanged(QTextEdit*)),
+ SLOT(selectionChanged(QTextEdit*)));
+ connect(transEditor, SIGNAL(textChanged(QTextEdit*)),
+ SLOT(emitTranslationChanged(QTextEdit*)));
+ connect(transEditor, SIGNAL(textChanged(QTextEdit*)), SLOT(resetHoverSelection()));
+ connect(transEditor, SIGNAL(cursorPositionChanged()), SLOT(resetHoverSelection()));
+
+ m_editors[model].transTexts << transEditor;
+}
+
+void MessageEditor::editorCreated(QTextEdit *te)
+{
+ FormMultiWidget *snd = static_cast<FormMultiWidget *>(sender());
+ for (int model = 0; ; ++model) {
+ MessageEditorData med = m_editors.at(model);
+ if (med.transTexts.contains(snd)) {
+ QFont font;
+ font.setPointSize(static_cast<int>(med.fontSize));
+ te->setFont(font);
+
+ te->installEventFilter(this);
+
+ fixTabOrder();
+ return;
+ }
+ }
+}
+
+void MessageEditor::fixTabOrder()
+{
+ m_tabOrderTimer.start(0);
+}
+
+void MessageEditor::reallyFixTabOrder()
+{
+ QWidget *prev = this;
+ foreach (const MessageEditorData &med, m_editors) {
+ foreach (FormMultiWidget *fmw, med.transTexts)
+ foreach (QTextEdit *te, fmw->getEditors()) {
+ setTabOrder(prev, te);
+ prev = te;
+ }
+ QTextEdit *te = med.transCommentText->getEditor();
+ setTabOrder(prev, te);
+ prev = te;
+ }
+}
+
+/*! internal
+ Returns all translations for an item.
+ The number of translations is dependent on if we have a plural form or not.
+ If we don't have a plural form, then this should only contain one item.
+ Otherwise it will contain the number of numerus forms for the particular language.
+*/
+QStringList MessageEditor::translations(int model) const
+{
+ QStringList translations;
+ for (int i = 0; i < m_editors[model].transTexts.count() &&
+ m_editors[model].transTexts.at(i)->isVisible(); ++i)
+ translations << m_editors[model].transTexts[i]->getTranslation();
+ return translations;
+}
+
+static void clearSelection(QTextEdit *t)
+{
+ bool oldBlockState = t->blockSignals(true);
+ QTextCursor c = t->textCursor();
+ c.clearSelection();
+ t->setTextCursor(c);
+ t->blockSignals(oldBlockState);
+}
+
+void MessageEditor::selectionChanged(QTextEdit *te)
+{
+ if (te != m_selectionHolder) {
+ if (m_selectionHolder)
+ clearSelection(m_selectionHolder);
+ m_selectionHolder = (te->textCursor().hasSelection() ? te : 0);
+ updateCanCutCopy();
+ }
+}
+
+void MessageEditor::resetHoverSelection()
+{
+ if (m_selectionHolder &&
+ (m_selectionHolder == m_source->getEditor()
+ || m_selectionHolder == m_pluralSource->getEditor()))
+ resetSelection();
+}
+
+void MessageEditor::resetSelection()
+{
+ if (m_selectionHolder) {
+ clearSelection(m_selectionHolder);
+ m_selectionHolder = 0;
+ updateCanCutCopy();
+ }
+}
+
+void MessageEditor::activeModelAndNumerus(int *model, int *numerus) const
+{
+ for (int j = 0; j < m_editors.count(); ++j) {
+ for (int i = 0; i < m_editors[j].transTexts.count(); ++i)
+ foreach (QTextEdit *te, m_editors[j].transTexts[i]->getEditors())
+ if (m_focusWidget == te) {
+ *model = j;
+ *numerus = i;
+ return;
+ }
+ if (m_focusWidget == m_editors[j].transCommentText->getEditor()) {
+ *model = j;
+ *numerus = -1;
+ return;
+ }
+ }
+ *model = -1;
+ *numerus = -1;
+}
+
+QTextEdit *MessageEditor::activeTranslation() const
+{
+ if (m_currentNumerus < 0)
+ return 0;
+ const QList<FormatTextEdit *> &editors =
+ m_editors[m_currentModel].transTexts[m_currentNumerus]->getEditors();
+ foreach (QTextEdit *te, editors)
+ if (te->hasFocus())
+ return te;
+ return editors.first();
+}
+
+QTextEdit *MessageEditor::activeOr1stTranslation() const
+{
+ if (m_currentNumerus < 0) {
+ for (int i = 0; i < m_editors.size(); ++i)
+ if (m_editors[i].container->isVisible()
+ && !m_editors[i].transTexts.first()->getEditors().first()->isReadOnly())
+ return m_editors[i].transTexts.first()->getEditors().first();
+ return 0;
+ }
+ return activeTranslation();
+}
+
+QTextEdit *MessageEditor::activeTransComment() const
+{
+ if (m_currentModel < 0 || m_currentNumerus >= 0)
+ return 0;
+ return m_editors[m_currentModel].transCommentText->getEditor();
+}
+
+QTextEdit *MessageEditor::activeEditor() const
+{
+ if (QTextEdit *te = activeTransComment())
+ return te;
+ return activeTranslation();
+}
+
+QTextEdit *MessageEditor::activeOr1stEditor() const
+{
+ if (QTextEdit *te = activeTransComment())
+ return te;
+ return activeOr1stTranslation();
+}
+
+void MessageEditor::setTargetLanguage(int model)
+{
+ const QStringList &numerusForms = m_dataModel->model(model)->numerusForms();
+ const QString &langLocalized = m_dataModel->model(model)->localizedLanguage();
+ for (int i = 0; i < numerusForms.count(); ++i) {
+ const QString &label = tr("%1 translation (%2)").arg(langLocalized, numerusForms[i]);
+ if (!i)
+ m_editors[model].firstForm = label;
+ if (i >= m_editors[model].transTexts.count())
+ addPluralForm(model, label, m_dataModel->isModelWritable(model));
+ else
+ m_editors[model].transTexts[i]->setLabel(label);
+ m_editors[model].transTexts[i]->setVisible(!i || m_editors[model].pluralEditMode);
+ m_editors[model].transTexts[i]->setWhatsThis(
+ tr("This is where you can enter or modify"
+ " the translation of the above source text.") );
+ }
+ for (int j = m_editors[model].transTexts.count() - numerusForms.count(); j > 0; --j)
+ delete m_editors[model].transTexts.takeLast();
+ m_editors[model].invariantForm = tr("%1 translation").arg(langLocalized);
+ m_editors[model].transCommentText->setLabel(tr("%1 translator comments").arg(langLocalized));
+}
+
+MessageEditorData *MessageEditor::modelForWidget(const QObject *o)
+{
+ for (int j = 0; j < m_editors.count(); ++j) {
+ for (int i = 0; i < m_editors[j].transTexts.count(); ++i)
+ foreach (QTextEdit *te, m_editors[j].transTexts[i]->getEditors())
+ if (te == o)
+ return &m_editors[j];
+ if (m_editors[j].transCommentText->getEditor() == o)
+ return &m_editors[j];
+ }
+ return 0;
+}
+
+static bool applyFont(MessageEditorData *med)
+{
+ QFont font;
+ font.setPointSize(static_cast<int>(med->fontSize));
+ for (int i = 0; i < med->transTexts.count(); ++i)
+ foreach (QTextEdit *te, med->transTexts[i]->getEditors())
+ te->setFont(font);
+ med->transCommentText->getEditor()->setFont(font);
+ return true;
+}
+
+static bool incFont(MessageEditorData *med)
+{
+ if (!med || med->fontSize >= 32)
+ return true;
+ med->fontSize *= 1.2;
+ return applyFont(med);
+}
+
+static bool decFont(MessageEditorData *med)
+{
+ if (!med || med->fontSize <= 8)
+ return true;
+ med->fontSize /= 1.2;
+ return applyFont(med);
+}
+
+bool MessageEditor::eventFilter(QObject *o, QEvent *e)
+{
+ // handle copying from the source
+ if (e->type() == QEvent::ShortcutOverride) {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(e);
+
+ if (ke->modifiers() & Qt::ControlModifier) {
+ if (ke->key() == Qt::Key_C) {
+ if (m_source->getEditor()->textCursor().hasSelection()) {
+ m_source->getEditor()->copy();
+ return true;
+ }
+ if (m_pluralSource->getEditor()->textCursor().hasSelection()) {
+ m_pluralSource->getEditor()->copy();
+ return true;
+ }
+ } else if (ke->key() == Qt::Key_A) {
+ return true;
+ }
+ }
+ } else if (e->type() == QEvent::KeyPress) {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(e);
+ if (ke->modifiers() & Qt::ControlModifier) {
+ if (ke->key() == Qt::Key_Plus || ke->key() == Qt::Key_Equal)
+ return incFont(modelForWidget(o));
+ if (ke->key() == Qt::Key_Minus)
+ return decFont(modelForWidget(o));
+ } else {
+ // Ctrl-Tab is still passed through to the textedit and causes a tab to be inserted.
+ if (ke->key() == Qt::Key_Tab) {
+ focusNextChild();
+ return true;
+ }
+ }
+ } else if (e->type() == QEvent::Wheel) {
+ QWheelEvent *we = static_cast<QWheelEvent *>(e);
+ if (we->modifiers() & Qt::ControlModifier) {
+ if (we->delta() > 0)
+ return incFont(modelForWidget(o));
+ return decFont(modelForWidget(o));
+ }
+ } else if (e->type() == QEvent::FocusIn) {
+ QWidget *widget = static_cast<QWidget *>(o);
+ if (widget != m_focusWidget)
+ trackFocus(widget);
+ }
+
+ return QScrollArea::eventFilter(o, e);
+}
+
+void MessageEditor::grabFocus(QWidget *widget)
+{
+ if (widget != m_focusWidget) {
+ widget->setFocus();
+ trackFocus(widget);
+ }
+}
+
+void MessageEditor::trackFocus(QWidget *widget)
+{
+ m_focusWidget = widget;
+
+ int model, numerus;
+ activeModelAndNumerus(&model, &numerus);
+ if (model != m_currentModel || numerus != m_currentNumerus) {
+ resetSelection();
+ m_currentModel = model;
+ m_currentNumerus = numerus;
+ emit activeModelChanged(activeModel());
+ updateBeginFromSource();
+ updateUndoRedo();
+ updateCanPaste();
+ }
+}
+
+void MessageEditor::showNothing()
+{
+ m_source->clearTranslation();
+ m_pluralSource->clearTranslation();
+ m_commentText->clearTranslation();
+ for (int j = 0; j < m_editors.count(); ++j) {
+ setEditingEnabled(j, false);
+ foreach (FormMultiWidget *widget, m_editors[j].transTexts)
+ widget->clearTranslation();
+ m_editors[j].transCommentText->clearTranslation();
+ }
+ emit pasteAvailable(false);
+ updateBeginFromSource();
+ updateUndoRedo();
+}
+
+void MessageEditor::showMessage(const MultiDataIndex &index)
+{
+ m_currentIndex = index;
+
+ bool hadMsg = false;
+ for (int j = 0; j < m_editors.size(); ++j) {
+
+ MessageEditorData &ed = m_editors[j];
+
+ MessageItem *item = m_dataModel->messageItem(index, j);
+ if (!item) {
+ ed.container->hide();
+ continue;
+ }
+ ed.container->show();
+
+ if (!hadMsg) {
+
+ // Source text form
+ m_source->setTranslation(item->text());
+ m_pluralSource->setTranslation(item->pluralText());
+ // Use location from first non-obsolete message
+ if (!item->fileName().isEmpty()) {
+ QString toolTip = tr("'%1'\nLine: %2").arg(item->fileName(), QString::number(item->lineNumber()));
+ m_source->setToolTip(toolTip);
+ } else {
+ m_source->setToolTip(QLatin1String(""));
+ }
+
+ // Comment field
+ QString commentText = item->comment().simplified();
+
+ if (!item->extraComment().isEmpty()) {
+ if (!commentText.isEmpty())
+ commentText += QLatin1String("\n");
+ commentText += item->extraComment().simplified();
+ }
+
+ m_commentText->setTranslation(commentText);
+
+ hadMsg = true;
+ }
+
+ setEditingEnabled(j, m_dataModel->isModelWritable(j)
+ && item->message().type() != TranslatorMessage::Obsolete);
+
+ // Translation label
+ ed.pluralEditMode = item->translations().count() > 1;
+ ed.transTexts.first()->setLabel(ed.pluralEditMode ? ed.firstForm : ed.invariantForm);
+
+ // Translation forms
+ if (item->text().isEmpty() && !item->context().isEmpty()) {
+ for (int i = 0; i < ed.transTexts.size(); ++i)
+ ed.transTexts.at(i)->setVisible(false);
+ } else {
+ QStringList normalizedTranslations =
+ m_dataModel->model(j)->normalizedTranslations(*item);
+ for (int i = 0; i < ed.transTexts.size(); ++i) {
+ bool shouldShow = (i < normalizedTranslations.count());
+ if (shouldShow)
+ setTranslation(j, normalizedTranslations.at(i), i);
+ else
+ setTranslation(j, QString(), i);
+ ed.transTexts.at(i)->setVisible(i == 0 || shouldShow);
+ }
+ }
+
+ ed.transCommentText->setTranslation(item->translatorComment().trimmed(), false);
+ }
+
+ updateUndoRedo();
+}
+
+void MessageEditor::setTranslation(int model, const QString &translation, int numerus)
+{
+ MessageEditorData &ed = m_editors[model];
+ if (numerus >= ed.transTexts.count())
+ numerus = 0;
+ FormMultiWidget *transForm = ed.transTexts[numerus];
+ transForm->setTranslation(translation, false);
+
+ updateBeginFromSource();
+}
+
+void MessageEditor::setTranslation(int latestModel, const QString &translation)
+{
+ int numerus;
+ if (m_currentNumerus < 0) {
+ numerus = 0;
+ } else {
+ latestModel = m_currentModel;
+ numerus = m_currentNumerus;
+ }
+ FormMultiWidget *transForm = m_editors[latestModel].transTexts[numerus];
+ transForm->getEditors().first()->setFocus();
+ transForm->setTranslation(translation, true);
+
+ updateBeginFromSource();
+}
+
+void MessageEditor::setEditingEnabled(int model, bool enabled)
+{
+ MessageEditorData &ed = m_editors[model];
+ foreach (FormMultiWidget *widget, ed.transTexts)
+ widget->setEditingEnabled(enabled);
+ ed.transCommentText->setEditingEnabled(enabled);
+
+ updateCanPaste();
+}
+
+void MessageEditor::setLengthVariants(bool on)
+{
+ m_lengthVariants = on;
+ foreach (const MessageEditorData &ed, m_editors)
+ foreach (FormMultiWidget *widget, ed.transTexts)
+ widget->setMultiEnabled(on);
+}
+
+void MessageEditor::undo()
+{
+ activeEditor()->document()->undo();
+}
+
+void MessageEditor::redo()
+{
+ activeEditor()->document()->redo();
+}
+
+void MessageEditor::updateUndoRedo()
+{
+ bool newUndoAvail = false;
+ bool newRedoAvail = false;
+ if (QTextEdit *te = activeEditor()) {
+ QTextDocument *doc = te->document();
+ newUndoAvail = doc->isUndoAvailable();
+ newRedoAvail = doc->isRedoAvailable();
+ }
+
+ if (newUndoAvail != m_undoAvail) {
+ m_undoAvail = newUndoAvail;
+ emit undoAvailable(newUndoAvail);
+ }
+
+ if (newRedoAvail != m_redoAvail) {
+ m_redoAvail = newRedoAvail;
+ emit redoAvailable(newRedoAvail);
+ }
+}
+
+void MessageEditor::cut()
+{
+ m_selectionHolder->cut();
+}
+
+void MessageEditor::copy()
+{
+ m_selectionHolder->copy();
+}
+
+void MessageEditor::updateCanCutCopy()
+{
+ bool newCopyState = false;
+ bool newCutState = false;
+
+ if (m_selectionHolder) {
+ newCopyState = true;
+ newCutState = !m_selectionHolder->isReadOnly();
+ }
+
+ if (newCopyState != m_copyAvail) {
+ m_copyAvail = newCopyState;
+ emit copyAvailable(m_copyAvail);
+ }
+
+ if (newCutState != m_cutAvail) {
+ m_cutAvail = newCutState;
+ emit cutAvailable(m_cutAvail);
+ }
+}
+
+void MessageEditor::paste()
+{
+ activeEditor()->paste();
+}
+
+void MessageEditor::updateCanPaste()
+{
+ QTextEdit *te;
+ emit pasteAvailable(!m_clipboardEmpty
+ && (te = activeEditor()) && !te->isReadOnly());
+}
+
+void MessageEditor::clipboardChanged()
+{
+ // this is expensive, so move it out of the common path in updateCanPaste
+ m_clipboardEmpty = qApp->clipboard()->text().isNull();
+ updateCanPaste();
+}
+
+void MessageEditor::selectAll()
+{
+ // make sure we don't select the selection of a translator textedit,
+ // if we really want the source text editor to be selected.
+ QTextEdit *te;
+ if ((te = m_source->getEditor())->underMouse()
+ || (te = m_pluralSource->getEditor())->underMouse()
+ || ((te = activeEditor()) && te->hasFocus()))
+ te->selectAll();
+}
+
+void MessageEditor::emitTranslationChanged(QTextEdit *widget)
+{
+ grabFocus(widget); // DND proofness
+ updateBeginFromSource();
+ updateUndoRedo();
+ emit translationChanged(translations(m_currentModel));
+}
+
+void MessageEditor::emitTranslatorCommentChanged(QTextEdit *widget)
+{
+ grabFocus(widget); // DND proofness
+ updateUndoRedo();
+ emit translatorCommentChanged(m_editors[m_currentModel].transCommentText->getTranslation());
+}
+
+void MessageEditor::updateBeginFromSource()
+{
+ bool overwrite = false;
+ if (QTextEdit *activeEditor = activeTranslation())
+ overwrite = !activeEditor->isReadOnly()
+ && activeEditor->toPlainText().trimmed().isEmpty();
+ emit beginFromSourceAvailable(overwrite);
+}
+
+void MessageEditor::beginFromSource()
+{
+ MessageItem *item = m_dataModel->messageItem(m_currentIndex, m_currentModel);
+ setTranslation(m_currentModel,
+ m_currentNumerus > 0 && !item->pluralText().isEmpty() ?
+ item->pluralText() : item->text());
+}
+
+void MessageEditor::setEditorFocus()
+{
+ if (!widget()->hasFocus())
+ if (QTextEdit *activeEditor = activeOr1stEditor())
+ activeEditor->setFocus();
+}
+
+void MessageEditor::setEditorFocus(int model)
+{
+ if (m_currentModel != model) {
+ if (model < 0) {
+ resetSelection();
+ m_currentNumerus = -1;
+ m_currentModel = -1;
+ m_focusWidget = 0;
+ emit activeModelChanged(activeModel());
+ updateBeginFromSource();
+ updateUndoRedo();
+ updateCanPaste();
+ } else {
+ m_editors[model].transTexts.first()->getEditors().first()->setFocus();
+ }
+ }
+}
+
+bool MessageEditor::focusNextUnfinished(int start)
+{
+ for (int j = start; j < m_editors.count(); ++j)
+ if (m_dataModel->isModelWritable(j))
+ if (MessageItem *item = m_dataModel->messageItem(m_currentIndex, j))
+ if (item->type() == TranslatorMessage::Unfinished) {
+ m_editors[j].transTexts.first()->getEditors().first()->setFocus();
+ return true;
+ }
+ return false;
+}
+
+void MessageEditor::setUnfinishedEditorFocus()
+{
+ focusNextUnfinished(0);
+}
+
+bool MessageEditor::focusNextUnfinished()
+{
+ return focusNextUnfinished(m_currentModel + 1);
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/messageeditor.h b/src/linguist/linguist/messageeditor.h
new file mode 100644
index 000000000..875ef3303
--- /dev/null
+++ b/src/linguist/linguist/messageeditor.h
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MESSAGEEDITOR_H
+#define MESSAGEEDITOR_H
+
+#include "messagemodel.h"
+
+#include <QtCore/QLocale>
+#include <QtCore/QTimer>
+
+#include <QtGui/QFrame>
+#include <QtGui/QScrollArea>
+
+QT_BEGIN_NAMESPACE
+
+class QBoxLayout;
+class QMainWindow;
+class QTextEdit;
+
+class MessageEditor;
+class FormatTextEdit;
+class FormWidget;
+class FormMultiWidget;
+
+struct MessageEditorData {
+ QWidget *container;
+ FormWidget *transCommentText;
+ QList<FormMultiWidget *> transTexts;
+ QString invariantForm;
+ QString firstForm;
+ qreal fontSize;
+ bool pluralEditMode;
+};
+
+class MessageEditor : public QScrollArea
+{
+ Q_OBJECT
+
+public:
+ MessageEditor(MultiDataModel *dataModel, QMainWindow *parent = 0);
+
+ void showNothing();
+ void showMessage(const MultiDataIndex &index);
+ void setNumerusForms(int model, const QStringList &numerusForms);
+ bool eventFilter(QObject *, QEvent *);
+ void setTranslation(int model, const QString &translation, int numerus);
+ int activeModel() const { return (m_editors.count() != 1) ? m_currentModel : 0; }
+ void setEditorFocus(int model);
+ void setUnfinishedEditorFocus();
+ bool focusNextUnfinished();
+
+signals:
+ void translationChanged(const QStringList &translations);
+ void translatorCommentChanged(const QString &comment);
+ void activeModelChanged(int model);
+
+ void undoAvailable(bool avail);
+ void redoAvailable(bool avail);
+ void cutAvailable(bool avail);
+ void copyAvailable(bool avail);
+ void pasteAvailable(bool avail);
+ void beginFromSourceAvailable(bool enable);
+
+public slots:
+ void undo();
+ void redo();
+ void cut();
+ void copy();
+ void paste();
+ void selectAll();
+ void beginFromSource();
+ void setEditorFocus();
+ void setTranslation(int latestModel, const QString &translation);
+ void setLengthVariants(bool on);
+
+private slots:
+ void editorCreated(QTextEdit *);
+ void selectionChanged(QTextEdit *);
+ void resetHoverSelection();
+ void emitTranslationChanged(QTextEdit *);
+ void emitTranslatorCommentChanged(QTextEdit *);
+ void updateCanPaste();
+ void clipboardChanged();
+ void messageModelAppended();
+ void messageModelDeleted(int model);
+ void allModelsDeleted();
+ void setTargetLanguage(int model);
+ void reallyFixTabOrder();
+
+private:
+ void setupEditorPage();
+ void setEditingEnabled(int model, bool enabled);
+ bool focusNextUnfinished(int start);
+ void resetSelection();
+ void grabFocus(QWidget *widget);
+ void trackFocus(QWidget *widget);
+ void activeModelAndNumerus(int *model, int *numerus) const;
+ QTextEdit *activeTranslation() const;
+ QTextEdit *activeOr1stTranslation() const;
+ QTextEdit *activeTransComment() const;
+ QTextEdit *activeEditor() const;
+ QTextEdit *activeOr1stEditor() const;
+ MessageEditorData *modelForWidget(const QObject *o);
+ int activeTranslationNumerus() const;
+ QStringList translations(int model) const;
+ void updateBeginFromSource();
+ void updateUndoRedo();
+ void updateCanCutCopy();
+ void addPluralForm(int model, const QString &label, bool writable);
+ void fixTabOrder();
+ QPalette paletteForModel(int model) const;
+
+ MultiDataModel *m_dataModel;
+
+ MultiDataIndex m_currentIndex;
+ int m_currentModel;
+ int m_currentNumerus;
+
+ bool m_lengthVariants;
+
+ bool m_undoAvail;
+ bool m_redoAvail;
+ bool m_cutAvail;
+ bool m_copyAvail;
+
+ bool m_clipboardEmpty;
+
+ QTextEdit *m_selectionHolder;
+ QWidget *m_focusWidget;
+ QBoxLayout *m_layout;
+ FormWidget *m_source;
+ FormWidget *m_pluralSource;
+ FormWidget *m_commentText;
+ QList<MessageEditorData> m_editors;
+
+ QTimer m_tabOrderTimer;
+};
+
+QT_END_NAMESPACE
+
+#endif // MESSAGEEDITOR_H
diff --git a/src/linguist/linguist/messageeditorwidgets.cpp b/src/linguist/linguist/messageeditorwidgets.cpp
new file mode 100644
index 000000000..f55c336a9
--- /dev/null
+++ b/src/linguist/linguist/messageeditorwidgets.cpp
@@ -0,0 +1,456 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "messageeditorwidgets.h"
+#include "messagehighlighter.h"
+
+#include <translator.h>
+
+#include <QAbstractTextDocumentLayout>
+#include <QAction>
+#include <QApplication>
+#include <QClipboard>
+#include <QDebug>
+#include <QLayout>
+#include <QMenu>
+#include <QMessageBox>
+#include <QPainter>
+#include <QScrollArea>
+#include <QTextBlock>
+#include <QTextDocumentFragment>
+#include <QToolButton>
+#include <QVBoxLayout>
+
+QT_BEGIN_NAMESPACE
+
+ExpandingTextEdit::ExpandingTextEdit(QWidget *parent)
+ : QTextEdit(parent)
+{
+ setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding));
+
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
+ QAbstractTextDocumentLayout *docLayout = document()->documentLayout();
+ connect(docLayout, SIGNAL(documentSizeChanged(QSizeF)), SLOT(updateHeight(QSizeF)));
+ connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(reallyEnsureCursorVisible()));
+
+ m_minimumHeight = qRound(docLayout->documentSize().height()) + frameWidth() * 2;
+}
+
+void ExpandingTextEdit::updateHeight(const QSizeF &documentSize)
+{
+ m_minimumHeight = qRound(documentSize.height()) + frameWidth() * 2;
+ updateGeometry();
+}
+
+QSize ExpandingTextEdit::sizeHint() const
+{
+ return QSize(100, m_minimumHeight);
+}
+
+QSize ExpandingTextEdit::minimumSizeHint() const
+{
+ return QSize(100, m_minimumHeight);
+}
+
+void ExpandingTextEdit::reallyEnsureCursorVisible()
+{
+ QObject *ancestor = parent();
+ while (ancestor) {
+ QScrollArea *scrollArea = qobject_cast<QScrollArea*>(ancestor);
+ if (scrollArea &&
+ (scrollArea->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff &&
+ scrollArea->horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOff)) {
+ const QRect &r = cursorRect();
+ const QPoint &c = mapTo(scrollArea->widget(), r.center());
+ scrollArea->ensureVisible(c.x(), c.y());
+ break;
+ }
+ ancestor = ancestor->parent();
+ }
+}
+
+FormatTextEdit::FormatTextEdit(QWidget *parent)
+ : ExpandingTextEdit(parent)
+{
+ setLineWrapMode(QTextEdit::WidgetWidth);
+ setAcceptRichText(false);
+ QTextOption option = document()->defaultTextOption();
+ option.setFlags(option.flags()
+ | QTextOption::ShowLineAndParagraphSeparators
+ | QTextOption::ShowTabsAndSpaces);
+ document()->setDefaultTextOption(option);
+
+ // Do not set different background if disabled
+ QPalette p = palette();
+ p.setColor(QPalette::Disabled, QPalette::Base, p.color(QPalette::Active, QPalette::Base));
+ setPalette(p);
+
+ setEditable(true);
+
+ m_highlighter = new MessageHighlighter(this);
+}
+
+void FormatTextEdit::setEditable(bool editable)
+{
+ // save default frame style
+ static int framed = frameStyle();
+ static Qt::FocusPolicy defaultFocus = focusPolicy();
+
+ if (editable) {
+ setFrameStyle(framed);
+ setFocusPolicy(defaultFocus);
+ } else {
+ setFrameStyle(QFrame::NoFrame | QFrame::Plain);
+ setFocusPolicy(Qt::NoFocus);
+ }
+
+ setReadOnly(!editable);
+}
+
+void FormatTextEdit::setPlainText(const QString &text, bool userAction)
+{
+ if (!userAction) {
+ // Prevent contentsChanged signal
+ bool oldBlockState = blockSignals(true);
+ document()->setUndoRedoEnabled(false);
+ ExpandingTextEdit::setPlainText(text);
+ // highlighter is out of sync because of blocked signals
+ m_highlighter->rehighlight();
+ document()->setUndoRedoEnabled(true);
+ blockSignals(oldBlockState);
+ } else {
+ ExpandingTextEdit::setPlainText(text);
+ }
+}
+
+FormWidget::FormWidget(const QString &label, bool isEditable, QWidget *parent)
+ : QWidget(parent),
+ m_hideWhenEmpty(false)
+{
+ QVBoxLayout *layout = new QVBoxLayout;
+ layout->setMargin(0);
+
+ m_label = new QLabel(this);
+ QFont fnt;
+ fnt.setBold(true);
+ m_label->setFont(fnt);
+ m_label->setText(label);
+ layout->addWidget(m_label);
+
+ m_editor = new FormatTextEdit(this);
+ m_editor->setEditable(isEditable);
+ //m_textEdit->setWhatsThis(tr("This area shows text from an auxillary translation."));
+ layout->addWidget(m_editor);
+
+ setLayout(layout);
+
+ connect(m_editor, SIGNAL(textChanged()), SLOT(slotTextChanged()));
+ connect(m_editor, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged()));
+ connect(m_editor, SIGNAL(cursorPositionChanged()), SIGNAL(cursorPositionChanged()));
+}
+
+void FormWidget::slotTextChanged()
+{
+ emit textChanged(m_editor);
+}
+
+void FormWidget::slotSelectionChanged()
+{
+ emit selectionChanged(m_editor);
+}
+
+void FormWidget::setTranslation(const QString &text, bool userAction)
+{
+ m_editor->setPlainText(text, userAction);
+ if (m_hideWhenEmpty)
+ setHidden(text.isEmpty());
+}
+
+void FormWidget::setEditingEnabled(bool enable)
+{
+ // Use read-only state so that the text can still be copied
+ m_editor->setReadOnly(!enable);
+ m_label->setEnabled(enable);
+}
+
+
+class ButtonWrapper : public QWidget
+{
+ // no Q_OBJECT: no need to, and don't want the useless moc file
+
+public:
+ ButtonWrapper(QWidget *wrapee, QWidget *relator) : m_wrapee(wrapee)
+ {
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored);
+ QBoxLayout *box = new QVBoxLayout;
+ box->setMargin(0);
+ setLayout(box);
+ box->addWidget(wrapee, 0, Qt::AlignBottom);
+ if (relator)
+ relator->installEventFilter(this);
+ }
+
+protected:
+ virtual bool eventFilter(QObject *object, QEvent *event)
+ {
+ if (event->type() == QEvent::Resize) {
+ QWidget *relator = static_cast<QWidget *>(object);
+ setFixedHeight((relator->height() + layout()->spacing() + m_wrapee->height()) / 2);
+ }
+ return false;
+ }
+
+private:
+ QWidget *m_wrapee;
+};
+
+FormMultiWidget::FormMultiWidget(const QString &label, QWidget *parent)
+ : QWidget(parent),
+ m_hideWhenEmpty(false),
+ m_multiEnabled(false),
+ m_plusIcon(QIcon(QLatin1String(":/images/plus.png"))), // make static
+ m_minusIcon(QIcon(QLatin1String(":/images/minus.png")))
+{
+ m_label = new QLabel(this);
+ QFont fnt;
+ fnt.setBold(true);
+ m_label->setFont(fnt);
+ m_label->setText(label);
+
+ m_plusButtons.append(
+ new ButtonWrapper(makeButton(m_plusIcon, SLOT(plusButtonClicked())), 0));
+}
+
+QAbstractButton *FormMultiWidget::makeButton(const QIcon &icon, const char *slot)
+{
+ QAbstractButton *btn = new QToolButton(this);
+ btn->setIcon(icon);
+ btn->setFixedSize(icon.availableSizes().first() /* + something */);
+ btn->setFocusPolicy(Qt::NoFocus);
+ connect(btn, SIGNAL(clicked()), slot);
+ return btn;
+}
+
+void FormMultiWidget::addEditor(int idx)
+{
+ FormatTextEdit *editor = new FormatTextEdit(this);
+ m_editors.insert(idx, editor);
+
+ m_minusButtons.insert(idx, makeButton(m_minusIcon, SLOT(minusButtonClicked())));
+ m_plusButtons.insert(idx + 1,
+ new ButtonWrapper(makeButton(m_plusIcon, SLOT(plusButtonClicked())), editor));
+
+ connect(editor, SIGNAL(textChanged()), SLOT(slotTextChanged()));
+ connect(editor, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged()));
+ connect(editor, SIGNAL(cursorPositionChanged()), SIGNAL(cursorPositionChanged()));
+ editor->installEventFilter(this);
+
+ emit editorCreated(editor);
+}
+
+bool FormMultiWidget::eventFilter(QObject *watched, QEvent *event)
+{
+ int i = 0;
+ while (m_editors.at(i) != watched)
+ if (++i >= m_editors.count()) // Happens when deleting an editor
+ return false;
+ if (event->type() == QEvent::FocusOut) {
+ m_minusButtons.at(i)->setToolTip(QString());
+ m_plusButtons.at(i)->setToolTip(QString());
+ m_plusButtons.at(i + 1)->setToolTip(QString());
+ } else if (event->type() == QEvent::FocusIn) {
+ m_minusButtons.at(i)->setToolTip(/*: translate, but don't change */ tr("Alt+Delete"));
+ m_plusButtons.at(i)->setToolTip(/*: translate, but don't change */ tr("Shift+Alt+Insert"));
+ m_plusButtons.at(i + 1)->setToolTip(/*: translate, but don't change */ tr("Alt+Insert"));
+ } else if (event->type() == QEvent::KeyPress) {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(event);
+ if (ke->modifiers() & Qt::AltModifier) {
+ if (ke->key() == Qt::Key_Delete) {
+ deleteEditor(i);
+ return true;
+ } else if (ke->key() == Qt::Key_Insert) {
+ if (!(ke->modifiers() & Qt::ShiftModifier))
+ ++i;
+ insertEditor(i);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void FormMultiWidget::updateLayout()
+{
+ delete layout();
+
+ QGridLayout *layout = new QGridLayout;
+ layout->setMargin(0);
+ setLayout(layout);
+
+ bool variants = m_multiEnabled && m_label->isEnabled();
+
+ layout->addWidget(m_label, 0, 0, 1, variants ? 3 : 1);
+
+ for (int i = 0; i < m_plusButtons.count(); ++i) {
+ if (variants)
+ layout->addWidget(m_plusButtons.at(i), 1 + i * 2, 0, 2, 1, Qt::AlignTop);
+ m_plusButtons.at(i)->setVisible(variants);
+ }
+ for (int j = 0; j < m_minusButtons.count(); ++j) {
+ if (variants)
+ layout->addWidget(m_minusButtons.at(j), 2 + j * 2, 2, 2, 1, Qt::AlignVCenter);
+ m_minusButtons.at(j)->setVisible(variants);
+ }
+ for (int k = 0; k < m_editors.count(); ++k)
+ layout->addWidget(m_editors.at(k), 2 + k * 2, variants ? 1 : 0, 2, 1, Qt::AlignVCenter);
+
+ updateGeometry();
+}
+
+void FormMultiWidget::slotTextChanged()
+{
+ emit textChanged(static_cast<QTextEdit *>(sender()));
+}
+
+void FormMultiWidget::slotSelectionChanged()
+{
+ emit selectionChanged(static_cast<QTextEdit *>(sender()));
+}
+
+void FormMultiWidget::setTranslation(const QString &text, bool userAction)
+{
+ QStringList texts = text.split(QChar(Translator::BinaryVariantSeparator), QString::KeepEmptyParts);
+
+ while (m_editors.count() > texts.count()) {
+ delete m_minusButtons.takeLast();
+ delete m_plusButtons.takeLast();
+ delete m_editors.takeLast();
+ }
+ while (m_editors.count() < texts.count())
+ addEditor(m_editors.count());
+ updateLayout();
+
+ for (int i = 0; i < texts.count(); ++i)
+ // XXX this will emit n textChanged signals
+ m_editors.at(i)->setPlainText(texts.at(i), userAction);
+
+ if (m_hideWhenEmpty)
+ setHidden(text.isEmpty());
+}
+
+QString FormMultiWidget::getTranslation() const
+{
+ QString ret;
+ for (int i = 0; i < m_editors.count(); ++i) {
+ if (i)
+ ret += QChar(Translator::BinaryVariantSeparator);
+ ret += m_editors.at(i)->toPlainText();
+ }
+ return ret;
+}
+
+void FormMultiWidget::setEditingEnabled(bool enable)
+{
+ // Use read-only state so that the text can still be copied
+ for (int i = 0; i < m_editors.count(); ++i)
+ m_editors.at(i)->setReadOnly(!enable);
+ m_label->setEnabled(enable);
+ if (m_multiEnabled)
+ updateLayout();
+}
+
+void FormMultiWidget::setMultiEnabled(bool enable)
+{
+ m_multiEnabled = enable;
+ if (m_label->isEnabled())
+ updateLayout();
+}
+
+void FormMultiWidget::minusButtonClicked()
+{
+ int i = 0;
+ while (m_minusButtons.at(i) != sender())
+ ++i;
+ deleteEditor(i);
+}
+
+void FormMultiWidget::plusButtonClicked()
+{
+ QWidget *btn = static_cast<QAbstractButton *>(sender())->parentWidget();
+ int i = 0;
+ while (m_plusButtons.at(i) != btn)
+ ++i;
+ insertEditor(i);
+}
+
+void FormMultiWidget::deleteEditor(int idx)
+{
+ if (m_editors.count() == 1) {
+ // Don't just clear(), so the undo history is not lost
+ QTextCursor c = m_editors.first()->textCursor();
+ c.select(QTextCursor::Document);
+ c.removeSelectedText();
+ } else {
+ if (!m_editors.at(idx)->toPlainText().isEmpty()) {
+ if (QMessageBox::question(topLevelWidget(), tr("Confirmation - Qt Linguist"),
+ tr("Delete non-empty length variant?"),
+ QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes)
+ != QMessageBox::Yes)
+ return;
+ }
+ delete m_editors.takeAt(idx);
+ delete m_minusButtons.takeAt(idx);
+ delete m_plusButtons.takeAt(idx + 1);
+ updateLayout();
+ emit textChanged(m_editors.at((m_editors.count() == idx) ? idx - 1 : idx));
+ }
+}
+
+void FormMultiWidget::insertEditor(int idx)
+{
+ addEditor(idx);
+ updateLayout();
+ emit textChanged(m_editors.at(idx));
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/messageeditorwidgets.h b/src/linguist/linguist/messageeditorwidgets.h
new file mode 100644
index 000000000..482d9b9db
--- /dev/null
+++ b/src/linguist/linguist/messageeditorwidgets.h
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MESSAGEEDITORWIDGETS_H
+#define MESSAGEEDITORWIDGETS_H
+
+#include <QIcon>
+#include <QImage>
+#include <QLabel>
+#include <QMap>
+#include <QTextEdit>
+#include <QUrl>
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractButton;
+class QAction;
+class QContextMenuEvent;
+class QKeyEvent;
+class QMenu;
+class QSizeF;
+class QString;
+class QVariant;
+
+class MessageHighlighter;
+
+/*
+ Automatically adapt height to document contents
+ */
+class ExpandingTextEdit : public QTextEdit
+{
+ Q_OBJECT
+
+public:
+ ExpandingTextEdit(QWidget *parent = 0);
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+private slots:
+ void updateHeight(const QSizeF &documentSize);
+ void reallyEnsureCursorVisible();
+
+private:
+ int m_minimumHeight;
+};
+
+/*
+ Format markup & control characters
+*/
+class FormatTextEdit : public ExpandingTextEdit
+{
+ Q_OBJECT
+public:
+ FormatTextEdit(QWidget *parent = 0);
+ void setEditable(bool editable);
+
+public slots:
+ void setPlainText(const QString & text, bool userAction);
+
+private:
+ MessageHighlighter *m_highlighter;
+};
+
+/*
+ Displays text field & associated label
+*/
+class FormWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ FormWidget(const QString &label, bool isEditable, QWidget *parent = 0);
+ void setLabel(const QString &label) { m_label->setText(label); }
+ void setTranslation(const QString &text, bool userAction = false);
+ void clearTranslation() { setTranslation(QString(), false); }
+ QString getTranslation() { return m_editor->toPlainText(); }
+ void setEditingEnabled(bool enable);
+ void setHideWhenEmpty(bool optional) { m_hideWhenEmpty = optional; }
+ FormatTextEdit *getEditor() { return m_editor; }
+
+signals:
+ void textChanged(QTextEdit *);
+ void selectionChanged(QTextEdit *);
+ void cursorPositionChanged();
+
+private slots:
+ void slotSelectionChanged();
+ void slotTextChanged();
+
+private:
+ QLabel *m_label;
+ FormatTextEdit *m_editor;
+ bool m_hideWhenEmpty;
+};
+
+/*
+ Displays text fields & associated label
+*/
+class FormMultiWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ FormMultiWidget(const QString &label, QWidget *parent = 0);
+ void setLabel(const QString &label) { m_label->setText(label); }
+ void setTranslation(const QString &text, bool userAction = false);
+ void clearTranslation() { setTranslation(QString(), false); }
+ QString getTranslation() const;
+ void setEditingEnabled(bool enable);
+ void setMultiEnabled(bool enable);
+ void setHideWhenEmpty(bool optional) { m_hideWhenEmpty = optional; }
+ const QList<FormatTextEdit *> &getEditors() const { return m_editors; }
+
+signals:
+ void editorCreated(QTextEdit *);
+ void textChanged(QTextEdit *);
+ void selectionChanged(QTextEdit *);
+ void cursorPositionChanged();
+
+protected:
+ bool eventFilter(QObject *watched, QEvent *event);
+
+private slots:
+ void slotTextChanged();
+ void slotSelectionChanged();
+ void minusButtonClicked();
+ void plusButtonClicked();
+
+private:
+ void addEditor(int idx);
+ void updateLayout();
+ QAbstractButton *makeButton(const QIcon &icon, const char *slot);
+ void insertEditor(int idx);
+ void deleteEditor(int idx);
+
+ QLabel *m_label;
+ QList<FormatTextEdit *> m_editors;
+ QList<QWidget *> m_plusButtons;
+ QList<QAbstractButton *> m_minusButtons;
+ bool m_hideWhenEmpty;
+ bool m_multiEnabled;
+ QIcon m_plusIcon, m_minusIcon;
+};
+
+QT_END_NAMESPACE
+
+#endif // MESSAGEEDITORWIDGETS_H
diff --git a/src/linguist/linguist/messagehighlighter.cpp b/src/linguist/linguist/messagehighlighter.cpp
new file mode 100644
index 000000000..bef2d6221
--- /dev/null
+++ b/src/linguist/linguist/messagehighlighter.cpp
@@ -0,0 +1,210 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "messagehighlighter.h"
+
+#include <QtCore/QTextStream>
+
+QT_BEGIN_NAMESPACE
+
+MessageHighlighter::MessageHighlighter(QTextEdit *textEdit)
+ : QSyntaxHighlighter(textEdit)
+{
+ QTextCharFormat entityFormat;
+ entityFormat.setForeground(Qt::red);
+ m_formats[Entity] = entityFormat;
+
+ QTextCharFormat tagFormat;
+ tagFormat.setForeground(Qt::darkMagenta);
+ m_formats[Tag] = tagFormat;
+
+ QTextCharFormat commentFormat;
+ commentFormat.setForeground(Qt::gray);
+ commentFormat.setFontItalic(true);
+ m_formats[Comment] = commentFormat;
+
+ QTextCharFormat attributeFormat;
+ attributeFormat.setForeground(Qt::black);
+ attributeFormat.setFontItalic(true);
+ m_formats[Attribute] = attributeFormat;
+
+ QTextCharFormat valueFormat;
+ valueFormat.setForeground(Qt::blue);
+ m_formats[Value] = valueFormat;
+
+ QTextCharFormat acceleratorFormat;
+ acceleratorFormat.setFontUnderline(true);
+ m_formats[Accelerator] = acceleratorFormat;
+
+ QTextCharFormat variableFormat;
+ variableFormat.setForeground(Qt::blue);
+ m_formats[Variable] = variableFormat;
+
+ rehighlight();
+}
+
+void MessageHighlighter::highlightBlock(const QString &text)
+{
+ static const QLatin1Char tab = QLatin1Char('\t');
+ static const QLatin1Char space = QLatin1Char(' ');
+ static const QLatin1Char amp = QLatin1Char('&');
+ static const QLatin1Char endTag = QLatin1Char('>');
+ static const QLatin1Char quot = QLatin1Char('"');
+ static const QLatin1Char apos = QLatin1Char('\'');
+ static const QLatin1Char semicolon = QLatin1Char(';');
+ static const QLatin1Char equals = QLatin1Char('=');
+ static const QLatin1Char percent = QLatin1Char('%');
+ static const QLatin1String startComment = QLatin1String("<!--");
+ static const QLatin1String endComment = QLatin1String("-->");
+ static const QLatin1String endElement = QLatin1String("/>");
+
+ int state = previousBlockState();
+ int len = text.length();
+ int start = 0;
+ int pos = 0;
+
+ while (pos < len) {
+ switch (state) {
+ case NormalState:
+ default:
+ while (pos < len) {
+ QChar ch = text.at(pos);
+ if (ch == QLatin1Char('<')) {
+ if (text.mid(pos, 4) == startComment) {
+ state = InComment;
+ } else {
+ state = InTag;
+ start = pos;
+ while (pos < len && text.at(pos) != space
+ && text.at(pos) != endTag
+ && text.at(pos) != tab
+ && text.mid(pos, 2) != endElement)
+ ++pos;
+ if (text.mid(pos, 2) == endElement)
+ ++pos;
+ setFormat(start, pos - start,
+ m_formats[Tag]);
+ break;
+ }
+ break;
+ } else if (ch == amp && pos + 1 < len) {
+ // Default is Accelerator
+ if (text.at(pos + 1).isLetterOrNumber())
+ setFormat(pos + 1, 1, m_formats[Accelerator]);
+
+ // When a semicolon follows assume an Entity
+ start = pos;
+ ch = text.at(++pos);
+ while (pos + 1 < len && ch != semicolon && ch.isLetterOrNumber())
+ ch = text.at(++pos);
+ if (ch == semicolon)
+ setFormat(start, pos - start + 1, m_formats[Entity]);
+ } else if (ch == percent) {
+ start = pos;
+ // %[1-9]*
+ for (++pos; pos < len && text.at(pos).isDigit(); ++pos) {}
+ // %n
+ if (pos < len && pos == start + 1 && text.at(pos) == QLatin1Char('n'))
+ ++pos;
+ setFormat(start, pos - start, m_formats[Variable]);
+ } else {
+ // No tag, comment or entity started, continue...
+ ++pos;
+ }
+ }
+ break;
+ case InComment:
+ start = pos;
+ while (pos < len) {
+ if (text.mid(pos, 3) == endComment) {
+ pos += 3;
+ state = NormalState;
+ break;
+ } else {
+ ++pos;
+ }
+ }
+ setFormat(start, pos - start, m_formats[Comment]);
+ break;
+ case InTag:
+ QChar quote = QChar::Null;
+ while (pos < len) {
+ QChar ch = text.at(pos);
+ if (quote.isNull()) {
+ start = pos;
+ if (ch == apos || ch == quot) {
+ quote = ch;
+ } else if (ch == endTag) {
+ ++pos;
+ setFormat(start, pos - start, m_formats[Tag]);
+ state = NormalState;
+ break;
+ } else if (text.mid(pos, 2) == endElement) {
+ pos += 2;
+ setFormat(start, pos - start, m_formats[Tag]);
+ state = NormalState;
+ break;
+ } else if (ch != space && text.at(pos) != tab) {
+ // Tag not ending, not a quote and no whitespace, so
+ // we must be dealing with an attribute.
+ ++pos;
+ while (pos < len && text.at(pos) != space
+ && text.at(pos) != tab
+ && text.at(pos) != equals)
+ ++pos;
+ setFormat(start, pos - start, m_formats[Attribute]);
+ start = pos;
+ }
+ } else if (ch == quote) {
+ quote = QChar::Null;
+
+ // Anything quoted is a value
+ setFormat(start, pos - start, m_formats[Value]);
+ }
+ ++pos;
+ }
+ break;
+ }
+ }
+ setCurrentBlockState(state);
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/messagehighlighter.h b/src/linguist/linguist/messagehighlighter.h
new file mode 100644
index 000000000..455140f61
--- /dev/null
+++ b/src/linguist/linguist/messagehighlighter.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MESSAGEHIGHLIGHTER_H
+#define MESSAGEHIGHLIGHTER_H
+
+#include <QtGui/QSyntaxHighlighter>
+
+QT_BEGIN_NAMESPACE
+
+/* Message highlighter based on HtmlSyntaxHighlighter from designer */
+class MessageHighlighter : public QSyntaxHighlighter
+{
+ Q_OBJECT
+
+public:
+ MessageHighlighter(QTextEdit *textEdit);
+
+protected:
+ void highlightBlock(const QString &text);
+
+private:
+ enum Construct {
+ Entity,
+ Tag,
+ Comment,
+ Attribute,
+ Value,
+ Accelerator, // "Open &File"
+ Variable, // "Opening %1"
+ LastConstruct = Variable
+ };
+
+ enum State {
+ NormalState = -1,
+ InComment,
+ InTag
+ };
+
+ QTextCharFormat m_formats[LastConstruct + 1];
+};
+
+QT_END_NAMESPACE
+
+#endif // MESSAGEHIGHLIGHTER_H
diff --git a/src/linguist/linguist/messagemodel.cpp b/src/linguist/linguist/messagemodel.cpp
new file mode 100644
index 000000000..2d126577c
--- /dev/null
+++ b/src/linguist/linguist/messagemodel.cpp
@@ -0,0 +1,1426 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "messagemodel.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QTextCodec>
+
+#include <QtGui/QMessageBox>
+#include <QtGui/QPainter>
+#include <QtGui/QPixmap>
+#include <QtGui/QTextDocument>
+
+#include <private/qtranslator_p.h>
+
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+/******************************************************************************
+ *
+ * MessageItem
+ *
+ *****************************************************************************/
+
+MessageItem::MessageItem(const TranslatorMessage &message)
+ : m_message(message),
+ m_danger(false)
+{
+ if (m_message.translation().isEmpty())
+ m_message.setTranslation(QString());
+}
+
+
+bool MessageItem::compare(const QString &findText, bool matchSubstring,
+ Qt::CaseSensitivity cs) const
+{
+ return matchSubstring
+ ? text().indexOf(findText, 0, cs) >= 0
+ : text().compare(findText, cs) == 0;
+}
+
+/******************************************************************************
+ *
+ * ContextItem
+ *
+ *****************************************************************************/
+
+ContextItem::ContextItem(const QString &context)
+ : m_context(context),
+ m_finishedCount(0),
+ m_finishedDangerCount(0),
+ m_unfinishedDangerCount(0),
+ m_nonobsoleteCount(0)
+{}
+
+void ContextItem::appendToComment(const QString &str)
+{
+ if (!m_comment.isEmpty())
+ m_comment += QLatin1String("\n\n");
+ m_comment += str;
+}
+
+MessageItem *ContextItem::messageItem(int i) const
+{
+ if (i >= 0 && i < msgItemList.count())
+ return const_cast<MessageItem *>(&msgItemList[i]);
+ Q_ASSERT(i >= 0 && i < msgItemList.count());
+ return 0;
+}
+
+MessageItem *ContextItem::findMessage(const QString &sourcetext, const QString &comment) const
+{
+ for (int i = 0; i < messageCount(); ++i) {
+ MessageItem *mi = messageItem(i);
+ if (mi->text() == sourcetext && mi->comment() == comment)
+ return mi;
+ }
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * DataModel
+ *
+ *****************************************************************************/
+
+DataModel::DataModel(QObject *parent)
+ : QObject(parent),
+ m_modified(false),
+ m_numMessages(0),
+ m_srcWords(0),
+ m_srcChars(0),
+ m_srcCharsSpc(0),
+ m_language(QLocale::Language(-1)),
+ m_sourceLanguage(QLocale::Language(-1)),
+ m_country(QLocale::Country(-1)),
+ m_sourceCountry(QLocale::Country(-1))
+{}
+
+QStringList DataModel::normalizedTranslations(const MessageItem &m) const
+{
+ return Translator::normalizedTranslations(m.message(), m_numerusForms.count());
+}
+
+ContextItem *DataModel::contextItem(int context) const
+{
+ if (context >= 0 && context < m_contextList.count())
+ return const_cast<ContextItem *>(&m_contextList[context]);
+ Q_ASSERT(context >= 0 && context < m_contextList.count());
+ return 0;
+}
+
+MessageItem *DataModel::messageItem(const DataIndex &index) const
+{
+ if (ContextItem *c = contextItem(index.context()))
+ return c->messageItem(index.message());
+ return 0;
+}
+
+ContextItem *DataModel::findContext(const QString &context) const
+{
+ for (int c = 0; c < m_contextList.count(); ++c) {
+ ContextItem *ctx = contextItem(c);
+ if (ctx->context() == context)
+ return ctx;
+ }
+ return 0;
+}
+
+MessageItem *DataModel::findMessage(const QString &context,
+ const QString &sourcetext, const QString &comment) const
+{
+ if (ContextItem *ctx = findContext(context))
+ return ctx->findMessage(sourcetext, comment);
+ return 0;
+}
+
+static int calcMergeScore(const DataModel *one, const DataModel *two)
+{
+ int inBoth = 0;
+ for (int i = 0; i < two->contextCount(); ++i) {
+ ContextItem *oc = two->contextItem(i);
+ if (ContextItem *c = one->findContext(oc->context())) {
+ for (int j = 0; j < oc->messageCount(); ++j) {
+ MessageItem *m = oc->messageItem(j);
+ if (c->findMessage(m->text(), m->comment()) >= 0)
+ ++inBoth;
+ }
+ }
+ }
+ return inBoth * 100 / two->messageCount();
+}
+
+bool DataModel::isWellMergeable(const DataModel *other) const
+{
+ if (!other->messageCount() || !messageCount())
+ return true;
+
+ return calcMergeScore(this, other) + calcMergeScore(other, this) > 90;
+}
+
+bool DataModel::load(const QString &fileName, bool *langGuessed, QWidget *parent)
+{
+ Translator tor;
+ ConversionData cd;
+ bool ok = tor.load(fileName, cd, QLatin1String("auto"));
+ if (!ok) {
+ QMessageBox::warning(parent, QObject::tr("Qt Linguist"), cd.error());
+ return false;
+ }
+
+ if (!tor.messageCount()) {
+ QMessageBox::warning(parent, QObject::tr("Qt Linguist"),
+ tr("The translation file '%1' will not be loaded because it is empty.")
+ .arg(Qt::escape(fileName)));
+ return false;
+ }
+
+ Translator::Duplicates dupes = tor.resolveDuplicates();
+ if (!dupes.byId.isEmpty() || !dupes.byContents.isEmpty()) {
+ QString err = tr("<qt>Duplicate messages found in '%1':").arg(Qt::escape(fileName));
+ int numdups = 0;
+ foreach (int i, dupes.byId) {
+ if (++numdups >= 5) {
+ err += tr("<p>[more duplicates omitted]");
+ goto doWarn;
+ }
+ err += tr("<p>* ID: %1").arg(Qt::escape(tor.message(i).id()));
+ }
+ foreach (int j, dupes.byContents) {
+ const TranslatorMessage &msg = tor.message(j);
+ if (++numdups >= 5) {
+ err += tr("<p>[more duplicates omitted]");
+ break;
+ }
+ err += tr("<p>* Context: %1<br>* Source: %2")
+ .arg(Qt::escape(msg.context()), Qt::escape(msg.sourceText()));
+ if (!msg.comment().isEmpty())
+ err += tr("<br>* Comment: %3").arg(Qt::escape(msg.comment()));
+ }
+ doWarn:
+ QMessageBox::warning(parent, QObject::tr("Qt Linguist"), err);
+ }
+
+ m_srcFileName = fileName;
+ m_codecName = tor.codecName();
+ m_relativeLocations = (tor.locationsType() == Translator::RelativeLocations);
+ m_extra = tor.extras();
+ m_contextList.clear();
+ m_numMessages = 0;
+
+ QHash<QString, int> contexts;
+
+ m_srcWords = 0;
+ m_srcChars = 0;
+ m_srcCharsSpc = 0;
+
+ foreach (const TranslatorMessage &msg, tor.messages()) {
+ if (!contexts.contains(msg.context())) {
+ contexts.insert(msg.context(), m_contextList.size());
+ m_contextList.append(ContextItem(msg.context()));
+ }
+
+ ContextItem *c = contextItem(contexts.value(msg.context()));
+ if (msg.sourceText() == QLatin1String(ContextComment)) {
+ c->appendToComment(msg.comment());
+ } else {
+ MessageItem tmp(msg);
+ if (msg.type() == TranslatorMessage::Finished)
+ c->incrementFinishedCount();
+ if (msg.type() != TranslatorMessage::Obsolete) {
+ doCharCounting(tmp.text(), m_srcWords, m_srcChars, m_srcCharsSpc);
+ doCharCounting(tmp.pluralText(), m_srcWords, m_srcChars, m_srcCharsSpc);
+ c->incrementNonobsoleteCount();
+ }
+ c->appendMessage(tmp);
+ ++m_numMessages;
+ }
+ }
+
+ // Try to detect the correct language in the following order
+ // 1. Look for the language attribute in the ts
+ // if that fails
+ // 2. Guestimate the language from the filename
+ // (expecting the qt_{en,de}.ts convention)
+ // if that fails
+ // 3. Retrieve the locale from the system.
+ *langGuessed = false;
+ QString lang = tor.languageCode();
+ if (lang.isEmpty()) {
+ lang = QFileInfo(fileName).baseName();
+ int pos = lang.indexOf(QLatin1Char('_'));
+ if (pos != -1 && pos + 3 == lang.length())
+ lang = fileName.mid(pos + 1);
+ else
+ lang.clear();
+ *langGuessed = true;
+ }
+ QLocale::Language l;
+ QLocale::Country c;
+ Translator::languageAndCountry(lang, &l, &c);
+ if (l == QLocale::C) {
+ QLocale sys;
+ l = sys.language();
+ c = sys.country();
+ *langGuessed = true;
+ }
+ if (!setLanguageAndCountry(l, c))
+ QMessageBox::warning(parent, QObject::tr("Qt Linguist"),
+ tr("Linguist does not know the plural rules for '%1'.\n"
+ "Will assume a single universal form.")
+ .arg(m_localizedLanguage));
+ // Try to detect the correct source language in the following order
+ // 1. Look for the language attribute in the ts
+ // if that fails
+ // 2. Assume English
+ lang = tor.sourceLanguageCode();
+ if (lang.isEmpty()) {
+ l = QLocale::C;
+ c = QLocale::AnyCountry;
+ } else {
+ Translator::languageAndCountry(lang, &l, &c);
+ }
+ setSourceLanguageAndCountry(l, c);
+
+ setModified(false);
+
+ return true;
+}
+
+bool DataModel::save(const QString &fileName, QWidget *parent)
+{
+ Translator tor;
+ for (DataModelIterator it(this); it.isValid(); ++it)
+ tor.append(it.current()->message());
+
+ tor.setLanguageCode(Translator::makeLanguageCode(m_language, m_country));
+ tor.setSourceLanguageCode(Translator::makeLanguageCode(m_sourceLanguage, m_sourceCountry));
+ tor.setCodecName(m_codecName);
+ tor.setLocationsType(m_relativeLocations ? Translator::RelativeLocations
+ : Translator::AbsoluteLocations);
+ tor.setExtras(m_extra);
+ ConversionData cd;
+ bool ok = tor.save(fileName, cd, QLatin1String("auto"));
+ if (ok)
+ setModified(false);
+ else
+ QMessageBox::warning(parent, QObject::tr("Qt Linguist"), cd.error());
+ return ok;
+}
+
+bool DataModel::saveAs(const QString &newFileName, QWidget *parent)
+{
+ if (!save(newFileName, parent))
+ return false;
+ m_srcFileName = newFileName;
+ return true;
+}
+
+bool DataModel::release(const QString &fileName, bool verbose, bool ignoreUnfinished,
+ TranslatorSaveMode mode, QWidget *parent)
+{
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly)) {
+ QMessageBox::warning(parent, QObject::tr("Qt Linguist"),
+ tr("Cannot create '%2': %1").arg(file.errorString()).arg(fileName));
+ return false;
+ }
+ Translator tor;
+ QLocale locale(m_language, m_country);
+ tor.setLanguageCode(locale.name());
+ for (DataModelIterator it(this); it.isValid(); ++it)
+ tor.append(it.current()->message());
+ ConversionData cd;
+ cd.m_verbose = verbose;
+ cd.m_ignoreUnfinished = ignoreUnfinished;
+ cd.m_saveMode = mode;
+ bool ok = tor.release(&file, cd);
+ if (!ok)
+ QMessageBox::warning(parent, QObject::tr("Qt Linguist"), cd.error());
+ return ok;
+}
+
+void DataModel::doCharCounting(const QString &text, int &trW, int &trC, int &trCS)
+{
+ trCS += text.length();
+ bool inWord = false;
+ for (int i = 0; i < text.length(); ++i) {
+ if (text[i].isLetterOrNumber() || text[i] == QLatin1Char('_')) {
+ if (!inWord) {
+ ++trW;
+ inWord = true;
+ }
+ } else {
+ inWord = false;
+ }
+ if (!text[i].isSpace())
+ trC++;
+ }
+}
+
+bool DataModel::setLanguageAndCountry(QLocale::Language lang, QLocale::Country country)
+{
+ if (m_language == lang && m_country == country)
+ return true;
+ m_language = lang;
+ m_country = country;
+
+ if (lang == QLocale::C || uint(lang) > uint(QLocale::LastLanguage)) // XXX does this make any sense?
+ lang = QLocale::English;
+ QByteArray rules;
+ bool ok = getNumerusInfo(lang, country, &rules, &m_numerusForms, 0);
+ m_localizedLanguage = QCoreApplication::translate("MessageEditor", QLocale::languageToString(lang).toAscii());
+ m_countRefNeeds.clear();
+ for (int i = 0; i < rules.size(); ++i) {
+ m_countRefNeeds.append(!(rules.at(i) == Q_EQ && (i == (rules.size() - 2) || rules.at(i + 2) == (char)Q_NEWRULE)));
+ while (++i < rules.size() && rules.at(i) != (char)Q_NEWRULE) {}
+ }
+ m_countRefNeeds.append(true);
+ if (!ok) {
+ m_numerusForms.clear();
+ m_numerusForms << tr("Universal Form");
+ }
+ emit languageChanged();
+ setModified(true);
+ return ok;
+}
+
+void DataModel::setSourceLanguageAndCountry(QLocale::Language lang, QLocale::Country country)
+{
+ if (m_sourceLanguage == lang && m_sourceCountry == country)
+ return;
+ m_sourceLanguage = lang;
+ m_sourceCountry = country;
+ setModified(true);
+}
+
+void DataModel::updateStatistics()
+{
+ int trW = 0;
+ int trC = 0;
+ int trCS = 0;
+
+ for (DataModelIterator it(this); it.isValid(); ++it) {
+ const MessageItem *mi = it.current();
+ if (mi->isFinished())
+ foreach (const QString &trnsl, mi->translations())
+ doCharCounting(trnsl, trW, trC, trCS);
+ }
+
+ emit statsChanged(m_srcWords, m_srcChars, m_srcCharsSpc, trW, trC, trCS);
+}
+
+void DataModel::setModified(bool isModified)
+{
+ if (m_modified == isModified)
+ return;
+ m_modified = isModified;
+ emit modifiedChanged();
+}
+
+QString DataModel::prettifyPlainFileName(const QString &fn)
+{
+ static QString workdir = QDir::currentPath() + QLatin1Char('/');
+
+ return QDir::toNativeSeparators(fn.startsWith(workdir) ? fn.mid(workdir.length()) : fn);
+}
+
+QString DataModel::prettifyFileName(const QString &fn)
+{
+ if (fn.startsWith(QLatin1Char('=')))
+ return QLatin1Char('=') + prettifyPlainFileName(fn.mid(1));
+ else
+ return prettifyPlainFileName(fn);
+}
+
+/******************************************************************************
+ *
+ * DataModelIterator
+ *
+ *****************************************************************************/
+
+DataModelIterator::DataModelIterator(DataModel *model, int context, int message)
+ : DataIndex(context, message), m_model(model)
+{
+}
+
+bool DataModelIterator::isValid() const
+{
+ return m_context < m_model->m_contextList.count();
+}
+
+void DataModelIterator::operator++()
+{
+ ++m_message;
+ if (m_message >= m_model->m_contextList.at(m_context).messageCount()) {
+ ++m_context;
+ m_message = 0;
+ }
+}
+
+MessageItem *DataModelIterator::current() const
+{
+ return m_model->messageItem(*this);
+}
+
+
+/******************************************************************************
+ *
+ * MultiMessageItem
+ *
+ *****************************************************************************/
+
+MultiMessageItem::MultiMessageItem(const MessageItem *m)
+ : m_text(m->text()),
+ m_pluralText(m->pluralText()),
+ m_comment(m->comment()),
+ m_nonnullCount(0),
+ m_nonobsoleteCount(0),
+ m_editableCount(0),
+ m_unfinishedCount(0)
+{
+}
+
+/******************************************************************************
+ *
+ * MultiContextItem
+ *
+ *****************************************************************************/
+
+MultiContextItem::MultiContextItem(int oldCount, ContextItem *ctx, bool writable)
+ : m_context(ctx->context()),
+ m_comment(ctx->comment()),
+ m_finishedCount(0),
+ m_editableCount(0),
+ m_nonobsoleteCount(0)
+{
+ QList<MessageItem *> mList;
+ QList<MessageItem *> eList;
+ for (int j = 0; j < ctx->messageCount(); ++j) {
+ MessageItem *m = ctx->messageItem(j);
+ mList.append(m);
+ eList.append(0);
+ m_multiMessageList.append(MultiMessageItem(m));
+ }
+ for (int i = 0; i < oldCount; ++i) {
+ m_messageLists.append(eList);
+ m_writableMessageLists.append(0);
+ m_contextList.append(0);
+ }
+ m_messageLists.append(mList);
+ m_writableMessageLists.append(writable ? &m_messageLists.last() : 0);
+ m_contextList.append(ctx);
+}
+
+void MultiContextItem::appendEmptyModel()
+{
+ QList<MessageItem *> eList;
+ for (int j = 0; j < messageCount(); ++j)
+ eList.append(0);
+ m_messageLists.append(eList);
+ m_writableMessageLists.append(0);
+ m_contextList.append(0);
+}
+
+void MultiContextItem::assignLastModel(ContextItem *ctx, bool writable)
+{
+ if (writable)
+ m_writableMessageLists.last() = &m_messageLists.last();
+ m_contextList.last() = ctx;
+}
+
+// XXX this is not needed, yet
+void MultiContextItem::moveModel(int oldPos, int newPos)
+{
+ m_contextList.insert(newPos, m_contextList[oldPos]);
+ m_messageLists.insert(newPos, m_messageLists[oldPos]);
+ m_writableMessageLists.insert(newPos, m_writableMessageLists[oldPos]);
+ removeModel(oldPos < newPos ? oldPos : oldPos + 1);
+}
+
+void MultiContextItem::removeModel(int pos)
+{
+ m_contextList.removeAt(pos);
+ m_messageLists.removeAt(pos);
+ m_writableMessageLists.removeAt(pos);
+}
+
+void MultiContextItem::putMessageItem(int pos, MessageItem *m)
+{
+ m_messageLists.last()[pos] = m;
+}
+
+void MultiContextItem::appendMessageItems(const QList<MessageItem *> &m)
+{
+ QList<MessageItem *> nullItems = m; // Basically, just a reservation
+ for (int i = 0; i < nullItems.count(); ++i)
+ nullItems[i] = 0;
+ for (int i = 0; i < m_messageLists.count() - 1; ++i)
+ m_messageLists[i] += nullItems;
+ m_messageLists.last() += m;
+ foreach (MessageItem *mi, m)
+ m_multiMessageList.append(MultiMessageItem(mi));
+}
+
+void MultiContextItem::removeMultiMessageItem(int pos)
+{
+ for (int i = 0; i < m_messageLists.count(); ++i)
+ m_messageLists[i].removeAt(pos);
+ m_multiMessageList.removeAt(pos);
+}
+
+int MultiContextItem::firstNonobsoleteMessageIndex(int msgIdx) const
+{
+ for (int i = 0; i < m_messageLists.size(); ++i)
+ if (m_messageLists[i][msgIdx] && !m_messageLists[i][msgIdx]->isObsolete())
+ return i;
+ return -1;
+}
+
+int MultiContextItem::findMessage(const QString &sourcetext, const QString &comment) const
+{
+ for (int i = 0, cnt = messageCount(); i < cnt; ++i) {
+ MultiMessageItem *m = multiMessageItem(i);
+ if (m->text() == sourcetext && m->comment() == comment)
+ return i;
+ }
+ return -1;
+}
+
+/******************************************************************************
+ *
+ * MultiDataModel
+ *
+ *****************************************************************************/
+
+static const uchar paletteRGBs[7][3] = {
+ { 236, 244, 255 }, // blue
+ { 236, 255, 255 }, // cyan
+ { 236, 255, 232 }, // green
+ { 255, 255, 230 }, // yellow
+ { 255, 242, 222 }, // orange
+ { 255, 236, 236 }, // red
+ { 252, 236, 255 } // purple
+};
+
+MultiDataModel::MultiDataModel(QObject *parent) :
+ QObject(parent),
+ m_numFinished(0),
+ m_numEditable(0),
+ m_numMessages(0),
+ m_modified(false)
+{
+ for (int i = 0; i < 7; ++i)
+ m_colors[i] = QColor(paletteRGBs[i][0], paletteRGBs[i][1], paletteRGBs[i][2]);
+
+ m_bitmap = QBitmap(8, 8);
+ m_bitmap.clear();
+ QPainter p(&m_bitmap);
+ for (int j = 0; j < 8; ++j)
+ for (int k = 0; k < 8; ++k)
+ if ((j + k) & 4)
+ p.drawPoint(j, k);
+}
+
+MultiDataModel::~MultiDataModel()
+{
+ qDeleteAll(m_dataModels);
+}
+
+QBrush MultiDataModel::brushForModel(int model) const
+{
+ QBrush brush(m_colors[model % 7]);
+ if (!isModelWritable(model))
+ brush.setTexture(m_bitmap);
+ return brush;
+}
+
+bool MultiDataModel::isWellMergeable(const DataModel *dm) const
+{
+ if (!dm->messageCount() || !messageCount())
+ return true;
+
+ int inBothNew = 0;
+ for (int i = 0; i < dm->contextCount(); ++i) {
+ ContextItem *c = dm->contextItem(i);
+ if (MultiContextItem *mc = findContext(c->context())) {
+ for (int j = 0; j < c->messageCount(); ++j) {
+ MessageItem *m = c->messageItem(j);
+ if (mc->findMessage(m->text(), m->comment()) >= 0)
+ ++inBothNew;
+ }
+ }
+ }
+ int newRatio = inBothNew * 100 / dm->messageCount();
+
+ int inBothOld = 0;
+ for (int k = 0; k < contextCount(); ++k) {
+ MultiContextItem *mc = multiContextItem(k);
+ if (ContextItem *c = dm->findContext(mc->context())) {
+ for (int j = 0; j < mc->messageCount(); ++j) {
+ MultiMessageItem *m = mc->multiMessageItem(j);
+ if (c->findMessage(m->text(), m->comment()))
+ ++inBothOld;
+ }
+ }
+ }
+ int oldRatio = inBothOld * 100 / messageCount();
+
+ return newRatio + oldRatio > 90;
+}
+
+void MultiDataModel::append(DataModel *dm, bool readWrite)
+{
+ int insCol = modelCount() + 1;
+ m_msgModel->beginInsertColumns(QModelIndex(), insCol, insCol);
+ m_dataModels.append(dm);
+ for (int j = 0; j < contextCount(); ++j) {
+ m_msgModel->beginInsertColumns(m_msgModel->createIndex(j, 0, 0), insCol, insCol);
+ m_multiContextList[j].appendEmptyModel();
+ m_msgModel->endInsertColumns();
+ }
+ m_msgModel->endInsertColumns();
+ int appendedContexts = 0;
+ for (int i = 0; i < dm->contextCount(); ++i) {
+ ContextItem *c = dm->contextItem(i);
+ int mcx = findContextIndex(c->context());
+ if (mcx >= 0) {
+ MultiContextItem *mc = multiContextItem(mcx);
+ mc->assignLastModel(c, readWrite);
+ QList<MessageItem *> appendItems;
+ for (int j = 0; j < c->messageCount(); ++j) {
+ MessageItem *m = c->messageItem(j);
+ int msgIdx = mc->findMessage(m->text(), m->comment());
+ if (msgIdx >= 0)
+ mc->putMessageItem(msgIdx, m);
+ else
+ appendItems << m;
+ }
+ if (!appendItems.isEmpty()) {
+ int msgCnt = mc->messageCount();
+ m_msgModel->beginInsertRows(m_msgModel->createIndex(mcx, 0, 0),
+ msgCnt, msgCnt + appendItems.size() - 1);
+ mc->appendMessageItems(appendItems);
+ m_msgModel->endInsertRows();
+ m_numMessages += appendItems.size();
+ }
+ } else {
+ m_multiContextList << MultiContextItem(modelCount() - 1, c, readWrite);
+ m_numMessages += c->messageCount();
+ ++appendedContexts;
+ }
+ }
+ if (appendedContexts) {
+ // Do that en block to avoid itemview inefficiency. It doesn't hurt that we
+ // announce the availability of the data "long" after it was actually added.
+ m_msgModel->beginInsertRows(QModelIndex(),
+ contextCount() - appendedContexts, contextCount() - 1);
+ m_msgModel->endInsertRows();
+ }
+ dm->setWritable(readWrite);
+ updateCountsOnAdd(modelCount() - 1, readWrite);
+ connect(dm, SIGNAL(modifiedChanged()), SLOT(onModifiedChanged()));
+ connect(dm, SIGNAL(languageChanged()), SLOT(onLanguageChanged()));
+ connect(dm, SIGNAL(statsChanged(int,int,int,int,int,int)), SIGNAL(statsChanged(int,int,int,int,int,int)));
+ emit modelAppended();
+}
+
+void MultiDataModel::close(int model)
+{
+ if (m_dataModels.count() == 1) {
+ closeAll();
+ } else {
+ updateCountsOnRemove(model, isModelWritable(model));
+ int delCol = model + 1;
+ m_msgModel->beginRemoveColumns(QModelIndex(), delCol, delCol);
+ for (int i = m_multiContextList.size(); --i >= 0;) {
+ m_msgModel->beginRemoveColumns(m_msgModel->createIndex(i, 0, 0), delCol, delCol);
+ m_multiContextList[i].removeModel(model);
+ m_msgModel->endRemoveColumns();
+ }
+ delete m_dataModels.takeAt(model);
+ m_msgModel->endRemoveColumns();
+ emit modelDeleted(model);
+ for (int i = m_multiContextList.size(); --i >= 0;) {
+ MultiContextItem &mc = m_multiContextList[i];
+ QModelIndex contextIdx = m_msgModel->createIndex(i, 0, 0);
+ for (int j = mc.messageCount(); --j >= 0;)
+ if (mc.multiMessageItem(j)->isEmpty()) {
+ m_msgModel->beginRemoveRows(contextIdx, j, j);
+ mc.removeMultiMessageItem(j);
+ m_msgModel->endRemoveRows();
+ --m_numMessages;
+ }
+ if (!mc.messageCount()) {
+ m_msgModel->beginRemoveRows(QModelIndex(), i, i);
+ m_multiContextList.removeAt(i);
+ m_msgModel->endRemoveRows();
+ }
+ }
+ onModifiedChanged();
+ }
+}
+
+void MultiDataModel::closeAll()
+{
+ m_numFinished = 0;
+ m_numEditable = 0;
+ m_numMessages = 0;
+ qDeleteAll(m_dataModels);
+ m_dataModels.clear();
+ m_multiContextList.clear();
+ m_msgModel->reset();
+ emit allModelsDeleted();
+ onModifiedChanged();
+}
+
+// XXX this is not needed, yet
+void MultiDataModel::moveModel(int oldPos, int newPos)
+{
+ int delPos = oldPos < newPos ? oldPos : oldPos + 1;
+ m_dataModels.insert(newPos, m_dataModels[oldPos]);
+ m_dataModels.removeAt(delPos);
+ for (int i = 0; i < m_multiContextList.size(); ++i)
+ m_multiContextList[i].moveModel(oldPos, newPos);
+}
+
+QStringList MultiDataModel::prettifyFileNames(const QStringList &names)
+{
+ QStringList out;
+
+ foreach (const QString &name, names)
+ out << DataModel::prettifyFileName(name);
+ return out;
+}
+
+QString MultiDataModel::condenseFileNames(const QStringList &names)
+{
+ if (names.isEmpty())
+ return QString();
+
+ if (names.count() < 2)
+ return names.first();
+
+ QString prefix = names.first();
+ if (prefix.startsWith(QLatin1Char('=')))
+ prefix.remove(0, 1);
+ QString suffix = prefix;
+ for (int i = 1; i < names.count(); ++i) {
+ QString fn = names[i];
+ if (fn.startsWith(QLatin1Char('=')))
+ fn.remove(0, 1);
+ for (int j = 0; j < prefix.length(); ++j)
+ if (fn[j] != prefix[j]) {
+ if (j < prefix.length()) {
+ while (j > 0 && prefix[j - 1].isLetterOrNumber())
+ --j;
+ prefix.truncate(j);
+ }
+ break;
+ }
+ int fnl = fn.length() - 1;
+ int sxl = suffix.length() - 1;
+ for (int k = 0; k <= sxl; ++k)
+ if (fn[fnl - k] != suffix[sxl - k]) {
+ if (k < sxl) {
+ while (k > 0 && suffix[sxl - k + 1].isLetterOrNumber())
+ --k;
+ if (prefix.length() + k > fnl)
+ --k;
+ suffix.remove(0, sxl - k + 1);
+ }
+ break;
+ }
+ }
+ QString ret = prefix + QLatin1Char('{');
+ int pxl = prefix.length();
+ int sxl = suffix.length();
+ for (int j = 0; j < names.count(); ++j) {
+ if (j)
+ ret += QLatin1Char(',');
+ int off = pxl;
+ QString fn = names[j];
+ if (fn.startsWith(QLatin1Char('='))) {
+ ret += QLatin1Char('=');
+ ++off;
+ }
+ ret += fn.mid(off, fn.length() - sxl - off);
+ }
+ ret += QLatin1Char('}') + suffix;
+ return ret;
+}
+
+QStringList MultiDataModel::srcFileNames(bool pretty) const
+{
+ QStringList names;
+ foreach (DataModel *dm, m_dataModels)
+ names << (dm->isWritable() ? QString() : QString::fromLatin1("=")) + dm->srcFileName(pretty);
+ return names;
+}
+
+QString MultiDataModel::condensedSrcFileNames(bool pretty) const
+{
+ return condenseFileNames(srcFileNames(pretty));
+}
+
+bool MultiDataModel::isModified() const
+{
+ foreach (const DataModel *mdl, m_dataModels)
+ if (mdl->isModified())
+ return true;
+ return false;
+}
+
+void MultiDataModel::onModifiedChanged()
+{
+ bool modified = isModified();
+ if (modified != m_modified) {
+ emit modifiedChanged(modified);
+ m_modified = modified;
+ }
+}
+
+void MultiDataModel::onLanguageChanged()
+{
+ int i = 0;
+ while (sender() != m_dataModels[i])
+ ++i;
+ emit languageChanged(i);
+}
+
+int MultiDataModel::isFileLoaded(const QString &name) const
+{
+ for (int i = 0; i < m_dataModels.size(); ++i)
+ if (m_dataModels[i]->srcFileName() == name)
+ return i;
+ return -1;
+}
+
+int MultiDataModel::findContextIndex(const QString &context) const
+{
+ for (int i = 0; i < m_multiContextList.size(); ++i) {
+ const MultiContextItem &mc = m_multiContextList[i];
+ if (mc.context() == context)
+ return i;
+ }
+ return -1;
+}
+
+MultiContextItem *MultiDataModel::findContext(const QString &context) const
+{
+ for (int i = 0; i < m_multiContextList.size(); ++i) {
+ const MultiContextItem &mc = m_multiContextList[i];
+ if (mc.context() == context)
+ return const_cast<MultiContextItem *>(&mc);
+ }
+ return 0;
+}
+
+MessageItem *MultiDataModel::messageItem(const MultiDataIndex &index, int model) const
+{
+ if (index.context() < contextCount() && model >= 0 && model < modelCount()) {
+ MultiContextItem *mc = multiContextItem(index.context());
+ if (index.message() < mc->messageCount())
+ return mc->messageItem(model, index.message());
+ }
+ Q_ASSERT(model >= 0 && model < modelCount());
+ Q_ASSERT(index.context() < contextCount());
+ return 0;
+}
+
+void MultiDataModel::setTranslation(const MultiDataIndex &index, const QString &translation)
+{
+ MessageItem *m = messageItem(index);
+ if (translation == m->translation())
+ return;
+ m->setTranslation(translation);
+ setModified(index.model(), true);
+ emit translationChanged(index);
+}
+
+void MultiDataModel::setFinished(const MultiDataIndex &index, bool finished)
+{
+ MultiContextItem *mc = multiContextItem(index.context());
+ MultiMessageItem *mm = mc->multiMessageItem(index.message());
+ ContextItem *c = contextItem(index);
+ MessageItem *m = messageItem(index);
+ TranslatorMessage::Type type = m->type();
+ if (type == TranslatorMessage::Unfinished && finished) {
+ m->setType(TranslatorMessage::Finished);
+ mm->decrementUnfinishedCount();
+ if (!mm->countUnfinished()) {
+ incrementFinishedCount();
+ mc->incrementFinishedCount();
+ emit multiContextDataChanged(index);
+ }
+ c->incrementFinishedCount();
+ if (m->danger()) {
+ c->incrementFinishedDangerCount();
+ c->decrementUnfinishedDangerCount();
+ if (!c->unfinishedDangerCount()
+ || c->finishedCount() == c->nonobsoleteCount())
+ emit contextDataChanged(index);
+ } else if (c->finishedCount() == c->nonobsoleteCount()) {
+ emit contextDataChanged(index);
+ }
+ emit messageDataChanged(index);
+ setModified(index.model(), true);
+ } else if (type == TranslatorMessage::Finished && !finished) {
+ m->setType(TranslatorMessage::Unfinished);
+ mm->incrementUnfinishedCount();
+ if (mm->countUnfinished() == 1) {
+ decrementFinishedCount();
+ mc->decrementFinishedCount();
+ emit multiContextDataChanged(index);
+ }
+ c->decrementFinishedCount();
+ if (m->danger()) {
+ c->decrementFinishedDangerCount();
+ c->incrementUnfinishedDangerCount();
+ if (c->unfinishedDangerCount() == 1
+ || c->finishedCount() + 1 == c->nonobsoleteCount())
+ emit contextDataChanged(index);
+ } else if (c->finishedCount() + 1 == c->nonobsoleteCount()) {
+ emit contextDataChanged(index);
+ }
+ emit messageDataChanged(index);
+ setModified(index.model(), true);
+ }
+}
+
+void MultiDataModel::setDanger(const MultiDataIndex &index, bool danger)
+{
+ ContextItem *c = contextItem(index);
+ MessageItem *m = messageItem(index);
+ if (!m->danger() && danger) {
+ if (m->isFinished()) {
+ c->incrementFinishedDangerCount();
+ if (c->finishedDangerCount() == 1)
+ emit contextDataChanged(index);
+ } else {
+ c->incrementUnfinishedDangerCount();
+ if (c->unfinishedDangerCount() == 1)
+ emit contextDataChanged(index);
+ }
+ emit messageDataChanged(index);
+ m->setDanger(danger);
+ } else if (m->danger() && !danger) {
+ if (m->isFinished()) {
+ c->decrementFinishedDangerCount();
+ if (!c->finishedDangerCount())
+ emit contextDataChanged(index);
+ } else {
+ c->decrementUnfinishedDangerCount();
+ if (!c->unfinishedDangerCount())
+ emit contextDataChanged(index);
+ }
+ emit messageDataChanged(index);
+ m->setDanger(danger);
+ }
+}
+
+void MultiDataModel::updateCountsOnAdd(int model, bool writable)
+{
+ for (int i = 0; i < m_multiContextList.size(); ++i) {
+ MultiContextItem &mc = m_multiContextList[i];
+ for (int j = 0; j < mc.messageCount(); ++j)
+ if (MessageItem *m = mc.messageItem(model, j)) {
+ MultiMessageItem *mm = mc.multiMessageItem(j);
+ mm->incrementNonnullCount();
+ if (!m->isObsolete()) {
+ if (writable) {
+ if (!mm->countEditable()) {
+ mc.incrementEditableCount();
+ incrementEditableCount();
+ if (m->isFinished()) {
+ mc.incrementFinishedCount();
+ incrementFinishedCount();
+ } else {
+ mm->incrementUnfinishedCount();
+ }
+ } else if (!m->isFinished()) {
+ if (!mm->isUnfinished()) {
+ mc.decrementFinishedCount();
+ decrementFinishedCount();
+ }
+ mm->incrementUnfinishedCount();
+ }
+ mm->incrementEditableCount();
+ }
+ mc.incrementNonobsoleteCount();
+ mm->incrementNonobsoleteCount();
+ }
+ }
+ }
+}
+
+void MultiDataModel::updateCountsOnRemove(int model, bool writable)
+{
+ for (int i = 0; i < m_multiContextList.size(); ++i) {
+ MultiContextItem &mc = m_multiContextList[i];
+ for (int j = 0; j < mc.messageCount(); ++j)
+ if (MessageItem *m = mc.messageItem(model, j)) {
+ MultiMessageItem *mm = mc.multiMessageItem(j);
+ mm->decrementNonnullCount();
+ if (!m->isObsolete()) {
+ mm->decrementNonobsoleteCount();
+ mc.decrementNonobsoleteCount();
+ if (writable) {
+ mm->decrementEditableCount();
+ if (!mm->countEditable()) {
+ mc.decrementEditableCount();
+ decrementEditableCount();
+ if (m->isFinished()) {
+ mc.decrementFinishedCount();
+ decrementFinishedCount();
+ } else {
+ mm->decrementUnfinishedCount();
+ }
+ } else if (!m->isFinished()) {
+ mm->decrementUnfinishedCount();
+ if (!mm->isUnfinished()) {
+ mc.incrementFinishedCount();
+ incrementFinishedCount();
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/******************************************************************************
+ *
+ * MultiDataModelIterator
+ *
+ *****************************************************************************/
+
+MultiDataModelIterator::MultiDataModelIterator(MultiDataModel *dataModel, int model, int context, int message)
+ : MultiDataIndex(model, context, message), m_dataModel(dataModel)
+{
+}
+
+void MultiDataModelIterator::operator++()
+{
+ Q_ASSERT(isValid());
+ ++m_message;
+ if (m_message >= m_dataModel->m_multiContextList.at(m_context).messageCount()) {
+ ++m_context;
+ m_message = 0;
+ }
+}
+
+bool MultiDataModelIterator::isValid() const
+{
+ return m_context < m_dataModel->m_multiContextList.count();
+}
+
+MessageItem *MultiDataModelIterator::current() const
+{
+ return m_dataModel->messageItem(*this);
+}
+
+
+/******************************************************************************
+ *
+ * MessageModel
+ *
+ *****************************************************************************/
+
+MessageModel::MessageModel(QObject *parent, MultiDataModel *data)
+ : QAbstractItemModel(parent), m_data(data)
+{
+ data->m_msgModel = this;
+ connect(m_data, SIGNAL(multiContextDataChanged(MultiDataIndex)),
+ SLOT(multiContextItemChanged(MultiDataIndex)));
+ connect(m_data, SIGNAL(contextDataChanged(MultiDataIndex)),
+ SLOT(contextItemChanged(MultiDataIndex)));
+ connect(m_data, SIGNAL(messageDataChanged(MultiDataIndex)),
+ SLOT(messageItemChanged(MultiDataIndex)));
+}
+
+QModelIndex MessageModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (!parent.isValid())
+ return createIndex(row, column, 0);
+ if (!parent.internalId())
+ return createIndex(row, column, parent.row() + 1);
+ return QModelIndex();
+}
+
+QModelIndex MessageModel::parent(const QModelIndex& index) const
+{
+ if (index.internalId())
+ return createIndex(index.internalId() - 1, 0, 0);
+ return QModelIndex();
+}
+
+void MessageModel::multiContextItemChanged(const MultiDataIndex &index)
+{
+ QModelIndex idx = createIndex(index.context(), m_data->modelCount() + 2, 0);
+ emit dataChanged(idx, idx);
+}
+
+void MessageModel::contextItemChanged(const MultiDataIndex &index)
+{
+ QModelIndex idx = createIndex(index.context(), index.model() + 1, 0);
+ emit dataChanged(idx, idx);
+}
+
+void MessageModel::messageItemChanged(const MultiDataIndex &index)
+{
+ QModelIndex idx = createIndex(index.message(), index.model() + 1, index.context() + 1);
+ emit dataChanged(idx, idx);
+}
+
+QModelIndex MessageModel::modelIndex(const MultiDataIndex &index)
+{
+ if (index.message() < 0) // Should be unused case
+ return createIndex(index.context(), index.model() + 1, 0);
+ return createIndex(index.message(), index.model() + 1, index.context() + 1);
+}
+
+int MessageModel::rowCount(const QModelIndex &parent) const
+{
+ if (!parent.isValid())
+ return m_data->contextCount(); // contexts
+ if (!parent.internalId()) // messages
+ return m_data->multiContextItem(parent.row())->messageCount();
+ return 0;
+}
+
+int MessageModel::columnCount(const QModelIndex &) const
+{
+ return m_data->modelCount() + 3;
+}
+
+QVariant MessageModel::data(const QModelIndex &index, int role) const
+{
+ static QVariant pxOn =
+ QVariant::fromValue(QPixmap(QLatin1String(":/images/s_check_on.png")));
+ static QVariant pxOff =
+ QVariant::fromValue(QPixmap(QLatin1String(":/images/s_check_off.png")));
+ static QVariant pxObsolete =
+ QVariant::fromValue(QPixmap(QLatin1String(":/images/s_check_obsolete.png")));
+ static QVariant pxDanger =
+ QVariant::fromValue(QPixmap(QLatin1String(":/images/s_check_danger.png")));
+ static QVariant pxWarning =
+ QVariant::fromValue(QPixmap(QLatin1String(":/images/s_check_warning.png")));
+ static QVariant pxEmpty =
+ QVariant::fromValue(QPixmap(QLatin1String(":/images/s_check_empty.png")));
+
+ int row = index.row();
+ int column = index.column() - 1;
+ if (column < 0)
+ return QVariant();
+
+ int numLangs = m_data->modelCount();
+
+ if (role == Qt::ToolTipRole && column < numLangs) {
+ return tr("Completion status for %1").arg(m_data->model(column)->localizedLanguage());
+ } else if (index.internalId()) {
+ // this is a message
+ int crow = index.internalId() - 1;
+ MultiContextItem *mci = m_data->multiContextItem(crow);
+ if (row >= mci->messageCount() || !index.isValid())
+ return QVariant();
+
+ if (role == Qt::DisplayRole || (role == Qt::ToolTipRole && column == numLangs)) {
+ switch (column - numLangs) {
+ case 0: // Source text
+ {
+ MultiMessageItem *msgItem = mci->multiMessageItem(row);
+ if (msgItem->text().isEmpty()) {
+ if (mci->context().isEmpty())
+ return tr("<file header>");
+ else
+ return tr("<context comment>");
+ }
+ return msgItem->text().simplified();
+ }
+ default: // Status or dummy column => no text
+ return QVariant();
+ }
+ }
+ else if (role == Qt::DecorationRole && column < numLangs) {
+ if (MessageItem *msgItem = mci->messageItem(column, row)) {
+ switch (msgItem->message().type()) {
+ case TranslatorMessage::Unfinished:
+ if (msgItem->translation().isEmpty())
+ return pxEmpty;
+ if (msgItem->danger())
+ return pxDanger;
+ return pxOff;
+ case TranslatorMessage::Finished:
+ if (msgItem->danger())
+ return pxWarning;
+ return pxOn;
+ default:
+ return pxObsolete;
+ }
+ }
+ return QVariant();
+ }
+ else if (role == SortRole) {
+ switch (column - numLangs) {
+ case 0: // Source text
+ return mci->multiMessageItem(row)->text().simplified().remove(QLatin1Char('&'));
+ case 1: // Dummy column
+ return QVariant();
+ default:
+ if (MessageItem *msgItem = mci->messageItem(column, row)) {
+ int rslt = !msgItem->translation().isEmpty();
+ if (!msgItem->danger())
+ rslt |= 2;
+ if (msgItem->isObsolete())
+ rslt |= 8;
+ else if (msgItem->isFinished())
+ rslt |= 4;
+ return rslt;
+ }
+ return INT_MAX;
+ }
+ }
+ else if (role == Qt::ForegroundRole && column > 0
+ && mci->multiMessageItem(row)->isObsolete()) {
+ return QBrush(Qt::darkGray);
+ }
+ else if (role == Qt::ForegroundRole && column == numLangs
+ && mci->multiMessageItem(row)->text().isEmpty()) {
+ return QBrush(QColor(0, 0xa0, 0xa0));
+ }
+ else if (role == Qt::BackgroundRole) {
+ if (column < numLangs && numLangs != 1)
+ return m_data->brushForModel(column);
+ }
+ } else {
+ // this is a context
+ if (row >= m_data->contextCount() || !index.isValid())
+ return QVariant();
+
+ MultiContextItem *mci = m_data->multiContextItem(row);
+
+ if (role == Qt::DisplayRole || (role == Qt::ToolTipRole && column == numLangs)) {
+ switch (column - numLangs) {
+ case 0: // Context
+ {
+ if (mci->context().isEmpty())
+ return tr("<unnamed context>");
+ return mci->context().simplified();
+ }
+ case 1:
+ {
+ QString s;
+ s.sprintf("%d/%d", mci->getNumFinished(), mci->getNumEditable());
+ return s;
+ }
+ default:
+ return QVariant(); // Status => no text
+ }
+ }
+ else if (role == Qt::DecorationRole && column < numLangs) {
+ if (ContextItem *contextItem = mci->contextItem(column)) {
+ if (contextItem->isObsolete())
+ return pxObsolete;
+ if (contextItem->isFinished())
+ return contextItem->finishedDangerCount() > 0 ? pxWarning : pxOn;
+ return contextItem->unfinishedDangerCount() > 0 ? pxDanger : pxOff;
+ }
+ return QVariant();
+ }
+ else if (role == SortRole) {
+ switch (column - numLangs) {
+ case 0: // Context (same as display role)
+ return mci->context().simplified();
+ case 1: // Items
+ return mci->getNumEditable();
+ default: // Percent
+ if (ContextItem *contextItem = mci->contextItem(column)) {
+ int totalItems = contextItem->nonobsoleteCount();
+ int percent = totalItems ? (100 * contextItem->finishedCount()) / totalItems : 100;
+ int rslt = percent * (((1 << 28) - 1) / 100) + totalItems;
+ if (contextItem->isObsolete()) {
+ rslt |= (1 << 30);
+ } else if (contextItem->isFinished()) {
+ rslt |= (1 << 29);
+ if (!contextItem->finishedDangerCount())
+ rslt |= (1 << 28);
+ } else {
+ if (!contextItem->unfinishedDangerCount())
+ rslt |= (1 << 28);
+ }
+ return rslt;
+ }
+ return INT_MAX;
+ }
+ }
+ else if (role == Qt::ForegroundRole && column >= numLangs
+ && m_data->multiContextItem(row)->isObsolete()) {
+ return QBrush(Qt::darkGray);
+ }
+ else if (role == Qt::ForegroundRole && column == numLangs
+ && m_data->multiContextItem(row)->context().isEmpty()) {
+ return QBrush(QColor(0, 0xa0, 0xa0));
+ }
+ else if (role == Qt::BackgroundRole) {
+ if (column < numLangs && numLangs != 1) {
+ QBrush brush = m_data->brushForModel(column);
+ if (row & 1) {
+ brush.setColor(brush.color().darker(108));
+ }
+ return brush;
+ }
+ }
+ }
+ return QVariant();
+}
+
+MultiDataIndex MessageModel::dataIndex(const QModelIndex &index, int model) const
+{
+ Q_ASSERT(index.isValid());
+ Q_ASSERT(index.internalId());
+ return MultiDataIndex(model, index.internalId() - 1, index.row());
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/messagemodel.h b/src/linguist/linguist/messagemodel.h
new file mode 100644
index 000000000..d8ca9d8e8
--- /dev/null
+++ b/src/linguist/linguist/messagemodel.h
@@ -0,0 +1,535 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MESSAGEMODEL_H
+#define MESSAGEMODEL_H
+
+#include "translator.h"
+
+#include <QtCore/QAbstractItemModel>
+#include <QtCore/QList>
+#include <QtCore/QHash>
+#include <QtCore/QLocale>
+#include <QtGui/QColor>
+#include <QtGui/QBitmap>
+#include <QtXml/QXmlDefaultHandler>
+
+
+QT_BEGIN_NAMESPACE
+
+class DataModel;
+class MultiDataModel;
+
+class MessageItem
+{
+public:
+ MessageItem(const TranslatorMessage &message);
+
+ bool danger() const { return m_danger; }
+ void setDanger(bool danger) { m_danger = danger; }
+
+ void setTranslation(const QString &translation)
+ { m_message.setTranslation(translation); }
+
+ QString context() const { return m_message.context(); }
+ QString text() const { return m_message.sourceText(); }
+ QString pluralText() const { return m_message.extra(QLatin1String("po-msgid_plural")); }
+ QString comment() const { return m_message.comment(); }
+ QString fileName() const { return m_message.fileName(); }
+ QString extraComment() const { return m_message.extraComment(); }
+ QString translatorComment() const { return m_message.translatorComment(); }
+ void setTranslatorComment(const QString &cmt) { m_message.setTranslatorComment(cmt); }
+ int lineNumber() const { return m_message.lineNumber(); }
+ QString translation() const { return m_message.translation(); }
+ QStringList translations() const { return m_message.translations(); }
+ void setTranslations(const QStringList &translations)
+ { m_message.setTranslations(translations); }
+
+ TranslatorMessage::Type type() const { return m_message.type(); }
+ void setType(TranslatorMessage::Type type) { m_message.setType(type); }
+
+ bool isFinished() const { return type() == TranslatorMessage::Finished; }
+ bool isObsolete() const { return type() == TranslatorMessage::Obsolete; }
+ const TranslatorMessage &message() const { return m_message; }
+
+ bool compare(const QString &findText, bool matchSubstring,
+ Qt::CaseSensitivity cs) const;
+
+private:
+ TranslatorMessage m_message;
+ bool m_danger;
+};
+
+
+class ContextItem
+{
+public:
+ ContextItem(const QString &context);
+
+ int finishedDangerCount() const { return m_finishedDangerCount; }
+ int unfinishedDangerCount() const { return m_unfinishedDangerCount; }
+
+ int finishedCount() const { return m_finishedCount; }
+ int unfinishedCount() const { return m_nonobsoleteCount - m_finishedCount; }
+ int nonobsoleteCount() const { return m_nonobsoleteCount; }
+
+ QString context() const { return m_context; }
+ QString comment() const { return m_comment; }
+ QString fullContext() const { return m_comment.trimmed(); }
+
+ // For item status in context list
+ bool isObsolete() const { return !nonobsoleteCount(); }
+ bool isFinished() const { return unfinishedCount() == 0; }
+
+ MessageItem *messageItem(int i) const;
+ int messageCount() const { return msgItemList.count(); }
+
+ MessageItem *findMessage(const QString &sourcetext, const QString &comment) const;
+
+private:
+ friend class DataModel;
+ friend class MultiDataModel;
+ void appendMessage(const MessageItem &msg) { msgItemList.append(msg); }
+ void appendToComment(const QString &x);
+ void incrementFinishedCount() { ++m_finishedCount; }
+ void decrementFinishedCount() { --m_finishedCount; }
+ void incrementFinishedDangerCount() { ++m_finishedDangerCount; }
+ void decrementFinishedDangerCount() { --m_finishedDangerCount; }
+ void incrementUnfinishedDangerCount() { ++m_unfinishedDangerCount; }
+ void decrementUnfinishedDangerCount() { --m_unfinishedDangerCount; }
+ void incrementNonobsoleteCount() { ++m_nonobsoleteCount; }
+
+ QString m_comment;
+ QString m_context;
+ int m_finishedCount;
+ int m_finishedDangerCount;
+ int m_unfinishedDangerCount;
+ int m_nonobsoleteCount;
+ QList<MessageItem> msgItemList;
+};
+
+
+class DataIndex
+{
+public:
+ DataIndex() : m_context(-1), m_message(-1) {}
+ DataIndex(int context, int message) : m_context(context), m_message(message) {}
+ int context() const { return m_context; }
+ int message() const { return m_message; }
+ bool isValid() const { return m_context >= 0; }
+protected:
+ int m_context;
+ int m_message;
+};
+
+
+class DataModelIterator : public DataIndex
+{
+public:
+ DataModelIterator(DataModel *model, int contextNo = 0, int messageNo = 0);
+ MessageItem *current() const;
+ bool isValid() const;
+ void operator++();
+private:
+ DataModelIterator() {}
+ DataModel *m_model; // not owned
+};
+
+
+class DataModel : public QObject
+{
+ Q_OBJECT
+public:
+ DataModel(QObject *parent = 0);
+
+ enum FindLocation { NoLocation = 0, SourceText = 0x1, Translations = 0x2, Comments = 0x4 };
+
+ // Specializations
+ int contextCount() const { return m_contextList.count(); }
+ ContextItem *findContext(const QString &context) const;
+ MessageItem *findMessage(const QString &context, const QString &sourcetext,
+ const QString &comment) const;
+
+ ContextItem *contextItem(int index) const;
+ MessageItem *messageItem(const DataIndex &index) const;
+
+ int messageCount() const { return m_numMessages; }
+ bool isEmpty() const { return m_numMessages == 0; }
+ bool isModified() const { return m_modified; }
+ void setModified(bool dirty);
+ bool isWritable() const { return m_writable; }
+ void setWritable(bool writable) { m_writable = writable; }
+
+ bool isWellMergeable(const DataModel *other) const;
+ bool load(const QString &fileName, bool *langGuessed, QWidget *parent);
+ bool save(QWidget *parent) { return save(m_srcFileName, parent); }
+ bool saveAs(const QString &newFileName, QWidget *parent);
+ bool release(const QString &fileName, bool verbose,
+ bool ignoreUnfinished, TranslatorSaveMode mode, QWidget *parent);
+ QString srcFileName(bool pretty = false) const
+ { return pretty ? prettifyPlainFileName(m_srcFileName) : m_srcFileName; }
+
+ static QString prettifyPlainFileName(const QString &fn);
+ static QString prettifyFileName(const QString &fn);
+
+ bool setLanguageAndCountry(QLocale::Language lang, QLocale::Country country);
+ QLocale::Language language() const { return m_language; }
+ QLocale::Country country() const { return m_country; }
+ void setSourceLanguageAndCountry(QLocale::Language lang, QLocale::Country country);
+ QLocale::Language sourceLanguage() const { return m_sourceLanguage; }
+ QLocale::Country sourceCountry() const { return m_sourceCountry; }
+
+ const QString &localizedLanguage() const { return m_localizedLanguage; }
+ const QStringList &numerusForms() const { return m_numerusForms; }
+ const QList<bool> &countRefNeeds() const { return m_countRefNeeds; }
+
+ QStringList normalizedTranslations(const MessageItem &m) const;
+ void doCharCounting(const QString& text, int& trW, int& trC, int& trCS);
+ void updateStatistics();
+
+ int getSrcWords() const { return m_srcWords; }
+ int getSrcChars() const { return m_srcChars; }
+ int getSrcCharsSpc() const { return m_srcCharsSpc; }
+
+signals:
+ void statsChanged(int words, int characters, int cs, int words2, int characters2, int cs2);
+ void progressChanged(int finishedCount, int oldFinishedCount);
+ void languageChanged();
+ void modifiedChanged();
+
+private:
+ friend class DataModelIterator;
+ QList<ContextItem> m_contextList;
+
+ bool save(const QString &fileName, QWidget *parent);
+ void updateLocale();
+
+ bool m_writable;
+ bool m_modified;
+
+ int m_numMessages;
+
+ // For statistics
+ int m_srcWords;
+ int m_srcChars;
+ int m_srcCharsSpc;
+
+ QString m_srcFileName;
+ QLocale::Language m_language;
+ QLocale::Language m_sourceLanguage;
+ QLocale::Country m_country;
+ QLocale::Country m_sourceCountry;
+ QByteArray m_codecName;
+ bool m_relativeLocations;
+ Translator::ExtraData m_extra;
+
+ QString m_localizedLanguage;
+ QStringList m_numerusForms;
+ QList<bool> m_countRefNeeds;
+};
+
+
+struct MultiMessageItem
+{
+public:
+ MultiMessageItem(const MessageItem *m);
+ QString text() const { return m_text; }
+ QString pluralText() const { return m_pluralText; }
+ QString comment() const { return m_comment; }
+ bool isEmpty() const { return !m_nonnullCount; }
+ // The next two include also read-only
+ bool isObsolete() const { return m_nonnullCount && !m_nonobsoleteCount; }
+ int countNonobsolete() const { return m_nonobsoleteCount; }
+ // The next three include only read-write
+ int countEditable() const { return m_editableCount; }
+ bool isUnfinished() const { return m_unfinishedCount != 0; }
+ int countUnfinished() const { return m_unfinishedCount; }
+
+private:
+ friend class MultiDataModel;
+ void incrementNonnullCount() { ++m_nonnullCount; }
+ void decrementNonnullCount() { --m_nonnullCount; }
+ void incrementNonobsoleteCount() { ++m_nonobsoleteCount; }
+ void decrementNonobsoleteCount() { --m_nonobsoleteCount; }
+ void incrementEditableCount() { ++m_editableCount; }
+ void decrementEditableCount() { --m_editableCount; }
+ void incrementUnfinishedCount() { ++m_unfinishedCount; }
+ void decrementUnfinishedCount() { --m_unfinishedCount; }
+
+ QString m_text;
+ QString m_pluralText;
+ QString m_comment;
+ int m_nonnullCount; // all
+ int m_nonobsoleteCount; // all
+ int m_editableCount; // read-write
+ int m_unfinishedCount; // read-write
+};
+
+struct MultiContextItem
+{
+public:
+ MultiContextItem(int oldCount, ContextItem *ctx, bool writable);
+
+ ContextItem *contextItem(int model) const { return m_contextList[model]; }
+
+ MultiMessageItem *multiMessageItem(int msgIdx) const
+ { return const_cast<MultiMessageItem *>(&m_multiMessageList[msgIdx]); }
+ MessageItem *messageItem(int model, int msgIdx) const { return m_messageLists[model][msgIdx]; }
+ int firstNonobsoleteMessageIndex(int msgIdx) const;
+ int findMessage(const QString &sourcetext, const QString &comment) const;
+
+ QString context() const { return m_context; }
+ QString comment() const { return m_comment; }
+ int messageCount() const { return m_messageLists.isEmpty() ? 0 : m_messageLists[0].count(); }
+ // For item count in context list
+ int getNumFinished() const { return m_finishedCount; }
+ int getNumEditable() const { return m_editableCount; }
+ // For background in context list
+ bool isObsolete() const { return messageCount() && !m_nonobsoleteCount; }
+
+private:
+ friend class MultiDataModel;
+ void appendEmptyModel();
+ void assignLastModel(ContextItem *ctx, bool writable);
+ void removeModel(int pos);
+ void moveModel(int oldPos, int newPos); // newPos is *before* removing at oldPos
+ void putMessageItem(int pos, MessageItem *m);
+ void appendMessageItems(const QList<MessageItem *> &m);
+ void removeMultiMessageItem(int pos);
+ void incrementFinishedCount() { ++m_finishedCount; }
+ void decrementFinishedCount() { --m_finishedCount; }
+ void incrementEditableCount() { ++m_editableCount; }
+ void decrementEditableCount() { --m_editableCount; }
+ void incrementNonobsoleteCount() { ++m_nonobsoleteCount; }
+ void decrementNonobsoleteCount() { --m_nonobsoleteCount; }
+
+ QString m_context;
+ QString m_comment;
+ QList<MultiMessageItem> m_multiMessageList;
+ QList<ContextItem *> m_contextList;
+ // The next two could be in the MultiMessageItems, but are here for efficiency
+ QList<QList<MessageItem *> > m_messageLists;
+ QList<QList<MessageItem *> *> m_writableMessageLists;
+ int m_finishedCount; // read-write
+ int m_editableCount; // read-write
+ int m_nonobsoleteCount; // all (note: this counts messages, not multi-messages)
+};
+
+
+class MultiDataIndex
+{
+public:
+ MultiDataIndex() : m_model(-1), m_context(-1), m_message(-1) {}
+ MultiDataIndex(int model, int context, int message)
+ : m_model(model), m_context(context), m_message(message) {}
+ void setModel(int model) { m_model = model; }
+ int model() const { return m_model; }
+ int context() const { return m_context; }
+ int message() const { return m_message; }
+ bool isValid() const { return m_context >= 0; }
+ bool operator==(const MultiDataIndex &other) const
+ { return m_model == other.m_model && m_context == other.m_context && m_message == other.m_message; }
+ bool operator!=(const MultiDataIndex &other) const { return !(*this == other); }
+protected:
+ int m_model;
+ int m_context;
+ int m_message;
+};
+
+
+class MultiDataModelIterator : public MultiDataIndex
+{
+public:
+ MultiDataModelIterator(MultiDataModel *model, int modelNo, int contextNo = 0, int messageNo = 0);
+ MessageItem *current() const;
+ bool isValid() const;
+ void operator++();
+private:
+ MultiDataModelIterator() {}
+ MultiDataModel *m_dataModel; // not owned
+};
+
+
+class MessageModel;
+
+class MultiDataModel : public QObject
+{
+ Q_OBJECT
+
+public:
+ MultiDataModel(QObject *parent = 0);
+ ~MultiDataModel();
+
+ bool isWellMergeable(const DataModel *dm) const;
+ void append(DataModel *dm, bool readWrite);
+ bool save(int model, QWidget *parent) { return m_dataModels[model]->save(parent); }
+ bool saveAs(int model, const QString &newFileName, QWidget *parent)
+ { return m_dataModels[model]->saveAs(newFileName, parent); }
+ bool release(int model, const QString &fileName, bool verbose, bool ignoreUnfinished, TranslatorSaveMode mode, QWidget *parent)
+ { return m_dataModels[model]->release(fileName, verbose, ignoreUnfinished, mode, parent); }
+ void close(int model);
+ void closeAll();
+ int isFileLoaded(const QString &name) const;
+ void moveModel(int oldPos, int newPos); // newPos is *before* removing at oldPos; note that this does not emit update signals
+
+ // Entire multi-model
+ int modelCount() const { return m_dataModels.count(); }
+ int contextCount() const { return m_multiContextList.count(); }
+ int messageCount() const { return m_numMessages; }
+ // Next two needed for progress indicator in main window
+ int getNumFinished() const { return m_numFinished; }
+ int getNumEditable() const { return m_numEditable; }
+ bool isModified() const;
+ QStringList srcFileNames(bool pretty = false) const;
+ QString condensedSrcFileNames(bool pretty = false) const;
+
+ // Per submodel
+ QString srcFileName(int model, bool pretty = false) const { return m_dataModels[model]->srcFileName(pretty); }
+ bool isModelWritable(int model) const { return m_dataModels[model]->isWritable(); }
+ bool isModified(int model) const { return m_dataModels[model]->isModified(); }
+ void setModified(int model, bool dirty) { m_dataModels[model]->setModified(dirty); }
+ QLocale::Language language(int model) const { return m_dataModels[model]->language(); }
+ QLocale::Language sourceLanguage(int model) const { return m_dataModels[model]->sourceLanguage(); }
+
+ // Per message
+ void setTranslation(const MultiDataIndex &index, const QString &translation);
+ void setFinished(const MultiDataIndex &index, bool finished);
+ void setDanger(const MultiDataIndex &index, bool danger);
+
+ // Retrieve items
+ DataModel *model(int i) { return m_dataModels[i]; }
+ MultiContextItem *multiContextItem(int ctxIdx) const
+ { return const_cast<MultiContextItem *>(&m_multiContextList[ctxIdx]); }
+ MultiMessageItem *multiMessageItem(const MultiDataIndex &index) const
+ { return multiContextItem(index.context())->multiMessageItem(index.message()); }
+ MessageItem *messageItem(const MultiDataIndex &index, int model) const;
+ MessageItem *messageItem(const MultiDataIndex &index) const { return messageItem(index, index.model()); }
+
+ static QString condenseFileNames(const QStringList &names);
+ static QStringList prettifyFileNames(const QStringList &names);
+
+ QBrush brushForModel(int model) const;
+
+signals:
+ void modelAppended();
+ void modelDeleted(int model);
+ void allModelsDeleted();
+ void languageChanged(int model);
+ void statsChanged(int words, int characters, int cs, int words2, int characters2, int cs2);
+ void modifiedChanged(bool);
+ void multiContextDataChanged(const MultiDataIndex &index);
+ void contextDataChanged(const MultiDataIndex &index);
+ void messageDataChanged(const MultiDataIndex &index);
+ void translationChanged(const MultiDataIndex &index); // Only the primary one
+
+private slots:
+ void onModifiedChanged();
+ void onLanguageChanged();
+
+private:
+ friend class MultiDataModelIterator;
+ friend class MessageModel;
+
+ int findContextIndex(const QString &context) const;
+ MultiContextItem *findContext(const QString &context) const;
+
+ ContextItem *contextItem(const MultiDataIndex &index) const
+ { return multiContextItem(index.context())->contextItem(index.model()); }
+
+ void updateCountsOnAdd(int model, bool writable);
+ void updateCountsOnRemove(int model, bool writable);
+ void incrementFinishedCount() { ++m_numFinished; }
+ void decrementFinishedCount() { --m_numFinished; }
+ void incrementEditableCount() { ++m_numEditable; }
+ void decrementEditableCount() { --m_numEditable; }
+
+ int m_numFinished;
+ int m_numEditable;
+ int m_numMessages;
+
+ bool m_modified;
+
+ QList<MultiContextItem> m_multiContextList;
+ QList<DataModel *> m_dataModels;
+
+ MessageModel *m_msgModel;
+
+ QColor m_colors[7];
+ QBitmap m_bitmap;
+};
+
+class MessageModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ enum { SortRole = Qt::UserRole };
+
+ MessageModel(QObject *parent, MultiDataModel *data);
+
+ // QAbstractItemModel
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex& index) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+ // Convenience
+ MultiDataIndex dataIndex(const QModelIndex &index, int model) const;
+ MultiDataIndex dataIndex(const QModelIndex &index) const
+ { return dataIndex(index, index.column() - 1 < m_data->modelCount() ? index.column() - 1 : -1); }
+ QModelIndex modelIndex(const MultiDataIndex &index);
+
+private slots:
+ void reset() { QAbstractItemModel::reset(); }
+ void multiContextItemChanged(const MultiDataIndex &index);
+ void contextItemChanged(const MultiDataIndex &index);
+ void messageItemChanged(const MultiDataIndex &index);
+
+private:
+ friend class MultiDataModel;
+
+ MultiDataModel *m_data; // not owned
+};
+
+QT_END_NAMESPACE
+
+#endif // MESSAGEMODEL_H
diff --git a/src/linguist/linguist/phrase.cpp b/src/linguist/linguist/phrase.cpp
new file mode 100644
index 000000000..2f51c42bd
--- /dev/null
+++ b/src/linguist/linguist/phrase.cpp
@@ -0,0 +1,355 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "phrase.h"
+#include "translator.h"
+
+#include <QApplication>
+#include <QFile>
+#include <QFileInfo>
+#include <QMessageBox>
+#include <QRegExp>
+#include <QTextCodec>
+#include <QTextStream>
+#include <QXmlAttributes>
+#include <QXmlDefaultHandler>
+#include <QXmlParseException>
+
+QT_BEGIN_NAMESPACE
+
+static QString protect(const QString & str)
+{
+ QString p = str;
+ p.replace(QLatin1Char('&'), QLatin1String("&amp;"));
+ p.replace(QLatin1Char('\"'), QLatin1String("&quot;"));
+ p.replace(QLatin1Char('>'), QLatin1String("&gt;"));
+ p.replace(QLatin1Char('<'), QLatin1String("&lt;"));
+ p.replace(QLatin1Char('\''), QLatin1String("&apos;"));
+ return p;
+}
+
+Phrase::Phrase()
+ : shrtc(-1), m_phraseBook(0)
+{
+}
+
+Phrase::Phrase(const QString &source, const QString &target,
+ const QString &definition, int sc)
+ : shrtc(sc), s(source), t(target), d(definition),
+ m_phraseBook(0)
+{
+}
+
+Phrase::Phrase(const QString &source, const QString &target,
+ const QString &definition, PhraseBook *phraseBook)
+ : shrtc(-1), s(source), t(target), d(definition),
+ m_phraseBook(phraseBook)
+{
+}
+
+void Phrase::setSource(const QString &ns)
+{
+ if (s == ns)
+ return;
+ s = ns;
+ if (m_phraseBook)
+ m_phraseBook->phraseChanged(this);
+}
+
+void Phrase::setTarget(const QString &nt)
+{
+ if (t == nt)
+ return;
+ t = nt;
+ if (m_phraseBook)
+ m_phraseBook->phraseChanged(this);
+}
+
+void Phrase::setDefinition(const QString &nd)
+{
+ if (d == nd)
+ return;
+ d = nd;
+ if (m_phraseBook)
+ m_phraseBook->phraseChanged(this);
+}
+
+bool operator==(const Phrase &p, const Phrase &q)
+{
+ return p.source() == q.source() && p.target() == q.target() &&
+ p.definition() == q.definition() && p.phraseBook() == q.phraseBook();
+}
+
+class QphHandler : public QXmlDefaultHandler
+{
+public:
+ QphHandler(PhraseBook *phraseBook)
+ : pb(phraseBook), ferrorCount(0) { }
+
+ virtual bool startElement(const QString &namespaceURI,
+ const QString &localName, const QString &qName,
+ const QXmlAttributes &atts);
+ virtual bool endElement(const QString &namespaceURI,
+ const QString &localName, const QString &qName);
+ virtual bool characters(const QString &ch);
+ virtual bool fatalError(const QXmlParseException &exception);
+
+ QString language() const { return m_language; }
+ QString sourceLanguage() const { return m_sourceLanguage; }
+
+private:
+ PhraseBook *pb;
+ QString source;
+ QString target;
+ QString definition;
+ QString m_language;
+ QString m_sourceLanguage;
+
+ QString accum;
+ int ferrorCount;
+};
+
+bool QphHandler::startElement(const QString & /* namespaceURI */,
+ const QString & /* localName */,
+ const QString &qName,
+ const QXmlAttributes &atts)
+{
+ if (qName == QLatin1String("QPH")) {
+ m_language = atts.value(QLatin1String("language"));
+ m_sourceLanguage = atts.value(QLatin1String("sourcelanguage"));
+ } else if (qName == QLatin1String("phrase")) {
+ source.truncate(0);
+ target.truncate(0);
+ definition.truncate(0);
+ }
+ accum.truncate(0);
+ return true;
+}
+
+bool QphHandler::endElement(const QString & /* namespaceURI */,
+ const QString & /* localName */,
+ const QString &qName)
+{
+ if (qName == QLatin1String("source"))
+ source = accum;
+ else if (qName == QLatin1String("target"))
+ target = accum;
+ else if (qName == QLatin1String("definition"))
+ definition = accum;
+ else if (qName == QLatin1String("phrase"))
+ pb->m_phrases.append(new Phrase(source, target, definition, pb));
+ return true;
+}
+
+bool QphHandler::characters(const QString &ch)
+{
+ accum += ch;
+ return true;
+}
+
+bool QphHandler::fatalError(const QXmlParseException &exception)
+{
+ if (ferrorCount++ == 0) {
+ QString msg = PhraseBook::tr("Parse error at line %1, column %2 (%3).")
+ .arg(exception.lineNumber()).arg(exception.columnNumber())
+ .arg(exception.message());
+ QMessageBox::information(0,
+ QObject::tr("Qt Linguist"), msg);
+ }
+ return false;
+}
+
+PhraseBook::PhraseBook() :
+ m_changed(false),
+ m_language(QLocale::C),
+ m_sourceLanguage(QLocale::C),
+ m_country(QLocale::AnyCountry),
+ m_sourceCountry(QLocale::AnyCountry)
+{
+}
+
+PhraseBook::~PhraseBook()
+{
+ qDeleteAll(m_phrases);
+}
+
+void PhraseBook::setLanguageAndCountry(QLocale::Language lang, QLocale::Country country)
+{
+ if (m_language == lang && m_country == country)
+ return;
+ m_language = lang;
+ m_country = country;
+ setModified(true);
+}
+
+void PhraseBook::setSourceLanguageAndCountry(QLocale::Language lang, QLocale::Country country)
+{
+ if (m_sourceLanguage == lang && m_sourceCountry == country)
+ return;
+ m_sourceLanguage = lang;
+ m_sourceCountry = country;
+ setModified(true);
+}
+
+bool PhraseBook::load(const QString &fileName, bool *langGuessed)
+{
+ QFile f(fileName);
+ if (!f.open(QIODevice::ReadOnly))
+ return false;
+
+ m_fileName = fileName;
+
+ QXmlInputSource in(&f);
+ QXmlSimpleReader reader;
+ // don't click on these!
+ reader.setFeature(QLatin1String("http://xml.org/sax/features/namespaces"), false);
+ reader.setFeature(QLatin1String("http://xml.org/sax/features/namespace-prefixes"), true);
+ reader.setFeature(QLatin1String("http://trolltech.com/xml/features/report-whitespace"
+ "-only-CharData"), false);
+ QphHandler *hand = new QphHandler(this);
+ reader.setContentHandler(hand);
+ reader.setErrorHandler(hand);
+
+ bool ok = reader.parse(in);
+ reader.setContentHandler(0);
+ reader.setErrorHandler(0);
+
+ Translator::languageAndCountry(hand->language(), &m_language, &m_country);
+ *langGuessed = false;
+ if (m_language == QLocale::C) {
+ QLocale sys;
+ m_language = sys.language();
+ m_country = sys.country();
+ *langGuessed = true;
+ }
+
+ QString lang = hand->sourceLanguage();
+ if (lang.isEmpty()) {
+ m_sourceLanguage = QLocale::C;
+ m_sourceCountry = QLocale::AnyCountry;
+ } else {
+ Translator::languageAndCountry(lang, &m_sourceLanguage, &m_sourceCountry);
+ }
+
+ delete hand;
+ f.close();
+ if (!ok) {
+ qDeleteAll(m_phrases);
+ m_phrases.clear();
+ } else {
+ emit listChanged();
+ }
+
+ return ok;
+}
+
+bool PhraseBook::save(const QString &fileName)
+{
+ QFile f(fileName);
+ if (!f.open(QIODevice::WriteOnly))
+ return false;
+
+ m_fileName = fileName;
+
+ QTextStream t(&f);
+ t.setCodec( QTextCodec::codecForName("UTF-8") );
+
+ t << "<!DOCTYPE QPH>\n<QPH";
+ if (sourceLanguage() != QLocale::C)
+ t << " sourcelanguage=\""
+ << Translator::makeLanguageCode(sourceLanguage(), sourceCountry()) << '"';
+ if (language() != QLocale::C)
+ t << " language=\"" << Translator::makeLanguageCode(language(), country()) << '"';
+ t << ">\n";
+ foreach (Phrase *p, m_phrases) {
+ t << "<phrase>\n";
+ t << " <source>" << protect( p->source() ) << "</source>\n";
+ t << " <target>" << protect( p->target() ) << "</target>\n";
+ if (!p->definition().isEmpty())
+ t << " <definition>" << protect( p->definition() )
+ << "</definition>\n";
+ t << "</phrase>\n";
+ }
+ t << "</QPH>\n";
+ f.close();
+ setModified(false);
+ return true;
+}
+
+void PhraseBook::append(Phrase *phrase)
+{
+ m_phrases.append(phrase);
+ phrase->setPhraseBook(this);
+ setModified(true);
+ emit listChanged();
+}
+
+void PhraseBook::remove(Phrase *phrase)
+{
+ m_phrases.removeOne(phrase);
+ phrase->setPhraseBook(0);
+ setModified(true);
+ emit listChanged();
+}
+
+void PhraseBook::setModified(bool modified)
+ {
+ if (m_changed != modified) {
+ emit modifiedChanged(modified);
+ m_changed = modified;
+ }
+}
+
+void PhraseBook::phraseChanged(Phrase *p)
+{
+ Q_UNUSED(p);
+
+ setModified(true);
+}
+
+QString PhraseBook::friendlyPhraseBookName() const
+{
+ if (!m_fileName.isEmpty())
+ return QFileInfo(m_fileName).fileName();
+ return QString();
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/phrase.h b/src/linguist/linguist/phrase.h
new file mode 100644
index 000000000..a46165c46
--- /dev/null
+++ b/src/linguist/linguist/phrase.h
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PHRASE_H
+#define PHRASE_H
+
+#include <QObject>
+#include <QString>
+#include <QList>
+#include <QtCore/QLocale>
+
+QT_BEGIN_NAMESPACE
+
+class PhraseBook;
+
+class Phrase
+{
+public:
+ Phrase();
+ Phrase(const QString &source, const QString &target,
+ const QString &definition, int sc = -1);
+ Phrase(const QString &source, const QString &target,
+ const QString &definition, PhraseBook *phraseBook);
+
+ QString source() const { return s; }
+ void setSource(const QString &ns);
+ QString target() const {return t;}
+ void setTarget(const QString &nt);
+ QString definition() const {return d;}
+ void setDefinition (const QString &nd);
+ int shortcut() const { return shrtc; }
+ PhraseBook *phraseBook() const { return m_phraseBook; }
+ void setPhraseBook(PhraseBook *book) { m_phraseBook = book; }
+
+private:
+ int shrtc;
+ QString s;
+ QString t;
+ QString d;
+ PhraseBook *m_phraseBook;
+};
+
+bool operator==(const Phrase &p, const Phrase &q);
+inline bool operator!=(const Phrase &p, const Phrase &q) {
+ return !(p == q);
+}
+
+class QphHandler;
+
+class PhraseBook : public QObject
+{
+ Q_OBJECT
+
+public:
+ PhraseBook();
+ ~PhraseBook();
+ bool load(const QString &fileName, bool *langGuessed);
+ bool save(const QString &fileName);
+ QList<Phrase *> phrases() const { return m_phrases; }
+ void append(Phrase *phrase);
+ void remove(Phrase *phrase);
+ QString fileName() const { return m_fileName; }
+ QString friendlyPhraseBookName() const;
+ bool isModified() const { return m_changed; }
+
+ void setLanguageAndCountry(QLocale::Language lang, QLocale::Country country);
+ QLocale::Language language() const { return m_language; }
+ QLocale::Country country() const { return m_country; }
+ void setSourceLanguageAndCountry(QLocale::Language lang, QLocale::Country country);
+ QLocale::Language sourceLanguage() const { return m_sourceLanguage; }
+ QLocale::Country sourceCountry() const { return m_sourceCountry; }
+
+signals:
+ void modifiedChanged(bool changed);
+ void listChanged();
+
+private:
+ // Prevent copying
+ PhraseBook(const PhraseBook &);
+ PhraseBook& operator=(const PhraseBook &);
+
+ void setModified(bool modified);
+ void phraseChanged(Phrase *phrase);
+
+ QList<Phrase *> m_phrases;
+ QString m_fileName;
+ bool m_changed;
+
+ QLocale::Language m_language;
+ QLocale::Language m_sourceLanguage;
+ QLocale::Country m_country;
+ QLocale::Country m_sourceCountry;
+
+ friend class QphHandler;
+ friend class Phrase;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/linguist/linguist/phrasebookbox.cpp b/src/linguist/linguist/phrasebookbox.cpp
new file mode 100644
index 000000000..32d707040
--- /dev/null
+++ b/src/linguist/linguist/phrasebookbox.cpp
@@ -0,0 +1,242 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/* TRANSLATOR PhraseBookBox
+
+ Go to Phrase > Edit Phrase Book... The dialog that pops up is a
+ PhraseBookBox.
+*/
+
+#include "phrasebookbox.h"
+#include "translationsettingsdialog.h"
+
+#include <QtEvents>
+#include <QLineEdit>
+#include <QMessageBox>
+#include <QHeaderView>
+#include <QSortFilterProxyModel>
+
+QT_BEGIN_NAMESPACE
+
+PhraseBookBox::PhraseBookBox(PhraseBook *phraseBook, QWidget *parent)
+ : QDialog(parent),
+ m_phraseBook(phraseBook),
+ m_translationSettingsDialog(0)
+{
+
+// This definition needs to be within class context for lupdate to find it
+#define NewPhrase tr("(New Entry)")
+
+ setupUi(this);
+ setWindowTitle(tr("%1[*] - Qt Linguist").arg(m_phraseBook->friendlyPhraseBookName()));
+ setWindowModified(m_phraseBook->isModified());
+
+ phrMdl = new PhraseModel(this);
+
+ m_sortedPhraseModel = new QSortFilterProxyModel(this);
+ m_sortedPhraseModel->setSortCaseSensitivity(Qt::CaseInsensitive);
+ m_sortedPhraseModel->setSortLocaleAware(true);
+ m_sortedPhraseModel->setDynamicSortFilter(true);
+ m_sortedPhraseModel->setSourceModel(phrMdl);
+
+ phraseList->setModel(m_sortedPhraseModel);
+ phraseList->header()->setDefaultSectionSize(150);
+ phraseList->header()->setResizeMode(QHeaderView::Interactive);
+
+ connect(sourceLed, SIGNAL(textChanged(QString)),
+ this, SLOT(sourceChanged(QString)));
+ connect(targetLed, SIGNAL(textChanged(QString)),
+ this, SLOT(targetChanged(QString)));
+ connect(definitionLed, SIGNAL(textChanged(QString)),
+ this, SLOT(definitionChanged(QString)));
+ connect(phraseList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(selectionChanged()));
+ connect(newBut, SIGNAL(clicked()), this, SLOT(newPhrase()));
+ connect(removeBut, SIGNAL(clicked()), this, SLOT(removePhrase()));
+ connect(settingsBut, SIGNAL(clicked()), this, SLOT(settings()));
+ connect(saveBut, SIGNAL(clicked()), this, SLOT(save()));
+ connect(m_phraseBook, SIGNAL(modifiedChanged(bool)), this, SLOT(setWindowModified(bool)));
+
+ sourceLed->installEventFilter(this);
+ targetLed->installEventFilter(this);
+ definitionLed->installEventFilter(this);
+
+ foreach (Phrase *p, phraseBook->phrases())
+ phrMdl->addPhrase(p);
+
+ phraseList->sortByColumn(0, Qt::AscendingOrder);
+
+ enableDisable();
+}
+
+bool PhraseBookBox::eventFilter(QObject *obj, QEvent *event)
+{
+ if (event->type() == QEvent::KeyPress &&
+ (obj == sourceLed || obj == targetLed || obj == definitionLed))
+ {
+ const QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
+ const int key = keyEvent->key();
+
+ switch (key) {
+ case Qt::Key_Down:
+ case Qt::Key_Up:
+ case Qt::Key_PageDown:
+ case Qt::Key_PageUp:
+ return QApplication::sendEvent(phraseList, event);
+ }
+ }
+ return QDialog::eventFilter(obj, event);
+}
+
+void PhraseBookBox::newPhrase()
+{
+ Phrase *p = new Phrase();
+ p->setSource(NewPhrase);
+ m_phraseBook->append(p);
+ selectItem(phrMdl->addPhrase(p));
+}
+
+void PhraseBookBox::removePhrase()
+{
+ QModelIndex index = currentPhraseIndex();
+ Phrase *phrase = phrMdl->phrase(index);
+ m_phraseBook->remove(phrase);
+ phrMdl->removePhrase(index);
+ delete phrase;
+}
+
+void PhraseBookBox::settings()
+{
+ if (!m_translationSettingsDialog)
+ m_translationSettingsDialog = new TranslationSettingsDialog(this);
+ m_translationSettingsDialog->setPhraseBook(m_phraseBook);
+ m_translationSettingsDialog->exec();
+}
+
+void PhraseBookBox::save()
+{
+ const QString &fileName = m_phraseBook->fileName();
+ if (!m_phraseBook->save(fileName))
+ QMessageBox::warning(this,
+ tr("Qt Linguist"),
+ tr("Cannot save phrase book '%1'.").arg(fileName));
+}
+
+void PhraseBookBox::sourceChanged(const QString& source)
+{
+ QModelIndex index = currentPhraseIndex();
+ if (index.isValid())
+ phrMdl->setData(phrMdl->index(index.row(), 0), source);
+}
+
+void PhraseBookBox::targetChanged(const QString& target)
+{
+ QModelIndex index = currentPhraseIndex();
+ if (index.isValid())
+ phrMdl->setData(phrMdl->index(index.row(), 1), target);
+}
+
+void PhraseBookBox::definitionChanged(const QString& definition)
+{
+ QModelIndex index = currentPhraseIndex();
+ if (index.isValid())
+ phrMdl->setData(phrMdl->index(index.row(), 2), definition);
+}
+
+void PhraseBookBox::selectionChanged()
+{
+ enableDisable();
+}
+
+void PhraseBookBox::selectItem(const QModelIndex &index)
+{
+ const QModelIndex &sortedIndex = m_sortedPhraseModel->mapFromSource(index);
+ phraseList->scrollTo(sortedIndex);
+ phraseList->setCurrentIndex(sortedIndex);
+}
+
+void PhraseBookBox::enableDisable()
+{
+ QModelIndex index = currentPhraseIndex();
+
+ sourceLed->blockSignals(true);
+ targetLed->blockSignals(true);
+ definitionLed->blockSignals(true);
+
+ bool indexValid = index.isValid();
+
+ if (indexValid) {
+ Phrase *p = phrMdl->phrase(index);
+ sourceLed->setText(p->source().simplified());
+ targetLed->setText(p->target().simplified());
+ definitionLed->setText(p->definition());
+ }
+ else {
+ sourceLed->setText(QString());
+ targetLed->setText(QString());
+ definitionLed->setText(QString());
+ }
+
+ sourceLed->setEnabled(indexValid);
+ targetLed->setEnabled(indexValid);
+ definitionLed->setEnabled(indexValid);
+ removeBut->setEnabled(indexValid);
+
+ sourceLed->blockSignals(false);
+ targetLed->blockSignals(false);
+ definitionLed->blockSignals(false);
+
+ QWidget *f = QApplication::focusWidget();
+ if (f != sourceLed && f != targetLed && f != definitionLed) {
+ QLineEdit *led = (sourceLed->text() == NewPhrase ? sourceLed : targetLed);
+ led->setFocus();
+ led->selectAll();
+ } else {
+ static_cast<QLineEdit*>(f)->selectAll();
+ }
+}
+
+QModelIndex PhraseBookBox::currentPhraseIndex() const
+{
+ return m_sortedPhraseModel->mapToSource(phraseList->currentIndex());
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/phrasebookbox.h b/src/linguist/linguist/phrasebookbox.h
new file mode 100644
index 000000000..3cf2ce8a3
--- /dev/null
+++ b/src/linguist/linguist/phrasebookbox.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PHRASEBOOKBOX_H
+#define PHRASEBOOKBOX_H
+
+#include "ui_phrasebookbox.h"
+#include "phrase.h"
+#include "phrasemodel.h"
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+
+class TranslationSettingsDialog;
+
+class QSortFilterProxyModel;
+
+class PhraseBookBox : public QDialog, public Ui::PhraseBookBox
+{
+ Q_OBJECT
+public:
+ PhraseBookBox(PhraseBook *phraseBook, QWidget *parent = 0);
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *event);
+
+private slots:
+ void newPhrase();
+ void removePhrase();
+ void settings();
+ void save();
+ void sourceChanged(const QString &source);
+ void targetChanged(const QString &target);
+ void definitionChanged(const QString &definition);
+ void selectionChanged();
+
+private:
+ void selectItem(const QModelIndex &index);
+ void enableDisable();
+ QModelIndex currentPhraseIndex() const;
+
+ QString fn;
+ PhraseBook *m_phraseBook;
+ PhraseModel *phrMdl;
+ QSortFilterProxyModel *m_sortedPhraseModel;
+ TranslationSettingsDialog *m_translationSettingsDialog;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/linguist/linguist/phrasebookbox.ui b/src/linguist/linguist/phrasebookbox.ui
new file mode 100644
index 000000000..5c29d5ea1
--- /dev/null
+++ b/src/linguist/linguist/phrasebookbox.ui
@@ -0,0 +1,236 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <comment>*********************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+*********************************************************************</comment>
+ <class>PhraseBookBox</class>
+ <widget class="QDialog" name="PhraseBookBox">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>596</width>
+ <height>454</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Edit Phrase Book</string>
+ </property>
+ <property name="whatsThis">
+ <string>This window allows you to add, modify, or delete entries in a phrase book.</string>
+ </property>
+ <layout class="QHBoxLayout" name="hboxLayout">
+ <item>
+ <layout class="QVBoxLayout" name="inputsLayout">
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="0">
+ <widget class="QLabel" name="target">
+ <property name="text">
+ <string>&amp;Translation:</string>
+ </property>
+ <property name="buddy">
+ <cstring>targetLed</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="targetLed">
+ <property name="whatsThis">
+ <string>This is the phrase in the target language corresponding to the source phrase.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="source">
+ <property name="text">
+ <string>S&amp;ource phrase:</string>
+ </property>
+ <property name="buddy">
+ <cstring>sourceLed</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="definitionLed">
+ <property name="whatsThis">
+ <string>This is a definition for the source phrase.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="sourceLed">
+ <property name="whatsThis">
+ <string>This is the phrase in the source language.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="definition">
+ <property name="text">
+ <string>&amp;Definition:</string>
+ </property>
+ <property name="buddy">
+ <cstring>definitionLed</cstring>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QTreeView" name="phraseList">
+ <property name="rootIsDecorated">
+ <bool>false</bool>
+ </property>
+ <property name="uniformRowHeights">
+ <bool>true</bool>
+ </property>
+ <property name="itemsExpandable">
+ <bool>false</bool>
+ </property>
+ <property name="sortingEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="expandsOnDoubleClick">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="buttonLayout">
+ <item>
+ <widget class="QPushButton" name="newBut">
+ <property name="whatsThis">
+ <string>Click here to add the phrase to the phrase book.</string>
+ </property>
+ <property name="text">
+ <string>&amp;New Entry</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="removeBut">
+ <property name="whatsThis">
+ <string>Click here to remove the entry from the phrase book.</string>
+ </property>
+ <property name="text">
+ <string>&amp;Remove Entry</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="settingsBut">
+ <property name="text">
+ <string>Settin&amp;gs...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="saveBut">
+ <property name="whatsThis">
+ <string>Click here to save the changes made.</string>
+ </property>
+ <property name="text">
+ <string>&amp;Save</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="closeBut">
+ <property name="whatsThis">
+ <string>Click here to close this window.</string>
+ </property>
+ <property name="text">
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="spacer1">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>51</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <tabstops>
+ <tabstop>sourceLed</tabstop>
+ <tabstop>targetLed</tabstop>
+ <tabstop>definitionLed</tabstop>
+ <tabstop>newBut</tabstop>
+ <tabstop>removeBut</tabstop>
+ <tabstop>saveBut</tabstop>
+ <tabstop>closeBut</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>closeBut</sender>
+ <signal>clicked()</signal>
+ <receiver>PhraseBookBox</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>545</x>
+ <y>166</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>545</x>
+ <y>199</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/linguist/linguist/phrasemodel.cpp b/src/linguist/linguist/phrasemodel.cpp
new file mode 100644
index 000000000..e28b64927
--- /dev/null
+++ b/src/linguist/linguist/phrasemodel.cpp
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "phrasemodel.h"
+
+QT_BEGIN_NAMESPACE
+
+void PhraseModel::removePhrases()
+{
+ int r = plist.count();
+ if (r > 0) {
+ plist.clear();
+ reset();
+ }
+}
+
+Phrase *PhraseModel::phrase(const QModelIndex &index) const
+{
+ return plist.at(index.row());
+}
+
+void PhraseModel::setPhrase(const QModelIndex &indx, Phrase *ph)
+{
+ int r = indx.row();
+
+ plist[r] = ph;
+
+ // update item in view
+ const QModelIndex &si = index(r, 0);
+ const QModelIndex &ei = index(r, 2);
+ emit dataChanged(si, ei);
+}
+
+QModelIndex PhraseModel::addPhrase(Phrase *p)
+{
+ int r = plist.count();
+
+ plist.append(p);
+
+ // update phrases as we add them
+ beginInsertRows(QModelIndex(), r, r);
+ QModelIndex i = index(r, 0);
+ endInsertRows();
+ return i;
+}
+
+void PhraseModel::removePhrase(const QModelIndex &index)
+{
+ int r = index.row();
+ beginRemoveRows(QModelIndex(), r, r);
+ plist.removeAt(r);
+ endRemoveRows();
+}
+
+QModelIndex PhraseModel::index(Phrase * const phr) const
+{
+ int row;
+ if ((row = plist.indexOf(phr)) == -1)
+ return QModelIndex();
+
+ return index(row, 0);
+}
+
+int PhraseModel::rowCount(const QModelIndex &) const
+{
+ return plist.count();
+}
+
+int PhraseModel::columnCount(const QModelIndex &) const
+{
+ return 3;
+}
+
+QVariant PhraseModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if ((role == Qt::DisplayRole) && (orientation == Qt::Horizontal)) {
+ switch(section) {
+ case 0:
+ return tr("Source phrase");
+ case 1:
+ return tr("Translation");
+ case 2:
+ return tr("Definition");
+ }
+ }
+
+ return QVariant();
+}
+
+Qt::ItemFlags PhraseModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return 0;
+ Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
+ // Edit is allowed for source & translation if item is from phrasebook
+ if (plist.at(index.row())->phraseBook()
+ && (index.column() != 2))
+ flags |= Qt::ItemIsEditable;
+ return flags;
+}
+
+bool PhraseModel::setData(const QModelIndex & index, const QVariant & value, int role)
+{
+ int row = index.row();
+ int column = index.column();
+
+ if (!index.isValid() || row >= plist.count() || role != Qt::EditRole)
+ return false;
+
+ Phrase *phrase = plist.at(row);
+
+ switch (column) {
+ case 0:
+ phrase->setSource(value.toString());
+ break;
+ case 1:
+ phrase->setTarget(value.toString());
+ break;
+ case 2:
+ phrase->setDefinition(value.toString());
+ break;
+ default:
+ return false;
+ }
+
+ emit dataChanged(index, index);
+ return true;
+}
+
+QVariant PhraseModel::data(const QModelIndex &index, int role) const
+{
+ int row = index.row();
+ int column = index.column();
+
+ if (row >= plist.count() || !index.isValid())
+ return QVariant();
+
+ Phrase *phrase = plist.at(row);
+
+ if (role == Qt::DisplayRole || (role == Qt::ToolTipRole && column != 2)) {
+ switch (column) {
+ case 0: // source phrase
+ return phrase->source().simplified();
+ case 1: // translation
+ return phrase->target().simplified();
+ case 2: // definition
+ return phrase->definition();
+ }
+ }
+ else if (role == Qt::EditRole && column != 2) {
+ switch (column) {
+ case 0: // source phrase
+ return phrase->source();
+ case 1: // translation
+ return phrase->target();
+ }
+ }
+
+ return QVariant();
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/phrasemodel.h b/src/linguist/linguist/phrasemodel.h
new file mode 100644
index 000000000..95efa00dd
--- /dev/null
+++ b/src/linguist/linguist/phrasemodel.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PHRASEMODEL_H
+#define PHRASEMODEL_H
+
+#include "phrase.h"
+
+#include <QList>
+#include <QAbstractItemModel>
+
+QT_BEGIN_NAMESPACE
+
+class PhraseModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ PhraseModel(QObject *parent = 0)
+ : QAbstractTableModel(parent)
+ {}
+
+ void removePhrases();
+ QList<Phrase *> phraseList() const {return plist;}
+
+ QModelIndex addPhrase(Phrase *p);
+ void removePhrase(const QModelIndex &index);
+
+ Phrase *phrase(const QModelIndex &index) const;
+ void setPhrase(const QModelIndex &indx, Phrase *ph);
+ QModelIndex index(Phrase * const phr) const;
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const
+ { return QAbstractTableModel::index(row, column, parent); }
+
+ // from qabstracttablemodel
+ int rowCount(const QModelIndex &) const;
+ int columnCount(const QModelIndex &) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const;
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+ bool setData(const QModelIndex &index, const QVariant &value,
+ int role = Qt::EditRole);
+
+ // HACK: This model will be displayed in a _TreeView_
+ // which has a tendency to expand 'children' on double click
+ bool hasChildren(const QModelIndex &parent) const
+ { return !parent.isValid(); }
+
+private:
+ QList<Phrase *> plist;
+};
+
+QT_END_NAMESPACE
+
+#endif // PHRASEMODEL_H
diff --git a/src/linguist/linguist/phraseview.cpp b/src/linguist/linguist/phraseview.cpp
new file mode 100644
index 000000000..f39a5fb36
--- /dev/null
+++ b/src/linguist/linguist/phraseview.cpp
@@ -0,0 +1,272 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "globals.h"
+#include "mainwindow.h"
+#include "messagemodel.h"
+#include "phrase.h"
+#include "phraseview.h"
+#include "phrasemodel.h"
+#include "simtexth.h"
+
+#include <QHeaderView>
+#include <QKeyEvent>
+#include <QSettings>
+#include <QTreeView>
+#include <QWidget>
+#include <QDebug>
+
+
+QT_BEGIN_NAMESPACE
+
+// Maximum number of guesses to display
+static const int MaxCandidates = 5;
+
+static QString phraseViewHeaderKey()
+{
+ return settingPath("PhraseViewHeader");
+}
+
+PhraseView::PhraseView(MultiDataModel *model, QList<QHash<QString, QList<Phrase *> > > *phraseDict, QWidget *parent)
+ : QTreeView(parent),
+ m_dataModel(model),
+ m_phraseDict(phraseDict),
+ m_modelIndex(-1),
+ m_doGuesses(true)
+{
+ setObjectName(QLatin1String("phrase list view"));
+
+ m_phraseModel = new PhraseModel(this);
+
+ setModel(m_phraseModel);
+ setAlternatingRowColors(true);
+ setSelectionBehavior(QAbstractItemView::SelectRows);
+ setSelectionMode(QAbstractItemView::SingleSelection);
+ setRootIsDecorated(false);
+ setItemsExpandable(false);
+
+ for (int i = 0; i < 10; i++)
+ (void) new GuessShortcut(i, this, SLOT(guessShortcut(int)));
+
+ header()->setResizeMode(QHeaderView::Interactive);
+ header()->setClickable(true);
+ header()->restoreState(QSettings().value(phraseViewHeaderKey()).toByteArray());
+
+ connect(this, SIGNAL(activated(QModelIndex)), this, SLOT(selectPhrase(QModelIndex)));
+}
+
+PhraseView::~PhraseView()
+{
+ QSettings().setValue(phraseViewHeaderKey(), header()->saveState());
+ deleteGuesses();
+}
+
+void PhraseView::toggleGuessing()
+{
+ m_doGuesses = !m_doGuesses;
+ update();
+}
+
+void PhraseView::update()
+{
+ setSourceText(m_modelIndex, m_sourceText);
+}
+
+
+void PhraseView::contextMenuEvent(QContextMenuEvent *event)
+{
+ QModelIndex index = indexAt(event->pos());
+ if (!index.isValid())
+ return;
+
+ QMenu *contextMenu = new QMenu(this);
+
+ QAction *insertAction = new QAction(tr("Insert"), contextMenu);
+ connect(insertAction, SIGNAL(triggered()), this, SLOT(selectPhrase()));
+
+ QAction *editAction = new QAction(tr("Edit"), contextMenu);
+ connect(editAction, SIGNAL(triggered()), this, SLOT(editPhrase()));
+ editAction->setEnabled(model()->flags(index) & Qt::ItemIsEditable);
+
+ contextMenu->addAction(insertAction);
+ contextMenu->addAction(editAction);
+
+ contextMenu->exec(event->globalPos());
+ event->accept();
+}
+
+void PhraseView::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ QModelIndex index = indexAt(event->pos());
+ if (!index.isValid())
+ return;
+
+ emit phraseSelected(m_modelIndex, m_phraseModel->phrase(index)->target());
+ event->accept();
+}
+
+void PhraseView::guessShortcut(int key)
+{
+ foreach (const Phrase *phrase, m_phraseModel->phraseList())
+ if (phrase->shortcut() == key) {
+ emit phraseSelected(m_modelIndex, phrase->target());
+ return;
+ }
+}
+
+void PhraseView::selectPhrase(const QModelIndex &index)
+{
+ emit phraseSelected(m_modelIndex, m_phraseModel->phrase(index)->target());
+}
+
+void PhraseView::selectPhrase()
+{
+ emit phraseSelected(m_modelIndex, m_phraseModel->phrase(currentIndex())->target());
+}
+
+void PhraseView::editPhrase()
+{
+ edit(currentIndex());
+}
+
+static CandidateList similarTextHeuristicCandidates(MultiDataModel *model, int mi,
+ const char *text, int maxCandidates)
+{
+ QList<int> scores;
+ CandidateList candidates;
+
+ StringSimilarityMatcher stringmatcher(QString::fromLatin1(text));
+
+ for (MultiDataModelIterator it(model, mi); it.isValid(); ++it) {
+ MessageItem *m = it.current();
+ if (!m)
+ continue;
+
+ TranslatorMessage mtm = m->message();
+ if (mtm.type() == TranslatorMessage::Unfinished
+ || mtm.translation().isEmpty())
+ continue;
+
+ QString s = m->text();
+
+ int score = stringmatcher.getSimilarityScore(s);
+
+ if (candidates.count() == maxCandidates && score > scores[maxCandidates - 1])
+ candidates.removeLast();
+ if (candidates.count() < maxCandidates && score >= textSimilarityThreshold ) {
+ Candidate cand(s, mtm.translation());
+
+ int i;
+ for (i = 0; i < candidates.size(); ++i) {
+ if (score >= scores.at(i)) {
+ if (score == scores.at(i)) {
+ if (candidates.at(i) == cand)
+ goto continue_outer_loop;
+ } else {
+ break;
+ }
+ }
+ }
+ scores.insert(i, score);
+ candidates.insert(i, cand);
+ }
+ continue_outer_loop:
+ ;
+ }
+ return candidates;
+}
+
+
+void PhraseView::setSourceText(int model, const QString &sourceText)
+{
+ m_modelIndex = model;
+ m_sourceText = sourceText;
+ m_phraseModel->removePhrases();
+ deleteGuesses();
+
+ if (model < 0)
+ return;
+
+ foreach (Phrase *p, getPhrases(model, sourceText))
+ m_phraseModel->addPhrase(p);
+
+ if (!sourceText.isEmpty() && m_doGuesses) {
+ CandidateList cl = similarTextHeuristicCandidates(m_dataModel, model,
+ sourceText.toLatin1(), MaxCandidates);
+ int n = 0;
+ foreach (const Candidate &candidate, cl) {
+ QString def;
+ if (n < 9)
+ def = tr("Guess (%1)").arg(QString(QKeySequence(Qt::CTRL | (Qt::Key_0 + (n + 1)))));
+ else
+ def = tr("Guess");
+ Phrase *guess = new Phrase(candidate.source, candidate.target, def, n);
+ m_guesses.append(guess);
+ m_phraseModel->addPhrase(guess);
+ ++n;
+ }
+ }
+}
+
+QList<Phrase *> PhraseView::getPhrases(int model, const QString &source)
+{
+ QList<Phrase *> phrases;
+ QString f = MainWindow::friendlyString(source);
+ QStringList lookupWords = f.split(QLatin1Char(' '));
+
+ foreach (const QString &s, lookupWords) {
+ if (m_phraseDict->at(model).contains(s)) {
+ foreach (Phrase *p, m_phraseDict->at(model).value(s)) {
+ if (f.contains(MainWindow::friendlyString(p->source())))
+ phrases.append(p);
+ }
+ }
+ }
+ return phrases;
+}
+
+void PhraseView::deleteGuesses()
+{
+ qDeleteAll(m_guesses);
+ m_guesses.clear();
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/phraseview.h b/src/linguist/linguist/phraseview.h
new file mode 100644
index 000000000..6feedffa3
--- /dev/null
+++ b/src/linguist/linguist/phraseview.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PHRASEVIEW_H
+#define PHRASEVIEW_H
+
+#include <QList>
+#include <QShortcut>
+#include <QTreeView>
+#include "phrase.h"
+
+QT_BEGIN_NAMESPACE
+
+class MultiDataModel;
+class PhraseModel;
+
+class GuessShortcut : public QShortcut
+{
+ Q_OBJECT
+public:
+ GuessShortcut(int nkey, QWidget *parent, const char *member)
+ : QShortcut(parent), nrkey(nkey)
+ {
+ setKey(Qt::CTRL + (Qt::Key_1 + nrkey));
+ connect(this, SIGNAL(activated()), this, SLOT(keyActivated()));
+ connect(this, SIGNAL(activated(int)), parent, member);
+ }
+
+private slots:
+ void keyActivated() { emit activated(nrkey); }
+
+signals:
+ void activated(int nkey);
+
+private:
+ int nrkey;
+};
+
+class PhraseView : public QTreeView
+{
+ Q_OBJECT
+
+public:
+ PhraseView(MultiDataModel *model, QList<QHash<QString, QList<Phrase *> > > *phraseDict, QWidget *parent = 0);
+ ~PhraseView();
+ void setSourceText(int model, const QString &sourceText);
+
+public slots:
+ void toggleGuessing();
+ void update();
+
+signals:
+ void phraseSelected(int latestModel, const QString &phrase);
+
+protected:
+ // QObject
+ virtual void contextMenuEvent(QContextMenuEvent *event);
+ // QAbstractItemView
+ virtual void mouseDoubleClickEvent(QMouseEvent *event);
+
+private slots:
+ void guessShortcut(int nkey);
+ void selectPhrase(const QModelIndex &index);
+ void selectPhrase();
+ void editPhrase();
+
+private:
+ QList<Phrase *> getPhrases(int model, const QString &sourceText);
+ void deleteGuesses();
+
+ MultiDataModel *m_dataModel;
+ QList<QHash<QString, QList<Phrase *> > > *m_phraseDict;
+ QList<Phrase *> m_guesses;
+ PhraseModel *m_phraseModel;
+ QString m_sourceText;
+ int m_modelIndex;
+ bool m_doGuesses;
+};
+
+QT_END_NAMESPACE
+
+#endif // PHRASEVIEW_H
diff --git a/src/linguist/linguist/printout.cpp b/src/linguist/linguist/printout.cpp
new file mode 100644
index 000000000..342f24081
--- /dev/null
+++ b/src/linguist/linguist/printout.cpp
@@ -0,0 +1,210 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "printout.h"
+
+#include <QPrinter>
+#include <QFontMetrics>
+
+QT_BEGIN_NAMESPACE
+
+PrintOut::PrintOut(QPrinter *printer)
+ : pr(printer), nextRule(NoRule), page(0)
+{
+ p.begin(pr);
+ QFont f(QLatin1String("Arial"));
+ f8 = f;
+ f8.setPointSize(8);
+ f10 = f;
+ f10.setPointSize(10);
+ p.setFont(f10);
+ fmetrics = new QFontMetrics(p.fontMetrics());
+ hmargin = 5 * printer->width() / printer->widthMM(); // 5 mm
+ vmargin = 5 * printer->height() / printer->heightMM(); // 5 mm
+ hsize = printer->width() - 2 * hmargin;
+ vsize = printer->height() - vmargin;
+ dateTime = QDateTime::currentDateTime();
+ breakPage(true); // init vsize and draw first header
+ cp = Paragraph(QPoint(hmargin, voffset));
+}
+
+PrintOut::~PrintOut()
+{
+ flushLine();
+ delete fmetrics;
+ p.end();
+}
+
+void PrintOut::setRule(Rule rule)
+{
+ if (nextRule < rule)
+ nextRule = rule;
+}
+
+void PrintOut::setGuide(const QString &guide)
+{
+ g = guide;
+}
+
+void PrintOut::vskip()
+{
+ if (!firstParagraph)
+ voffset += 14;
+}
+
+void PrintOut::flushLine(bool /* mayBreak */)
+{
+ if (voffset + cp.rect.height() > vsize)
+ breakPage();
+ else if (!firstParagraph)
+ drawRule(nextRule);
+
+ for (int i = 0; i < cp.boxes.count(); ++i) {
+ Box b = cp.boxes[i];
+ b.rect.translate(0, voffset);
+ QRect r = b.rect;
+ p.setFont(b.font);
+ p.drawText(r, b.text, b.options);
+ }
+ voffset += cp.rect.height();
+
+ nextRule = NoRule;
+ cp = Paragraph(QPoint(hmargin, voffset));
+ firstParagraph = false;
+}
+
+void PrintOut::addBox(int percent, const QString &text, Style style, Qt::Alignment halign)
+{
+ QTextOption options;
+ options.setAlignment(halign | Qt::AlignTop);
+ options.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+ QFont f = f10;
+ if (style == Strong)
+ f.setBold(true);
+ else if (style == Emphasis)
+ f.setItalic(true);
+ int wd = hsize * percent / 100;
+ QRect r(cp.rect.x() + cp.rect.width(), 0, wd, vsize);
+ const int ht = static_cast<int>(p.boundingRect(r, text, options).height());
+
+ Box b(r, text, f, options);
+ cp.boxes.append(b);
+ cp.rect.setSize(QSize(cp.rect.width() + wd, qMax(cp.rect.height(), ht)));
+}
+
+// use init if initial vsize should be calculated (first breakPage call)
+void PrintOut::breakPage(bool init)
+{
+ static const int LeftAlign = Qt::AlignLeft | Qt::AlignTop;
+ static const int RightAlign = Qt::AlignRight | Qt::AlignTop;
+ QRect r1, r2;
+ int h1 = 0;
+ int h2 = 0;
+
+ if (page > 0)
+ pr->newPage();
+
+ if (!init)
+ page++;
+
+ voffset = 0;
+
+ p.setFont(f10);
+ r1 = QRect(hmargin, voffset, 3 * hsize / 4, vsize);
+ r2 = QRect(r1.x() + r1.width(), voffset, hsize - r1.width(), vsize);
+ h1 = p.boundingRect(r1, LeftAlign, pr->docName()).height();
+ if (!init)
+ p.drawText(r1, LeftAlign, pr->docName());
+ h2 = p.boundingRect(r2, RightAlign, QString::number(page)).height();
+ if (!init)
+ p.drawText(r2, RightAlign, QString::number(page));
+ voffset += qMax(h1, h2 );
+
+ r1 = QRect(hmargin, voffset, hsize / 2, LeftAlign);
+ p.setFont(f8);
+ h1 = p.boundingRect(r1, LeftAlign, dateTime.toString()).height();
+ if (!init)
+ p.drawText(r1, LeftAlign, dateTime.toString());
+ p.setFont(f10);
+ voffset += qMax(h1, h2);
+
+ voffset += 4;
+ if (!init)
+ p.drawLine(QPoint(hmargin, voffset), QPoint(hmargin + hsize, voffset));
+ voffset += 14;
+
+ firstParagraph = true;
+
+ if (init) {
+ vsize -= voffset;
+ breakPage(); // now draw it when the vsize is ok
+ }
+
+}
+
+void PrintOut::drawRule(Rule rule)
+{
+ QPen pen;
+
+ switch (rule) {
+ case NoRule:
+ voffset += 5;
+ break;
+ case ThinRule:
+ pen.setColor(QColor(192, 192, 192));
+ pen.setStyle(Qt::DotLine);
+ pen.setWidth(0);
+ p.setPen(pen);
+ voffset += 5;
+ p.drawLine(QPoint(hmargin, voffset),
+ QPoint(hmargin + hsize, voffset));
+ p.setPen(QPen());
+ voffset += 2;
+ break;
+ case ThickRule:
+ voffset += 7;
+ p.drawLine(QPoint(hmargin, voffset),
+ QPoint(hmargin + hsize, voffset));
+ voffset += 4;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/printout.h b/src/linguist/linguist/printout.h
new file mode 100644
index 000000000..c46c0fabf
--- /dev/null
+++ b/src/linguist/linguist/printout.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PRINTOUT_H
+#define PRINTOUT_H
+
+#include <QFont>
+#include <QPainter>
+#include <QRect>
+#include <QTextOption>
+#include <QList>
+#include <QDateTime>
+
+QT_BEGIN_NAMESPACE
+
+class QPrinter;
+class QFontMetrics;
+
+class PrintOut
+{
+public:
+ enum Rule { NoRule, ThinRule, ThickRule };
+ enum Style { Normal, Strong, Emphasis };
+
+ PrintOut(QPrinter *printer);
+ ~PrintOut();
+
+ void setRule(Rule rule);
+ void setGuide(const QString &guide);
+ void vskip();
+ void flushLine(bool mayBreak = false);
+ void addBox(int percent, const QString &text = QString(),
+ Style style = Normal,
+ Qt::Alignment halign = Qt::AlignLeft);
+
+ int pageNum() const { return page; }
+
+ struct Box
+ {
+ QRect rect;
+ QString text;
+ QFont font;
+ QTextOption options;
+
+ Box( const QRect& r, const QString& t, const QFont& f, const QTextOption &o )
+ : rect( r ), text( t ), font( f ), options( o ) { }
+ };
+
+private:
+ void breakPage(bool init = false);
+ void drawRule( Rule rule );
+
+ struct Paragraph {
+ QRect rect;
+ QList<Box> boxes;
+
+ Paragraph() { }
+ Paragraph( QPoint p ) : rect( p, QSize(0, 0) ) { }
+ };
+
+ QPrinter *pr;
+ QPainter p;
+ QFont f8;
+ QFont f10;
+ QFontMetrics *fmetrics;
+ Rule nextRule;
+ Paragraph cp;
+ int page;
+ bool firstParagraph;
+ QString g;
+ QDateTime dateTime;
+
+ int hmargin;
+ int vmargin;
+ int voffset;
+ int hsize;
+ int vsize;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/linguist/linguist/recentfiles.cpp b/src/linguist/linguist/recentfiles.cpp
new file mode 100644
index 000000000..e1d79ce24
--- /dev/null
+++ b/src/linguist/linguist/recentfiles.cpp
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "recentfiles.h"
+#include "globals.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QFileInfo>
+#include <QtCore/QSettings>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+
+QT_BEGIN_NAMESPACE
+
+static QString configKey()
+{
+ return settingPath("RecentlyOpenedFiles");
+}
+
+
+RecentFiles::RecentFiles(const int maxEntries)
+ : m_groupOpen(false),
+ m_clone1st(false),
+ m_maxEntries(maxEntries)
+{
+ m_timer.setSingleShot(true);
+ m_timer.setInterval(3 * 60 * 1000);
+ connect(&m_timer, SIGNAL(timeout()), SLOT(closeGroup()));
+}
+
+/*
+ * The logic is as follows:
+ * - The most recent (i.e., topmost) item can be open ("in flux")
+ * - The item is closed by either a timeout (3 min or so) or a
+ * "terminal action" (e.g., closing all files)
+ * - While the item is open, modifications to the set of open files
+ * will modify that item instead of creating new items
+ * - If the open item is modified to be equal to an existing item,
+ * the existing item is deleted, but will be re-created when the
+ * open item is modified even further
+ * Cases (actions in parentheses are no-ops):
+ * - identical to top item => (do nothing)
+ * - closed, new item => insert at top, (clear marker)
+ * - closed, existing item => move to top, mark for cloning
+ * - open, new item, not marked => replace top, (clear marker)
+ * - open, new item, marked => insert at top, clear marker
+ * - open, existing item, not marked => replace top, delete copy, mark for cloning
+ * - open, existing item, marked => insert at top, delete copy, (mark for cloning)
+ * - closing clears marker
+ */
+void RecentFiles::addFiles(const QStringList &names)
+{
+ if (m_strLists.isEmpty() || names != m_strLists.first()) {
+ if (m_groupOpen && !m_clone1st)
+ // Group being open implies at least one item in the list
+ m_strLists.removeFirst();
+ m_groupOpen = true;
+
+ // We do *not* sort the actual entries, as that would destroy the user's
+ // chosen arrangement. However, we do the searching on sorted lists, so
+ // we throw out (probably) obsolete arrangements.
+ QList<QStringList> sortedLists = m_strLists;
+ for (int i = 0; i < sortedLists.size(); ++i)
+ sortedLists[i].sort();
+ QStringList sortedNames = names;
+ sortedNames.sort();
+
+ int index = sortedLists.indexOf(sortedNames);
+ if (index >= 0) {
+ m_strLists.removeAt(index);
+ m_clone1st = true;
+ } else {
+ if (m_strLists.count() >= m_maxEntries)
+ m_strLists.removeLast();
+ m_clone1st = false;
+ }
+ m_strLists.prepend(names);
+ }
+ m_timer.start();
+}
+
+void RecentFiles::closeGroup()
+{
+ m_timer.stop();
+ m_groupOpen = false;
+}
+
+void RecentFiles::readConfig()
+{
+ m_strLists.clear();
+ QVariant val = QSettings().value(configKey());
+ if (val.type() == QVariant::StringList) // Backwards compat to Qt < 4.5
+ foreach (const QString &s, val.toStringList())
+ m_strLists << QStringList(QFileInfo(s).canonicalFilePath());
+ else
+ foreach (const QVariant &v, val.toList())
+ m_strLists << v.toStringList();
+}
+
+void RecentFiles::writeConfig() const
+{
+ QList<QVariant> vals;
+ foreach (const QStringList &sl, m_strLists)
+ vals << sl;
+ QSettings().setValue(configKey(), vals);
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/recentfiles.h b/src/linguist/linguist/recentfiles.h
new file mode 100644
index 000000000..09c2b407c
--- /dev/null
+++ b/src/linguist/linguist/recentfiles.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef RECENTFILES_H
+#define RECENTFILES_H
+
+#include <QString>
+#include <QStringList>
+#include <QTimer>
+
+QT_BEGIN_NAMESPACE
+
+class RecentFiles : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit RecentFiles(const int maxEntries);
+
+ bool isEmpty() { return m_strLists.isEmpty(); }
+ void addFiles(const QStringList &names);
+ QString lastOpenedFile() const {
+ if (m_strLists.isEmpty() || m_strLists.first().isEmpty())
+ return QString::null;
+ return m_strLists.at(0).at(0);
+ }
+ const QList<QStringList>& filesLists() const { return m_strLists; }
+
+ void readConfig();
+ void writeConfig() const;
+
+public slots:
+ void closeGroup();
+
+private:
+ bool m_groupOpen;
+ bool m_clone1st;
+ int m_maxEntries;
+ QList<QStringList> m_strLists;
+ QTimer m_timer;
+};
+
+QT_END_NAMESPACE
+
+#endif // RECENTFILES_H
diff --git a/src/linguist/linguist/sourcecodeview.cpp b/src/linguist/linguist/sourcecodeview.cpp
new file mode 100644
index 000000000..b6c011714
--- /dev/null
+++ b/src/linguist/linguist/sourcecodeview.cpp
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "sourcecodeview.h"
+
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QTextStream>
+
+#include <QtGui/QTextCharFormat>
+#include <QtGui/QTextBlock>
+#include <QtGui/QTextCursor>
+
+QT_BEGIN_NAMESPACE
+
+SourceCodeView::SourceCodeView(QWidget *parent)
+ : QPlainTextEdit(parent),
+ m_isActive(true),
+ m_lineNumToLoad(0)
+{
+ setReadOnly(true);
+}
+
+void SourceCodeView::setSourceContext(const QString &fileName, const int lineNum)
+{
+ m_fileToLoad.clear();
+ setToolTip(fileName);
+
+ if (fileName.isEmpty()) {
+ clear();
+ m_currentFileName.clear();
+ appendHtml(tr("<i>Source code not available</i>"));
+ return;
+ }
+
+ if (m_isActive) {
+ showSourceCode(fileName, lineNum);
+ } else {
+ m_fileToLoad = fileName;
+ m_lineNumToLoad = lineNum;
+ }
+}
+
+void SourceCodeView::setActivated(bool activated)
+{
+ m_isActive = activated;
+ if (activated && !m_fileToLoad.isEmpty()) {
+ showSourceCode(m_fileToLoad, m_lineNumToLoad);
+ m_fileToLoad.clear();
+ }
+}
+
+void SourceCodeView::showSourceCode(const QString &absFileName, const int lineNum)
+{
+ QString fileText = fileHash.value(absFileName);
+
+ if (fileText.isNull()) { // File not in hash
+ m_currentFileName.clear();
+
+ // Assume fileName is relative to directory
+ QFile file(absFileName);
+
+ if (!file.exists()) {
+ clear();
+ appendHtml(tr("<i>File %1 not available</i>").arg(absFileName));
+ return;
+ }
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ clear();
+ appendHtml(tr("<i>File %1 not readable</i>").arg(absFileName));
+ return;
+ }
+ fileText = QString::fromLatin1(file.readAll());
+ fileHash.insert(absFileName, fileText);
+ }
+
+
+ if (m_currentFileName != absFileName) {
+ setPlainText(fileText);
+ m_currentFileName = absFileName;
+ }
+
+ QTextCursor cursor = textCursor();
+ cursor.setPosition(document()->findBlockByNumber(lineNum - 1).position());
+ setTextCursor(cursor);
+ centerCursor();
+ cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
+ cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
+
+ QTextEdit::ExtraSelection selectedLine;
+ selectedLine.cursor = cursor;
+
+ // Define custom color for line selection
+ const QColor fg = palette().color(QPalette::Highlight);
+ const QColor bg = palette().color(QPalette::Base);
+ QColor col;
+ const qreal ratio = 0.25;
+ col.setRedF(fg.redF() * ratio + bg.redF() * (1 - ratio));
+ col.setGreenF(fg.greenF() * ratio + bg.greenF() * (1 - ratio));
+ col.setBlueF(fg.blueF() * ratio + bg.blueF() * (1 - ratio));
+
+ selectedLine.format.setBackground(col);
+ selectedLine.format.setProperty(QTextFormat::FullWidthSelection, true);
+ setExtraSelections(QList<QTextEdit::ExtraSelection>() << selectedLine);
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/sourcecodeview.h b/src/linguist/linguist/sourcecodeview.h
new file mode 100644
index 000000000..6f42a31cb
--- /dev/null
+++ b/src/linguist/linguist/sourcecodeview.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SOURCECODEVIEW_H
+#define SOURCECODEVIEW_H
+
+#include <QDir>
+#include <QHash>
+#include <QPlainTextEdit>
+
+QT_BEGIN_NAMESPACE
+
+class SourceCodeView : public QPlainTextEdit
+{
+ Q_OBJECT
+public:
+ SourceCodeView(QWidget *parent = 0);
+ void setSourceContext(const QString &fileName, const int lineNum);
+
+public slots:
+ void setActivated(bool activated);
+
+private:
+ void showSourceCode(const QString &fileName, const int lineNum);
+
+ bool m_isActive;
+ QString m_fileToLoad;
+ int m_lineNumToLoad;
+ QString m_currentFileName;
+
+ QHash<QString, QString> fileHash;
+};
+
+QT_END_NAMESPACE
+
+#endif // SOURCECODEVIEW_H
diff --git a/src/linguist/linguist/statistics.cpp b/src/linguist/linguist/statistics.cpp
new file mode 100644
index 000000000..821ce7ab4
--- /dev/null
+++ b/src/linguist/linguist/statistics.cpp
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "statistics.h"
+
+QT_BEGIN_NAMESPACE
+
+Statistics::Statistics(QWidget* parent, Qt::WindowFlags fl)
+ : QDialog(parent, fl)
+{
+ setupUi(this);
+}
+
+void Statistics::languageChange()
+{
+ retranslateUi(this);
+}
+
+void Statistics::updateStats(int sW,int sC,int sCS,int trW,int trC,int trCS)
+{
+ untrWords->setText(QString::number(sW));
+ untrChars->setText(QString::number(sC));
+ untrCharsSpc->setText(QString::number(sCS));
+ trWords->setText(QString::number(trW));
+ trChars->setText(QString::number(trC));
+ trCharsSpc->setText(QString::number(trCS));
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/statistics.h b/src/linguist/linguist/statistics.h
new file mode 100644
index 000000000..fe1f6cfa2
--- /dev/null
+++ b/src/linguist/linguist/statistics.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef STATISTICS_H
+#define STATISTICS_H
+
+#include "ui_statistics.h"
+#include <QVariant>
+
+QT_BEGIN_NAMESPACE
+
+class Statistics : public QDialog, public Ui::Statistics
+{
+ Q_OBJECT
+
+public:
+ Statistics(QWidget *parent = 0, Qt::WindowFlags fl = 0);
+ ~Statistics() {}
+
+public slots:
+ virtual void updateStats(int w1, int c1, int cs1, int w2, int c2, int cs2);
+
+protected slots:
+ virtual void languageChange();
+};
+
+QT_END_NAMESPACE
+
+#endif // STATISTICS_H
diff --git a/src/linguist/linguist/statistics.ui b/src/linguist/linguist/statistics.ui
new file mode 100644
index 000000000..28402b0e4
--- /dev/null
+++ b/src/linguist/linguist/statistics.ui
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <comment>*********************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+*********************************************************************</comment>
+ <class>Statistics</class>
+ <widget class="QDialog" name="linguist_stats">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>336</width>
+ <height>169</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Statistics</string>
+ </property>
+ <layout class="QGridLayout">
+ <item row="1" column="0">
+ <layout class="QHBoxLayout">
+ <item>
+ <spacer name="spacer4_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="closeBtn">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="spacer4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="0">
+ <widget class="QFrame" name="frame4">
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QGridLayout">
+ <item row="0" column="2">
+ <widget class="QLabel" name="textLabel4">
+ <property name="text">
+ <string>Translation</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="textLabel5">
+ <property name="text">
+ <string>Source</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="untrWords">
+ <property name="text">
+ <string>0</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="trWords">
+ <property name="text">
+ <string>0</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="textLabel1">
+ <property name="text">
+ <string>Words:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="trChars">
+ <property name="text">
+ <string>0</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="untrChars">
+ <property name="text">
+ <string>0</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="textLabel3">
+ <property name="text">
+ <string>Characters:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="textLabel6">
+ <property name="text">
+ <string>Characters (with spaces):</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QLabel" name="trCharsSpc">
+ <property name="text">
+ <string>0</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLabel" name="untrCharsSpc">
+ <property name="text">
+ <string>0</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>closeBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>linguist_stats</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>179</x>
+ <y>144</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>239</x>
+ <y>142</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/linguist/linguist/translatedialog.cpp b/src/linguist/linguist/translatedialog.cpp
new file mode 100644
index 000000000..94acfd5fd
--- /dev/null
+++ b/src/linguist/linguist/translatedialog.cpp
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "translatedialog.h"
+
+QT_BEGIN_NAMESPACE
+
+TranslateDialog::TranslateDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ m_ui.setupUi(this);
+ connect(m_ui.findNxt, SIGNAL(clicked()), this, SLOT(emitFindNext()));
+ connect(m_ui.translate, SIGNAL(clicked()), this, SLOT(emitTranslateAndFindNext()));
+ connect(m_ui.translateAll, SIGNAL(clicked()), this, SLOT(emitTranslateAll()));
+ connect(m_ui.ledFindWhat, SIGNAL(textChanged(QString)), SLOT(verifyText()));
+ connect(m_ui.ckMatchCase, SIGNAL(toggled(bool)), SLOT(verifyText()));
+}
+
+void TranslateDialog::showEvent(QShowEvent *)
+{
+ verifyText();
+ m_ui.ledFindWhat->setFocus();
+}
+
+void TranslateDialog::verifyText()
+{
+ QString text = m_ui.ledFindWhat->text();
+ bool canFind = !text.isEmpty();
+ bool hit = false;
+ if (canFind)
+ emit requestMatchUpdate(hit);
+ m_ui.findNxt->setEnabled(canFind);
+ m_ui.translate->setEnabled(canFind && hit);
+ m_ui.translateAll->setEnabled(canFind);
+}
+
+void TranslateDialog::emitFindNext()
+{
+ emit activated(Skip);
+}
+
+void TranslateDialog::emitTranslateAndFindNext()
+{
+ emit activated(Translate);
+}
+
+void TranslateDialog::emitTranslateAll()
+{
+ emit activated(TranslateAll);
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/translatedialog.h b/src/linguist/linguist/translatedialog.h
new file mode 100644
index 000000000..80c09a74a
--- /dev/null
+++ b/src/linguist/linguist/translatedialog.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TRANSLATEDIALOG_H
+#define TRANSLATEDIALOG_H
+
+#include "ui_translatedialog.h"
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+
+class TranslateDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ enum {
+ Skip,
+ Translate,
+ TranslateAll
+ };
+
+ TranslateDialog(QWidget *parent = 0);
+
+ bool markFinished() const { return m_ui.ckMarkFinished->isChecked(); }
+ Qt::CaseSensitivity caseSensitivity() const
+ { return m_ui.ckMatchCase->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive; }
+ QString findText() const { return m_ui.ledFindWhat->text(); }
+ QString replaceText() const { return m_ui.ledTranslateTo->text(); }
+
+signals:
+ void requestMatchUpdate(bool &hit);
+ void activated(int mode);
+
+protected:
+ virtual void showEvent(QShowEvent *event);
+
+private slots:
+ void emitFindNext();
+ void emitTranslateAndFindNext();
+ void emitTranslateAll();
+ void verifyText();
+
+private:
+ Ui::TranslateDialog m_ui;
+};
+
+
+QT_END_NAMESPACE
+#endif //TRANSLATEDIALOG_H
+
diff --git a/src/linguist/linguist/translatedialog.ui b/src/linguist/linguist/translatedialog.ui
new file mode 100644
index 000000000..7c0baa722
--- /dev/null
+++ b/src/linguist/linguist/translatedialog.ui
@@ -0,0 +1,260 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <comment>*********************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+*********************************************************************</comment>
+ <class>TranslateDialog</class>
+ <widget class="QDialog" name="translateDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>407</width>
+ <height>174</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="whatsThis">
+ <string>This window allows you to search for some text in the translation source file.</string>
+ </property>
+ <layout class="QHBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QGridLayout">
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="ledTranslateTo">
+ <property name="whatsThis">
+ <string>Type in the text to search for.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="findWhat">
+ <property name="text">
+ <string>Find &amp;source text:</string>
+ </property>
+ <property name="buddy">
+ <cstring>ledFindWhat</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="translateTo">
+ <property name="text">
+ <string>&amp;Translate to:</string>
+ </property>
+ <property name="buddy">
+ <cstring>ledTranslateTo</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="ledFindWhat">
+ <property name="whatsThis">
+ <string>Type in the text to search for.</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Search options</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QCheckBox" name="ckMatchCase">
+ <property name="whatsThis">
+ <string>Texts such as 'TeX' and 'tex' are considered as different when checked.</string>
+ </property>
+ <property name="text">
+ <string>Match &amp;case</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="ckMarkFinished">
+ <property name="text">
+ <string>Mark new translation as &amp;finished</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="findNxt">
+ <property name="whatsThis">
+ <string>Click here to find the next occurrence of the text you typed in.</string>
+ </property>
+ <property name="text">
+ <string>Find Next</string>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="translate">
+ <property name="text">
+ <string>Translate</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="translateAll">
+ <property name="text">
+ <string>Translate All</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancel">
+ <property name="whatsThis">
+ <string>Click here to close this window.</string>
+ </property>
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>51</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <tabstops>
+ <tabstop>ledFindWhat</tabstop>
+ <tabstop>ledTranslateTo</tabstop>
+ <tabstop>findNxt</tabstop>
+ <tabstop>translate</tabstop>
+ <tabstop>translateAll</tabstop>
+ <tabstop>cancel</tabstop>
+ <tabstop>ckMatchCase</tabstop>
+ <tabstop>ckMarkFinished</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>cancel</sender>
+ <signal>clicked()</signal>
+ <receiver>translateDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>360</x>
+ <y>132</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>357</x>
+ <y>151</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/linguist/linguist/translationsettings.ui b/src/linguist/linguist/translationsettings.ui
new file mode 100644
index 000000000..c868360e6
--- /dev/null
+++ b/src/linguist/linguist/translationsettings.ui
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>TranslationSettingsDialog</class>
+ <widget class="QDialog" name="translationSettingsDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>416</width>
+ <height>263</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="srcGroupBox">
+ <property name="title">
+ <string>Source language</string>
+ </property>
+ <layout class="QGridLayout" name="_2">
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="srcCbLanguageList"/>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="srcLblLanguage">
+ <property name="text">
+ <string>Language</string>
+ </property>
+ <property name="buddy">
+ <cstring>tgtCbLanguageList</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="srcCbCountryList"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="srcLblCountry">
+ <property name="text">
+ <string>Country/Region</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="tgtGroupBox">
+ <property name="title">
+ <string>Target language</string>
+ </property>
+ <layout class="QGridLayout">
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="tgtCbLanguageList"/>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="tgtLblLanguage">
+ <property name="text">
+ <string>Language</string>
+ </property>
+ <property name="buddy">
+ <cstring>tgtCbLanguageList</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="tgtCbCountryList"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="tgtLblCountry">
+ <property name="text">
+ <string>Country/Region</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>100</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>translationSettingsDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>216</x>
+ <y>241</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>222</x>
+ <y>213</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/linguist/linguist/translationsettingsdialog.cpp b/src/linguist/linguist/translationsettingsdialog.cpp
new file mode 100644
index 000000000..2049af12b
--- /dev/null
+++ b/src/linguist/linguist/translationsettingsdialog.cpp
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "translationsettingsdialog.h"
+#include "messagemodel.h"
+#include "phrase.h"
+
+#include <QtCore/QLocale>
+
+QT_BEGIN_NAMESPACE
+
+TranslationSettingsDialog::TranslationSettingsDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ m_ui.setupUi(this);
+
+ for (int i = QLocale::C + 1; i < QLocale::LastLanguage; ++i) {
+ QString lang = QLocale::languageToString(QLocale::Language(i));
+ m_ui.srcCbLanguageList->addItem(lang, QVariant(i));
+ }
+ m_ui.srcCbLanguageList->model()->sort(0, Qt::AscendingOrder);
+ m_ui.srcCbLanguageList->insertItem(0, QLatin1String("POSIX"), QVariant(QLocale::C));
+
+ m_ui.tgtCbLanguageList->setModel(m_ui.srcCbLanguageList->model());
+}
+
+void TranslationSettingsDialog::setDataModel(DataModel *dataModel)
+{
+ m_dataModel = dataModel;
+ m_phraseBook = 0;
+ QString fn = QFileInfo(dataModel->srcFileName()).baseName();
+ setWindowTitle(tr("Settings for '%1' - Qt Linguist").arg(fn));
+}
+
+void TranslationSettingsDialog::setPhraseBook(PhraseBook *phraseBook)
+{
+ m_phraseBook = phraseBook;
+ m_dataModel = 0;
+ QString fn = QFileInfo(phraseBook->fileName()).baseName();
+ setWindowTitle(tr("Settings for '%1' - Qt Linguist").arg(fn));
+}
+
+static void fillCountryCombo(const QVariant &lng, QComboBox *combo)
+{
+ combo->clear();
+ QLocale::Language lang = QLocale::Language(lng.toInt());
+ if (lang != QLocale::C) {
+ foreach (QLocale::Country cntr, QLocale::countriesForLanguage(lang)) {
+ QString country = QLocale::countryToString(cntr);
+ combo->addItem(country, QVariant(cntr));
+ }
+ combo->model()->sort(0, Qt::AscendingOrder);
+ }
+ combo->insertItem(0, TranslationSettingsDialog::tr("Any Country"), QVariant(QLocale::AnyCountry));
+ combo->setCurrentIndex(0);
+}
+
+void TranslationSettingsDialog::on_srcCbLanguageList_currentIndexChanged(int idx)
+{
+ fillCountryCombo(m_ui.srcCbLanguageList->itemData(idx), m_ui.srcCbCountryList);
+}
+
+void TranslationSettingsDialog::on_tgtCbLanguageList_currentIndexChanged(int idx)
+{
+ fillCountryCombo(m_ui.tgtCbLanguageList->itemData(idx), m_ui.tgtCbCountryList);
+}
+
+void TranslationSettingsDialog::on_buttonBox_accepted()
+{
+ int itemindex = m_ui.tgtCbLanguageList->currentIndex();
+ QVariant var = m_ui.tgtCbLanguageList->itemData(itemindex);
+ QLocale::Language lang = QLocale::Language(var.toInt());
+
+ itemindex = m_ui.tgtCbCountryList->currentIndex();
+ var = m_ui.tgtCbCountryList->itemData(itemindex);
+ QLocale::Country country = QLocale::Country(var.toInt());
+
+ itemindex = m_ui.srcCbLanguageList->currentIndex();
+ var = m_ui.srcCbLanguageList->itemData(itemindex);
+ QLocale::Language lang2 = QLocale::Language(var.toInt());
+
+ itemindex = m_ui.srcCbCountryList->currentIndex();
+ var = m_ui.srcCbCountryList->itemData(itemindex);
+ QLocale::Country country2 = QLocale::Country(var.toInt());
+
+ if (m_phraseBook) {
+ m_phraseBook->setLanguageAndCountry(lang, country);
+ m_phraseBook->setSourceLanguageAndCountry(lang2, country2);
+ } else {
+ m_dataModel->setLanguageAndCountry(lang, country);
+ m_dataModel->setSourceLanguageAndCountry(lang2, country2);
+ }
+
+ accept();
+}
+
+void TranslationSettingsDialog::showEvent(QShowEvent *)
+{
+ QLocale::Language lang, lang2;
+ QLocale::Country country, country2;
+
+ if (m_phraseBook) {
+ lang = m_phraseBook->language();
+ country = m_phraseBook->country();
+ lang2 = m_phraseBook->sourceLanguage();
+ country2 = m_phraseBook->sourceCountry();
+ } else {
+ lang = m_dataModel->language();
+ country = m_dataModel->country();
+ lang2 = m_dataModel->sourceLanguage();
+ country2 = m_dataModel->sourceCountry();
+ }
+
+ int itemindex = m_ui.tgtCbLanguageList->findData(QVariant(int(lang)));
+ m_ui.tgtCbLanguageList->setCurrentIndex(itemindex == -1 ? 0 : itemindex);
+
+ itemindex = m_ui.tgtCbCountryList->findData(QVariant(int(country)));
+ m_ui.tgtCbCountryList->setCurrentIndex(itemindex == -1 ? 0 : itemindex);
+
+ itemindex = m_ui.srcCbLanguageList->findData(QVariant(int(lang2)));
+ m_ui.srcCbLanguageList->setCurrentIndex(itemindex == -1 ? 0 : itemindex);
+
+ itemindex = m_ui.srcCbCountryList->findData(QVariant(int(country2)));
+ m_ui.srcCbCountryList->setCurrentIndex(itemindex == -1 ? 0 : itemindex);
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/linguist/translationsettingsdialog.h b/src/linguist/linguist/translationsettingsdialog.h
new file mode 100644
index 000000000..9eee406fa
--- /dev/null
+++ b/src/linguist/linguist/translationsettingsdialog.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TRANSLATIONSETTINGSDIALOG_H
+#define TRANSLATIONSETTINGSDIALOG_H
+
+#include "ui_translationsettings.h"
+
+#include <QtCore/QLocale>
+#include <QtGui/QDialog>
+
+QT_BEGIN_NAMESPACE
+
+class DataModel;
+class PhraseBook;
+
+class TranslationSettingsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ TranslationSettingsDialog(QWidget *parent = 0);
+ void setDataModel(DataModel *model);
+ void setPhraseBook(PhraseBook *phraseBook);
+
+private:
+ virtual void showEvent(QShowEvent *e);
+
+private slots:
+ void on_buttonBox_accepted();
+ void on_srcCbLanguageList_currentIndexChanged(int idx);
+ void on_tgtCbLanguageList_currentIndexChanged(int idx);
+
+private:
+ Ui::TranslationSettingsDialog m_ui;
+ DataModel *m_dataModel;
+ PhraseBook *m_phraseBook;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // TRANSLATIONSETTINGSDIALOG_H