summaryrefslogtreecommitdiffstats
path: root/src/linguist
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit50123887ba0f33cf47520bee7c419d68742af2d1 (patch)
tree0eb8679b9e4e4370e59b44bfdcae616816e39aca /src/linguist
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/linguist')
-rw-r--r--src/linguist/lconvert/lconvert.pro22
-rw-r--r--src/linguist/lconvert/main.cpp301
-rw-r--r--src/linguist/linguist.pro6
-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
-rw-r--r--src/linguist/lrelease/lrelease.1118
-rw-r--r--src/linguist/lrelease/lrelease.pro23
-rw-r--r--src/linguist/lrelease/main.cpp391
-rw-r--r--src/linguist/lupdate/cpp.cpp2245
-rw-r--r--src/linguist/lupdate/java.cpp645
-rw-r--r--src/linguist/lupdate/lupdate.1153
-rw-r--r--src/linguist/lupdate/lupdate.exe.manifest14
-rw-r--r--src/linguist/lupdate/lupdate.h86
-rw-r--r--src/linguist/lupdate/lupdate.pro49
-rw-r--r--src/linguist/lupdate/main.cpp730
-rw-r--r--src/linguist/lupdate/merge.cpp515
-rw-r--r--src/linguist/lupdate/qdeclarative.cpp433
-rw-r--r--src/linguist/lupdate/qscript.cpp2612
-rw-r--r--src/linguist/lupdate/qscript.g2261
-rw-r--r--src/linguist/lupdate/ui.cpp208
-rw-r--r--src/linguist/lupdate/winmanifest.rc4
-rw-r--r--src/linguist/phrasebooks/danish.qph1018
-rw-r--r--src/linguist/phrasebooks/dutch.qph1044
-rw-r--r--src/linguist/phrasebooks/finnish.qph1033
-rw-r--r--src/linguist/phrasebooks/french.qph1493
-rw-r--r--src/linguist/phrasebooks/german.qph1075
-rw-r--r--src/linguist/phrasebooks/hungarian.qph752
-rw-r--r--src/linguist/phrasebooks/italian.qph1105
-rw-r--r--src/linguist/phrasebooks/japanese.qph1021
-rw-r--r--src/linguist/phrasebooks/norwegian.qph1004
-rw-r--r--src/linguist/phrasebooks/polish.qph527
-rw-r--r--src/linguist/phrasebooks/russian.qph1219
-rw-r--r--src/linguist/phrasebooks/spanish.qph1086
-rw-r--r--src/linguist/phrasebooks/swedish.qph1010
-rw-r--r--src/linguist/qdoc.conf15
-rw-r--r--src/linguist/shared/abstractproitemvisitor.h74
-rw-r--r--src/linguist/shared/formats.pri22
-rw-r--r--src/linguist/shared/numerus.cpp404
-rw-r--r--src/linguist/shared/po.cpp902
-rw-r--r--src/linguist/shared/profileevaluator.cpp2627
-rw-r--r--src/linguist/shared/profileevaluator.h116
-rw-r--r--src/linguist/shared/proitems.cpp358
-rw-r--r--src/linguist/shared/proitems.h248
-rw-r--r--src/linguist/shared/proparser.pri12
-rw-r--r--src/linguist/shared/proparserutils.h324
-rw-r--r--src/linguist/shared/qm.cpp803
-rw-r--r--src/linguist/shared/qph.cpp207
-rw-r--r--src/linguist/shared/simtexth.cpp277
-rw-r--r--src/linguist/shared/simtexth.h100
-rw-r--r--src/linguist/shared/translator.cpp759
-rw-r--r--src/linguist/shared/translator.h246
-rw-r--r--src/linguist/shared/translatormessage.cpp187
-rw-r--r--src/linguist/shared/translatormessage.h181
-rw-r--r--src/linguist/shared/ts.cpp781
-rw-r--r--src/linguist/shared/ts.dtd100
-rw-r--r--src/linguist/shared/xliff.cpp851
-rw-r--r--src/linguist/tests/data/main.cpp75
-rw-r--r--src/linguist/tests/data/test.pro9
-rw-r--r--src/linguist/tests/tests.pro16
-rw-r--r--src/linguist/tests/tst_linguist.cpp45
-rw-r--r--src/linguist/tests/tst_linguist.h63
-rw-r--r--src/linguist/tests/tst_lupdate.cpp195
-rw-r--r--src/linguist/tests/tst_simtexth.cpp73
181 files changed, 48062 insertions, 0 deletions
diff --git a/src/linguist/lconvert/lconvert.pro b/src/linguist/lconvert/lconvert.pro
new file mode 100644
index 000000000..66a194ec1
--- /dev/null
+++ b/src/linguist/lconvert/lconvert.pro
@@ -0,0 +1,22 @@
+
+TEMPLATE = app
+TARGET = lconvert
+DESTDIR = ../../../bin
+
+QT -= gui
+
+CONFIG += qt warn_on console
+CONFIG -= app_bundle
+
+build_all:!build_pass {
+ CONFIG -= build_all
+ CONFIG += release
+}
+
+include(../shared/formats.pri)
+
+DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII
+SOURCES += main.cpp
+
+target.path=$$[QT_INSTALL_BINS]
+INSTALLS += target
diff --git a/src/linguist/lconvert/main.cpp b/src/linguist/lconvert/main.cpp
new file mode 100644
index 000000000..a9960fc65
--- /dev/null
+++ b/src/linguist/lconvert/main.cpp
@@ -0,0 +1,301 @@
+/****************************************************************************
+**
+** 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 "translator.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QTranslator>
+#include <QtCore/QLibraryInfo>
+
+#include <iostream>
+
+QT_USE_NAMESPACE
+
+class LC {
+ Q_DECLARE_TR_FUNCTIONS(LConvert)
+};
+
+static int usage(const QStringList &args)
+{
+ Q_UNUSED(args);
+
+ QString loaders;
+ QString line(QLatin1String(" %1 - %2\n"));
+ foreach (Translator::FileFormat format, Translator::registeredFileFormats())
+ loaders += line.arg(format.extension, -5).arg(format.description);
+
+ std::cout << qPrintable(LC::tr("\nUsage:\n"
+ " lconvert [options] <infile> [<infile>...]\n\n"
+ "lconvert is part of Qt's Linguist tool chain. It can be used as a\n"
+ "stand-alone tool to convert and filter translation data files.\n"
+ "The following file formats are supported:\n\n%1\n"
+ "If multiple input files are specified, they are merged with\n"
+ "translations from later files taking precedence.\n\n"
+ "Options:\n"
+ " -h\n"
+ " --help Display this information and exit.\n\n"
+ " -i <infile>\n"
+ " --input-file <infile>\n"
+ " Specify input file. Use if <infile> might start with a dash.\n"
+ " This option can be used several times to merge inputs.\n"
+ " May be '-' (standard input) for use in a pipe.\n\n"
+ " -o <outfile>\n"
+ " --output-file <outfile>\n"
+ " Specify output file. Default is '-' (standard output).\n\n"
+ " -if <informat>\n"
+ " --input-format <format>\n"
+ " Specify input format for subsequent <infile>s.\n"
+ " The format is auto-detected from the file name and defaults to 'ts'.\n\n"
+ " -of <outformat>\n"
+ " --output-format <outformat>\n"
+ " Specify output format. See -if.\n\n"
+ " --input-codec <codec>\n"
+ " Specify encoding for QM and PO input files. Default is 'Latin1'\n"
+ " for QM and 'UTF-8' for PO files. UTF-8 is always tried as well for\n"
+ " QM, corresponding to the possible use of the trUtf8() function.\n\n"
+ " --output-codec <codec>\n"
+ " Specify encoding for PO output files. Default is 'UTF-8'.\n\n"
+ " --drop-tags <regexp>\n"
+ " Drop named extra tags when writing TS or XLIFF files.\n"
+ " May be specified repeatedly.\n\n"
+ " --drop-translations\n"
+ " Drop existing translations and reset the status to 'unfinished'.\n"
+ " Note: this implies --no-obsolete.\n\n"
+ " --source-language <language>[_<region>]\n"
+ " Specify/override the language of the source strings. Defaults to\n"
+ " POSIX if not specified and the file does not name it yet.\n\n"
+ " --target-language <language>[_<region>]\n"
+ " Specify/override the language of the translation.\n"
+ " The target language is guessed from the file name if this option\n"
+ " is not specified and the file contents name no language yet.\n\n"
+ " --no-obsolete\n"
+ " Drop obsolete messages.\n\n"
+ " --no-finished\n"
+ " Drop finished messages.\n\n"
+ " --sort-contexts\n"
+ " Sort contexts in output TS file alphabetically.\n\n"
+ " --locations {absolute|relative|none}\n"
+ " Override how source code references are saved in TS files.\n"
+ " Default is absolute.\n\n"
+ " --no-ui-lines\n"
+ " Drop line numbers from references to UI files.\n\n"
+ " --verbose\n"
+ " be a bit more verbose\n\n"
+ "Long options can be specified with only one leading dash, too.\n\n"
+ "Return value:\n"
+ " 0 on success\n"
+ " 1 on command line parse failures\n"
+ " 2 on read failures\n"
+ " 3 on write failures\n").arg(loaders));
+ return 1;
+}
+
+struct File
+{
+ QString name;
+ QString format;
+};
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+#ifndef Q_OS_WIN32
+ QTranslator translator;
+ QTranslator qtTranslator;
+ QString sysLocale = QLocale::system().name();
+ QString resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
+ if (translator.load(QLatin1String("linguist_") + sysLocale, resourceDir)
+ && qtTranslator.load(QLatin1String("qt_") + sysLocale, resourceDir)) {
+ app.installTranslator(&translator);
+ app.installTranslator(&qtTranslator);
+ }
+#endif // Q_OS_WIN32
+
+ QStringList args = app.arguments();
+ QList<File> inFiles;
+ QString inFormat(QLatin1String("auto"));
+ QString outFileName;
+ QString outFormat(QLatin1String("auto"));
+ QString targetLanguage;
+ QString sourceLanguage;
+ bool dropTranslations = false;
+ bool noObsolete = false;
+ bool noFinished = false;
+ bool verbose = false;
+ bool noUiLines = false;
+ Translator::LocationsType locations = Translator::DefaultLocations;
+
+ ConversionData cd;
+ Translator tr;
+
+ for (int i = 1; i < args.size(); ++i) {
+ if (args[i].startsWith(QLatin1String("--")))
+ args[i].remove(0, 1);
+ if (args[i] == QLatin1String("-o")
+ || args[i] == QLatin1String("-output-file")) {
+ if (++i >= args.size())
+ return usage(args);
+ outFileName = args[i];
+ } else if (args[i] == QLatin1String("-of")
+ || args[i] == QLatin1String("-output-format")) {
+ if (++i >= args.size())
+ return usage(args);
+ outFormat = args[i];
+ } else if (args[i] == QLatin1String("-i")
+ || args[i] == QLatin1String("-input-file")) {
+ if (++i >= args.size())
+ return usage(args);
+ File file;
+ file.name = args[i];
+ file.format = inFormat;
+ inFiles.append(file);
+ } else if (args[i] == QLatin1String("-if")
+ || args[i] == QLatin1String("-input-format")) {
+ if (++i >= args.size())
+ return usage(args);
+ inFormat = args[i];
+ } else if (args[i] == QLatin1String("-input-codec")) {
+ if (++i >= args.size())
+ return usage(args);
+ cd.m_codecForSource = args[i].toLatin1();
+ } else if (args[i] == QLatin1String("-output-codec")) {
+ if (++i >= args.size())
+ return usage(args);
+ cd.m_outputCodec = args[i].toLatin1();
+ } else if (args[i] == QLatin1String("-drop-tag")) {
+ if (++i >= args.size())
+ return usage(args);
+ cd.m_dropTags.append(args[i]);
+ } else if (args[i] == QLatin1String("-drop-translations")) {
+ dropTranslations = true;
+ } else if (args[i] == QLatin1String("-target-language")) {
+ if (++i >= args.size())
+ return usage(args);
+ targetLanguage = args[i];
+ } else if (args[i] == QLatin1String("-source-language")) {
+ if (++i >= args.size())
+ return usage(args);
+ sourceLanguage = args[i];
+ } else if (args[i].startsWith(QLatin1String("-h"))) {
+ usage(args);
+ return 0;
+ } else if (args[i] == QLatin1String("-no-obsolete")) {
+ noObsolete = true;
+ } else if (args[i] == QLatin1String("-no-finished")) {
+ noFinished = true;
+ } else if (args[i] == QLatin1String("-sort-contexts")) {
+ cd.m_sortContexts = true;
+ } else if (args[i] == QLatin1String("-locations")) {
+ if (++i >= args.size())
+ return usage(args);
+ if (args[i] == QLatin1String("none"))
+ locations = Translator::NoLocations;
+ else if (args[i] == QLatin1String("relative"))
+ locations = Translator::RelativeLocations;
+ else if (args[i] == QLatin1String("absolute"))
+ locations = Translator::AbsoluteLocations;
+ else
+ return usage(args);
+ } else if (args[i] == QLatin1String("-no-ui-lines")) {
+ noUiLines = true;
+ } else if (args[i] == QLatin1String("-verbose")) {
+ verbose = true;
+ } else if (args[i].startsWith(QLatin1Char('-'))) {
+ return usage(args);
+ } else {
+ File file;
+ file.name = args[i];
+ file.format = inFormat;
+ inFiles.append(file);
+ }
+ }
+
+ if (inFiles.isEmpty())
+ return usage(args);
+
+ tr.setLanguageCode(Translator::guessLanguageCodeFromFileName(inFiles[0].name));
+
+ if (!tr.load(inFiles[0].name, cd, inFiles[0].format)) {
+ std::cerr << qPrintable(cd.error());
+ return 2;
+ }
+ tr.reportDuplicates(tr.resolveDuplicates(), inFiles[0].name, verbose);
+
+ for (int i = 1; i < inFiles.size(); ++i) {
+ Translator tr2;
+ if (!tr2.load(inFiles[i].name, cd, inFiles[i].format)) {
+ std::cerr << qPrintable(cd.error());
+ return 2;
+ }
+ tr2.reportDuplicates(tr2.resolveDuplicates(), inFiles[i].name, verbose);
+ for (int j = 0; j < tr2.messageCount(); ++j)
+ tr.replaceSorted(tr2.message(j));
+ }
+
+ if (!targetLanguage.isEmpty())
+ tr.setLanguageCode(targetLanguage);
+ if (!sourceLanguage.isEmpty())
+ tr.setSourceLanguageCode(sourceLanguage);
+ if (noObsolete)
+ tr.stripObsoleteMessages();
+ if (noFinished)
+ tr.stripFinishedMessages();
+ if (dropTranslations)
+ tr.dropTranslations();
+ if (noUiLines)
+ tr.dropUiLines();
+ if (locations != Translator::DefaultLocations)
+ tr.setLocationsType(locations);
+
+ tr.normalizeTranslations(cd);
+ if (!cd.errors().isEmpty()) {
+ std::cerr << qPrintable(cd.error());
+ cd.clearErrors();
+ }
+ if (!tr.save(outFileName, cd, outFormat)) {
+ std::cerr << qPrintable(cd.error());
+ return 3;
+ }
+ return 0;
+}
diff --git a/src/linguist/linguist.pro b/src/linguist/linguist.pro
new file mode 100644
index 000000000..248c89e2b
--- /dev/null
+++ b/src/linguist/linguist.pro
@@ -0,0 +1,6 @@
+TEMPLATE = subdirs
+SUBDIRS = \
+ lrelease \
+ lupdate \
+ lconvert
+!no-png:!contains(QT_CONFIG, no-gui):SUBDIRS += linguist
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
diff --git a/src/linguist/lrelease/lrelease.1 b/src/linguist/lrelease/lrelease.1
new file mode 100644
index 000000000..61977926b
--- /dev/null
+++ b/src/linguist/lrelease/lrelease.1
@@ -0,0 +1,118 @@
+.TH lrelease 1 "18 October 2001" "Nokia Corporation and/or its subsidiary(-ies)" \" -*- nroff -*-
+.\"
+.\" 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 QtGui module 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$
+.\"
+.SH NAME
+lrelease \- generate Qt message files from Qt Linguist translation files
+.SH SYNOPSIS
+.B lrelease
+.RI "[ " options " ] " project-file
+.br
+.B lrelease
+.RI "[ " options " ] " ts-files " [ -qm " qm-file " ]"
+.SH DESCRIPTION
+This page documents the
+.B Qt Linguist Release
+tool for the Qt GUI toolkit.
+.B Lrelease
+reads a qmake/tmake project file (.pro file) and converts the
+translation files (TS files) specified in it into Qt message files
+(QM files) used by the application to translate.
+.PP
+The QM file format is a compact binary format that provides
+extremely fast lookups for translations and that is used by Qt.
+.SH OPTIONS
+.TP
+.I "-help"
+Display the usage and exit.
+.TP
+.I "-compress"
+Compress the QM files.
+.TP
+.I "-nounfinished"
+Do not include unfinished translations.
+.TP
+.I "-removeidentical"
+If the translated text is the same as
+the source text, do not include the message.
+.TP
+.I "-silent"
+Do not explain what is being done.
+.TP
+.I "-version"
+Display the version of
+.B lrelease
+and exit.
+.SH USAGE
+Here is an example .pro file that can be given to
+.B lrelease:
+.PP
+.in +4
+.nf
+HEADERS = funnydialog.h \\
+ wackywidget.h
+SOURCES = funnydialog.cpp \\
+ main.cpp \\
+ wackywidget.cpp
+FORMS = fancybox.ui
+TRANSLATIONS = gnomovision_dk.ts \\
+ gnomovision_fi.ts \\
+ gnomovision_no.ts \\
+ gnomovision_se.ts
+.fi
+.in -4
+.PP
+When running
+.B lrelease
+on this project file, the Qt message files gnomovision_dk.qm,
+gnomovision_fi.qm, gnomovision_no.qm and gnomovision_se.qm will be
+generated from gnomovision_dk.ts, gnomovision_fi.ts,
+gnomovision_no.ts and gnomovision_se.ts, respectively.
+.PP
+.B Lrelease
+can also be invoked with a list of TS files to convert:
+.PP
+.in +4
+.nf
+lrelease gnomovision_*.ts
+.fi
+.in -4
+.SH "SEE ALSO"
+.BR lupdate (1)
+and
+.BR http://qt.nokia.com/doc/i18n.html
diff --git a/src/linguist/lrelease/lrelease.pro b/src/linguist/lrelease/lrelease.pro
new file mode 100644
index 000000000..89694be6e
--- /dev/null
+++ b/src/linguist/lrelease/lrelease.pro
@@ -0,0 +1,23 @@
+TEMPLATE = app
+TARGET = lrelease
+DESTDIR = ../../../bin
+
+DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII
+SOURCES += main.cpp
+
+INCLUDEPATH += $$QT_BUILD_TREE/src/corelib/global # qlibraryinfo.cpp includes qconfig.cpp
+SOURCES += \
+ $$QT_SOURCE_TREE/src/corelib/global/qlibraryinfo.cpp \
+ $$QT_SOURCE_TREE/src/corelib/io/qsettings.cpp
+win32:SOURCES += $$QT_SOURCE_TREE/src/corelib/io/qsettings_win.cpp
+macx:SOURCES += $$QT_SOURCE_TREE/src/corelib/io/qsettings_mac.cpp
+
+include(../../../src/tools/bootstrap/bootstrap.pri)
+include(../shared/formats.pri)
+include(../shared/proparser.pri)
+include(../../shared/symbian/epocroot.pri)
+
+win32:LIBS += -ladvapi32 # for qsettings_win.cpp
+
+target.path=$$[QT_INSTALL_BINS]
+INSTALLS += target
diff --git a/src/linguist/lrelease/main.cpp b/src/linguist/lrelease/main.cpp
new file mode 100644
index 000000000..b1beec873
--- /dev/null
+++ b/src/linguist/lrelease/main.cpp
@@ -0,0 +1,391 @@
+/****************************************************************************
+**
+** 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 "translator.h"
+#include "profileevaluator.h"
+
+#ifndef QT_BOOTSTRAPPED
+#include <QtCore/QCoreApplication>
+#include <QtCore/QTranslator>
+#endif
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QRegExp>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QTextStream>
+
+#include <iostream>
+
+QT_USE_NAMESPACE
+
+#ifdef QT_BOOTSTRAPPED
+static void initBinaryDir(
+#ifndef Q_OS_WIN
+ const char *argv0
+#endif
+ );
+
+struct LR {
+ static inline QString tr(const char *sourceText, const char *comment = 0)
+ {
+ return QCoreApplication::translate("LRelease", sourceText, comment);
+ }
+};
+#else
+class LR {
+ Q_DECLARE_TR_FUNCTIONS(LRelease)
+};
+#endif
+
+static void printOut(const QString & out)
+{
+ QTextStream stream(stdout);
+ stream << out;
+}
+
+static void printUsage()
+{
+ printOut(LR::tr(
+ "Usage:\n"
+ " lrelease [options] project-file\n"
+ " lrelease [options] ts-files [-qm qm-file]\n\n"
+ "lrelease is part of Qt's Linguist tool chain. It can be used as a\n"
+ "stand-alone tool to convert XML-based translations files in the TS\n"
+ "format into the 'compiled' QM format used by QTranslator objects.\n\n"
+ "Options:\n"
+ " -help Display this information and exit\n"
+ " -idbased\n"
+ " Use IDs instead of source strings for message keying\n"
+ " -compress\n"
+ " Compress the QM files\n"
+ " -nounfinished\n"
+ " Do not include unfinished translations\n"
+ " -removeidentical\n"
+ " If the translated text is the same as\n"
+ " the source text, do not include the message\n"
+ " -markuntranslated <prefix>\n"
+ " If a message has no real translation, use the source text\n"
+ " prefixed with the given string instead\n"
+ " -silent\n"
+ " Do not explain what is being done\n"
+ " -version\n"
+ " Display the version of lrelease and exit\n"
+ ));
+}
+
+static bool loadTsFile(Translator &tor, const QString &tsFileName, bool /* verbose */)
+{
+ ConversionData cd;
+ bool ok = tor.load(tsFileName, cd, QLatin1String("auto"));
+ if (!ok) {
+ std::cerr << qPrintable(LR::tr("lrelease error: %1").arg(cd.error()));
+ } else {
+ if (!cd.errors().isEmpty())
+ printOut(cd.error());
+ }
+ cd.clearErrors();
+ return ok;
+}
+
+static bool releaseTranslator(Translator &tor, const QString &qmFileName,
+ ConversionData &cd, bool removeIdentical)
+{
+ tor.reportDuplicates(tor.resolveDuplicates(), qmFileName, cd.isVerbose());
+
+ if (cd.isVerbose())
+ printOut(LR::tr("Updating '%1'...\n").arg(qmFileName));
+ if (removeIdentical) {
+ if (cd.isVerbose())
+ printOut(LR::tr("Removing translations equal to source text in '%1'...\n").arg(qmFileName));
+ tor.stripIdenticalSourceTranslations();
+ }
+
+ QFile file(qmFileName);
+ if (!file.open(QIODevice::WriteOnly)) {
+ std::cerr << qPrintable(LR::tr("lrelease error: cannot create '%1': %2\n")
+ .arg(qmFileName, file.errorString()));
+ return false;
+ }
+
+ tor.normalizeTranslations(cd);
+ bool ok = tor.release(&file, cd);
+ file.close();
+
+ if (!ok) {
+ std::cerr << qPrintable(LR::tr("lrelease error: cannot save '%1': %2")
+ .arg(qmFileName, cd.error()));
+ } else if (!cd.errors().isEmpty()) {
+ printOut(cd.error());
+ }
+ cd.clearErrors();
+ return ok;
+}
+
+static bool releaseTsFile(const QString& tsFileName,
+ ConversionData &cd, bool removeIdentical)
+{
+ Translator tor;
+ if (!loadTsFile(tor, tsFileName, cd.isVerbose()))
+ return false;
+
+ QString qmFileName = tsFileName;
+ foreach (const Translator::FileFormat &fmt, Translator::registeredFileFormats()) {
+ if (qmFileName.endsWith(QLatin1Char('.') + fmt.extension)) {
+ qmFileName.chop(fmt.extension.length() + 1);
+ break;
+ }
+ }
+ qmFileName += QLatin1String(".qm");
+
+ return releaseTranslator(tor, qmFileName, cd, removeIdentical);
+}
+
+int main(int argc, char **argv)
+{
+#ifdef QT_BOOTSTRAPPED
+ initBinaryDir(
+#ifndef Q_OS_WIN
+ argv[0]
+#endif
+ );
+#else
+ QCoreApplication app(argc, argv);
+#ifndef Q_OS_WIN32
+ QTranslator translator;
+ QTranslator qtTranslator;
+ QString sysLocale = QLocale::system().name();
+ QString resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
+ if (translator.load(QLatin1String("linguist_") + sysLocale, resourceDir)
+ && qtTranslator.load(QLatin1String("qt_") + sysLocale, resourceDir)) {
+ app.installTranslator(&translator);
+ app.installTranslator(&qtTranslator);
+ }
+#endif // Q_OS_WIN32
+#endif // QT_BOOTSTRAPPED
+
+ ConversionData cd;
+ cd.m_verbose = true; // the default is true starting with Qt 4.2
+ bool removeIdentical = false;
+ Translator tor;
+ QStringList inputFiles;
+ QString outputFile;
+
+ for (int i = 1; i < argc; ++i) {
+ if (!strcmp(argv[i], "-compress")) {
+ cd.m_saveMode = SaveStripped;
+ continue;
+ } else if (!strcmp(argv[i], "-idbased")) {
+ cd.m_idBased = true;
+ continue;
+ } else if (!strcmp(argv[i], "-nocompress")) {
+ cd.m_saveMode = SaveEverything;
+ continue;
+ } else if (!strcmp(argv[i], "-removeidentical")) {
+ removeIdentical = true;
+ continue;
+ } else if (!strcmp(argv[i], "-nounfinished")) {
+ cd.m_ignoreUnfinished = true;
+ continue;
+ } else if (!strcmp(argv[i], "-markuntranslated")) {
+ if (i == argc - 1) {
+ printUsage();
+ return 1;
+ }
+ cd.m_unTrPrefix = QString::fromLocal8Bit(argv[++i]);
+ } else if (!strcmp(argv[i], "-silent")) {
+ cd.m_verbose = false;
+ continue;
+ } else if (!strcmp(argv[i], "-verbose")) {
+ cd.m_verbose = true;
+ continue;
+ } else if (!strcmp(argv[i], "-version")) {
+ printOut(LR::tr("lrelease version %1\n").arg(QLatin1String(QT_VERSION_STR)));
+ return 0;
+ } else if (!strcmp(argv[i], "-qm")) {
+ if (i == argc - 1) {
+ printUsage();
+ return 1;
+ }
+ outputFile = QString::fromLocal8Bit(argv[++i]);
+ } else if (!strcmp(argv[i], "-help")) {
+ printUsage();
+ return 0;
+ } else if (argv[i][0] == '-') {
+ printUsage();
+ return 1;
+ } else {
+ inputFiles << QString::fromLocal8Bit(argv[i]);
+ }
+ }
+
+ if (inputFiles.isEmpty()) {
+ printUsage();
+ return 1;
+ }
+
+ foreach (const QString &inputFile, inputFiles) {
+ if (inputFile.endsWith(QLatin1String(".pro"), Qt::CaseInsensitive)
+ || inputFile.endsWith(QLatin1String(".pri"), Qt::CaseInsensitive)) {
+ QFileInfo fi(inputFile);
+ ProFile pro(fi.absoluteFilePath());
+
+ ProFileEvaluator visitor;
+ visitor.setVerbose(cd.isVerbose());
+
+ if (!visitor.queryProFile(&pro)) {
+ std::cerr << qPrintable(LR::tr(
+ "lrelease error: cannot read project file '%1'.\n")
+ .arg(inputFile));
+ continue;
+ }
+ if (!visitor.accept(&pro)) {
+ std::cerr << qPrintable(LR::tr(
+ "lrelease error: cannot process project file '%1'.\n")
+ .arg(inputFile));
+ continue;
+ }
+
+ QStringList translations = visitor.values(QLatin1String("TRANSLATIONS"));
+ if (translations.isEmpty()) {
+ std::cerr << qPrintable(LR::tr(
+ "lrelease warning: Met no 'TRANSLATIONS' entry in project file '%1'\n")
+ .arg(inputFile));
+ } else {
+ QDir proDir(fi.absolutePath());
+ foreach (const QString &trans, translations)
+ if (!releaseTsFile(QFileInfo(proDir, trans).filePath(), cd, removeIdentical))
+ return 1;
+ }
+ } else {
+ if (outputFile.isEmpty()) {
+ if (!releaseTsFile(inputFile, cd, removeIdentical))
+ return 1;
+ } else {
+ if (!loadTsFile(tor, inputFile, cd.isVerbose()))
+ return 1;
+ }
+ }
+ }
+
+ if (!outputFile.isEmpty())
+ return releaseTranslator(tor, outputFile, cd, removeIdentical) ? 0 : 1;
+
+ return 0;
+}
+
+#ifdef QT_BOOTSTRAPPED
+
+#ifdef Q_OS_WIN
+# include <windows.h>
+#endif
+
+static QString binDir;
+
+static void initBinaryDir(
+#ifndef Q_OS_WIN
+ const char *_argv0
+#endif
+ )
+{
+#ifdef Q_OS_WIN
+ wchar_t module_name[MAX_PATH];
+ GetModuleFileName(0, module_name, MAX_PATH);
+ QFileInfo filePath = QString::fromWCharArray(module_name);
+ binDir = filePath.filePath();
+#else
+ QString argv0 = QFile::decodeName(QByteArray(_argv0));
+ QString absPath;
+
+ if (!argv0.isEmpty() && argv0.at(0) == QLatin1Char('/')) {
+ /*
+ If argv0 starts with a slash, it is already an absolute
+ file path.
+ */
+ absPath = argv0;
+ } else if (argv0.contains(QLatin1Char('/'))) {
+ /*
+ If argv0 contains one or more slashes, it is a file path
+ relative to the current directory.
+ */
+ absPath = QDir::current().absoluteFilePath(argv0);
+ } else {
+ /*
+ Otherwise, the file path has to be determined using the
+ PATH environment variable.
+ */
+ QByteArray pEnv = qgetenv("PATH");
+ QDir currentDir = QDir::current();
+ QStringList paths = QString::fromLocal8Bit(pEnv.constData()).split(QLatin1String(":"));
+ for (QStringList::const_iterator p = paths.constBegin(); p != paths.constEnd(); ++p) {
+ if ((*p).isEmpty())
+ continue;
+ QString candidate = currentDir.absoluteFilePath(*p + QLatin1Char('/') + argv0);
+ QFileInfo candidate_fi(candidate);
+ if (candidate_fi.exists() && !candidate_fi.isDir()) {
+ binDir = candidate_fi.canonicalPath();
+ return;
+ }
+ }
+ return;
+ }
+
+ QFileInfo fi(absPath);
+ if (fi.exists())
+ binDir = fi.canonicalPath();
+#endif
+}
+
+QT_BEGIN_NAMESPACE
+
+// The name is hard-coded in QLibraryInfo
+QString qmake_libraryInfoFile()
+{
+ if (binDir.isEmpty())
+ return QString();
+ return QDir(binDir).filePath(QString::fromLatin1("qt.conf"));
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_BOOTSTRAPPED
diff --git a/src/linguist/lupdate/cpp.cpp b/src/linguist/lupdate/cpp.cpp
new file mode 100644
index 000000000..2ae478e70
--- /dev/null
+++ b/src/linguist/lupdate/cpp.cpp
@@ -0,0 +1,2245 @@
+/****************************************************************************
+**
+** 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 "lupdate.h"
+
+#include <translator.h>
+
+#include <QtCore/QBitArray>
+#include <QtCore/QDebug>
+#include <QtCore/QFileInfo>
+#include <QtCore/QStack>
+#include <QtCore/QString>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextStream>
+#include <QtCore/QCoreApplication>
+
+#include <iostream>
+
+#include <ctype.h> // for isXXX()
+
+QT_BEGIN_NAMESPACE
+
+class LU {
+ Q_DECLARE_TR_FUNCTIONS(LUpdate)
+};
+
+/* qmake ignore Q_OBJECT */
+
+static QString MagicComment(QLatin1String("TRANSLATOR"));
+
+#define STRING(s) static QString str##s(QLatin1String(#s))
+
+//#define DIAGNOSE_RETRANSLATABILITY // FIXME: should make a runtime option of this
+
+class HashString {
+public:
+ HashString() : m_hash(0x80000000) {}
+ explicit HashString(const QString &str) : m_str(str), m_hash(0x80000000) {}
+ void setValue(const QString &str) { m_str = str; m_hash = 0x80000000; }
+ const QString &value() const { return m_str; }
+ bool operator==(const HashString &other) const { return m_str == other.m_str; }
+private:
+ QString m_str;
+ // qHash() of a QString is only 28 bits wide, so we can use
+ // the highest bit(s) as the "hash valid" flag.
+ mutable uint m_hash;
+ friend uint qHash(const HashString &str);
+};
+
+uint qHash(const HashString &str)
+{
+ if (str.m_hash & 0x80000000)
+ str.m_hash = qHash(str.m_str);
+ return str.m_hash;
+}
+
+class HashStringList {
+public:
+ explicit HashStringList(const QList<HashString> &list) : m_list(list), m_hash(0x80000000) {}
+ const QList<HashString> &value() const { return m_list; }
+ bool operator==(const HashStringList &other) const { return m_list == other.m_list; }
+private:
+ QList<HashString> m_list;
+ mutable uint m_hash;
+ friend uint qHash(const HashStringList &list);
+};
+
+uint qHash(const HashStringList &list)
+{
+ if (list.m_hash & 0x80000000) {
+ uint hash = 0;
+ foreach (const HashString &qs, list.m_list) {
+ hash ^= qHash(qs) ^ 0x0ad9f526;
+ hash = ((hash << 13) & 0x0fffffff) | (hash >> 15);
+ }
+ list.m_hash = hash;
+ }
+ return list.m_hash;
+}
+
+typedef QList<HashString> NamespaceList;
+
+struct Namespace {
+
+ Namespace() :
+ classDef(this),
+ hasTrFunctions(false), complained(false)
+ {}
+ ~Namespace()
+ {
+ qDeleteAll(children);
+ }
+
+ QHash<HashString, Namespace *> children;
+ QHash<HashString, NamespaceList> aliases;
+ QList<HashStringList> usings;
+
+ // Class declarations set no flags and create no namespaces, so they are ignored.
+ // Class definitions may appear multiple times - but only because we are trying to
+ // "compile" all sources irrespective of build configuration.
+ // Nested classes may be forward-declared inside a definition, and defined in another file.
+ // The latter will detach the class' child list, so clones need a backlink to the original
+ // definition (either one in case of multiple definitions).
+ // Namespaces can have tr() functions as well, so we need to track parent definitions for
+ // them as well. The complication is that we may have to deal with a forrest instead of
+ // a tree - in that case the parent will be arbitrary. However, it seem likely that
+ // Q_DECLARE_TR_FUNCTIONS would be used either in "class-like" namespaces with a central
+ // header or only locally in a file.
+ Namespace *classDef;
+
+ QString trQualification;
+
+ bool hasTrFunctions;
+ bool complained; // ... that tr functions are missing.
+};
+
+static int nextFileId;
+
+class VisitRecorder {
+public:
+ VisitRecorder()
+ {
+ m_ba.resize(nextFileId);
+ }
+ bool tryVisit(int fileId)
+ {
+ if (m_ba.at(fileId))
+ return false;
+ m_ba[fileId] = true;
+ return true;
+ }
+private:
+ QBitArray m_ba;
+};
+
+struct ParseResults {
+ int fileId;
+ Namespace rootNamespace;
+ QSet<const ParseResults *> includes;
+};
+
+typedef QHash<QString, const ParseResults *> ParseResultHash;
+typedef QHash<QString, const Translator *> TranslatorHash;
+
+class CppFiles {
+
+public:
+ static const ParseResults *getResults(const QString &cleanFile);
+ static void setResults(const QString &cleanFile, const ParseResults *results);
+ static const Translator *getTranslator(const QString &cleanFile);
+ static void setTranslator(const QString &cleanFile, const Translator *results);
+ static bool isBlacklisted(const QString &cleanFile);
+ static void setBlacklisted(const QString &cleanFile);
+
+private:
+ static ParseResultHash &parsedFiles();
+ static TranslatorHash &translatedFiles();
+ static QSet<QString> &blacklistedFiles();
+};
+
+class CppParser {
+
+public:
+ CppParser(ParseResults *results = 0);
+ void setInput(const QString &in);
+ void setInput(QTextStream &ts, const QString &fileName);
+ void setTranslator(Translator *_tor) { tor = _tor; }
+ void parse(const QString &initialContext, ConversionData &cd, QSet<QString> &inclusions);
+ void parseInternal(ConversionData &cd, QSet<QString> &inclusions);
+ const ParseResults *recordResults(bool isHeader);
+ void deleteResults() { delete results; }
+
+ struct SavedState {
+ NamespaceList namespaces;
+ QStack<int> namespaceDepths;
+ NamespaceList functionContext;
+ QString functionContextUnresolved;
+ QString pendingContext;
+ };
+
+private:
+ struct IfdefState {
+ IfdefState() {}
+ IfdefState(int _bracketDepth, int _braceDepth, int _parenDepth) :
+ bracketDepth(_bracketDepth),
+ braceDepth(_braceDepth),
+ parenDepth(_parenDepth),
+ elseLine(-1)
+ {}
+
+ SavedState state;
+ int bracketDepth, bracketDepth1st;
+ int braceDepth, braceDepth1st;
+ int parenDepth, parenDepth1st;
+ int elseLine;
+ };
+
+ std::ostream &yyMsg(int line = 0);
+
+ uint getChar();
+ uint getToken();
+ bool getMacroArgs();
+ bool match(uint t);
+ bool matchString(QString *s);
+ bool matchEncoding(bool *utf8);
+ bool matchStringOrNull(QString *s);
+ bool matchExpression();
+
+ QString transcode(const QString &str, bool utf8);
+ void recordMessage(
+ int line, const QString &context, const QString &text, const QString &comment,
+ const QString &extracomment, const QString &msgid, const TranslatorMessage::ExtraData &extra,
+ bool utf8, bool plural);
+
+ void processInclude(const QString &file, ConversionData &cd,
+ QSet<QString> &inclusions);
+
+ void saveState(SavedState *state);
+ void loadState(const SavedState *state);
+
+ static QString stringifyNamespace(const NamespaceList &namespaces);
+ static QStringList stringListifyNamespace(const NamespaceList &namespaces);
+ typedef bool (CppParser::*VisitNamespaceCallback)(const Namespace *ns, void *context) const;
+ bool visitNamespace(const NamespaceList &namespaces, int nsCount,
+ VisitNamespaceCallback callback, void *context,
+ VisitRecorder &vr, const ParseResults *rslt) const;
+ bool visitNamespace(const NamespaceList &namespaces, int nsCount,
+ VisitNamespaceCallback callback, void *context) const;
+ static QStringList stringListifySegments(const QList<HashString> &namespaces);
+ bool qualifyOneCallbackOwn(const Namespace *ns, void *context) const;
+ bool qualifyOneCallbackUsing(const Namespace *ns, void *context) const;
+ bool qualifyOne(const NamespaceList &namespaces, int nsCnt, const HashString &segment,
+ NamespaceList *resolved, QSet<HashStringList> *visitedUsings) const;
+ bool qualifyOne(const NamespaceList &namespaces, int nsCnt, const HashString &segment,
+ NamespaceList *resolved) const;
+ bool fullyQualify(const NamespaceList &namespaces, int nsCnt,
+ const QList<HashString> &segments, bool isDeclaration,
+ NamespaceList *resolved, QStringList *unresolved) const;
+ bool fullyQualify(const NamespaceList &namespaces,
+ const QList<HashString> &segments, bool isDeclaration,
+ NamespaceList *resolved, QStringList *unresolved) const;
+ bool fullyQualify(const NamespaceList &namespaces,
+ const QString &segments, bool isDeclaration,
+ NamespaceList *resolved, QStringList *unresolved) const;
+ bool findNamespaceCallback(const Namespace *ns, void *context) const;
+ const Namespace *findNamespace(const NamespaceList &namespaces, int nsCount = -1) const;
+ void enterNamespace(NamespaceList *namespaces, const HashString &name);
+ void truncateNamespaces(NamespaceList *namespaces, int lenght);
+ Namespace *modifyNamespace(NamespaceList *namespaces, bool haveLast = true);
+
+ enum {
+ Tok_Eof, Tok_class, Tok_friend, Tok_namespace, Tok_using, Tok_return,
+ Tok_tr, Tok_trUtf8, Tok_translate, Tok_translateUtf8, Tok_trid,
+ Tok_Q_OBJECT, Tok_Q_DECLARE_TR_FUNCTIONS,
+ Tok_Ident, Tok_Comment, Tok_String, Tok_Arrow, Tok_Colon, Tok_ColonColon,
+ Tok_Equals, Tok_LeftBracket, Tok_RightBracket,
+ Tok_LeftBrace, Tok_RightBrace, Tok_LeftParen, Tok_RightParen, Tok_Comma, Tok_Semicolon,
+ Tok_Null, Tok_Integer,
+ Tok_QuotedInclude, Tok_AngledInclude,
+ Tok_Other
+ };
+
+ // Tokenizer state
+ QString yyFileName;
+ int yyCh;
+ bool yyAtNewline;
+ bool yyCodecIsUtf8;
+ bool yyForceUtf8;
+ QString yyWord;
+ qlonglong yyInteger;
+ QStack<IfdefState> yyIfdefStack;
+ int yyBracketDepth;
+ int yyBraceDepth;
+ int yyParenDepth;
+ int yyLineNo;
+ int yyCurLineNo;
+ int yyBracketLineNo;
+ int yyBraceLineNo;
+ int yyParenLineNo;
+
+ // the string to read from and current position in the string
+ QTextCodec *yySourceCodec;
+ QString yyInStr;
+ const ushort *yyInPtr;
+
+ // Parser state
+ uint yyTok;
+
+ NamespaceList namespaces;
+ QStack<int> namespaceDepths;
+ NamespaceList functionContext;
+ QString functionContextUnresolved;
+ QString prospectiveContext;
+ QString pendingContext;
+ ParseResults *results;
+ Translator *tor;
+ bool directInclude;
+
+ SavedState savedState;
+ int yyMinBraceDepth;
+ bool inDefine;
+};
+
+CppParser::CppParser(ParseResults *_results)
+{
+ tor = 0;
+ if (_results) {
+ results = _results;
+ directInclude = true;
+ } else {
+ results = new ParseResults;
+ directInclude = false;
+ }
+ yyBracketDepth = 0;
+ yyBraceDepth = 0;
+ yyParenDepth = 0;
+ yyCurLineNo = 1;
+ yyBracketLineNo = 1;
+ yyBraceLineNo = 1;
+ yyParenLineNo = 1;
+ yyAtNewline = true;
+ yyMinBraceDepth = 0;
+ inDefine = false;
+}
+
+
+std::ostream &CppParser::yyMsg(int line)
+{
+ return std::cerr << qPrintable(yyFileName) << ':' << (line ? line : yyLineNo) << ": ";
+}
+
+void CppParser::setInput(const QString &in)
+{
+ yyInStr = in;
+ yyFileName = QString();
+ yySourceCodec = 0;
+ yyForceUtf8 = true;
+}
+
+void CppParser::setInput(QTextStream &ts, const QString &fileName)
+{
+ yyInStr = ts.readAll();
+ yyFileName = fileName;
+ yySourceCodec = ts.codec();
+ yyForceUtf8 = false;
+}
+
+/*
+ The first part of this source file is the C++ tokenizer. We skip
+ most of C++; the only tokens that interest us are defined here.
+ Thus, the code fragment
+
+ int main()
+ {
+ printf("Hello, world!\n");
+ return 0;
+ }
+
+ is broken down into the following tokens (Tok_ omitted):
+
+ Ident Ident LeftParen RightParen
+ LeftBrace
+ Ident LeftParen String RightParen Semicolon
+ return Semicolon
+ RightBrace.
+
+ The 0 doesn't produce any token.
+*/
+
+uint CppParser::getChar()
+{
+ const ushort *uc = yyInPtr;
+ forever {
+ ushort c = *uc;
+ if (!c) {
+ yyInPtr = uc;
+ return EOF;
+ }
+ ++uc;
+ if (c == '\\') {
+ ushort cc = *uc;
+ if (cc == '\n') {
+ ++yyCurLineNo;
+ ++uc;
+ continue;
+ }
+ if (cc == '\r') {
+ ++yyCurLineNo;
+ ++uc;
+ if (*uc == '\n')
+ ++uc;
+ continue;
+ }
+ }
+ if (c == '\r') {
+ if (*uc == '\n')
+ ++uc;
+ c = '\n';
+ ++yyCurLineNo;
+ yyAtNewline = true;
+ } else if (c == '\n') {
+ ++yyCurLineNo;
+ yyAtNewline = true;
+ } else if (c != ' ' && c != '\t' && c != '#') {
+ yyAtNewline = false;
+ }
+ yyInPtr = uc;
+ return c;
+ }
+}
+
+// This ignores commas, parens and comments.
+// IOW, it understands only a single, simple argument.
+bool CppParser::getMacroArgs()
+{
+ // Failing this assertion would mean losing the preallocated buffer.
+ Q_ASSERT(yyWord.isDetached());
+ yyWord.resize(0);
+
+ while (isspace(yyCh))
+ yyCh = getChar();
+ if (yyCh != '(')
+ return false;
+ do {
+ yyCh = getChar();
+ } while (isspace(yyCh));
+ ushort *ptr = (ushort *)yyWord.unicode();
+ while (yyCh != ')') {
+ if (yyCh == EOF)
+ return false;
+ *ptr++ = yyCh;
+ yyCh = getChar();
+ }
+ yyCh = getChar();
+ for (; ptr != (ushort *)yyWord.unicode() && isspace(*(ptr - 1)); --ptr) ;
+ yyWord.resize(ptr - (ushort *)yyWord.unicode());
+ return true;
+}
+
+STRING(Q_OBJECT);
+STRING(Q_DECLARE_TR_FUNCTIONS);
+STRING(QT_TR_NOOP);
+STRING(QT_TRID_NOOP);
+STRING(QT_TRANSLATE_NOOP);
+STRING(QT_TRANSLATE_NOOP3);
+STRING(QT_TR_NOOP_UTF8);
+STRING(QT_TRANSLATE_NOOP_UTF8);
+STRING(QT_TRANSLATE_NOOP3_UTF8);
+STRING(class);
+// QTranslator::findMessage() has the same parameters as QApplication::translate()
+STRING(findMessage);
+STRING(friend);
+STRING(namespace);
+STRING(operator);
+STRING(qtTrId);
+STRING(return);
+STRING(struct);
+STRING(TR);
+STRING(Tr);
+STRING(tr);
+STRING(trUtf8);
+STRING(translate);
+STRING(using);
+
+uint CppParser::getToken()
+{
+ restart:
+ // Failing this assertion would mean losing the preallocated buffer.
+ Q_ASSERT(yyWord.isDetached());
+ yyWord.resize(0);
+
+ while (yyCh != EOF) {
+ yyLineNo = yyCurLineNo;
+
+ if (yyCh == '#' && yyAtNewline) {
+ /*
+ Early versions of lupdate complained about
+ unbalanced braces in the following code:
+
+ #ifdef ALPHA
+ while (beta) {
+ #else
+ while (gamma) {
+ #endif
+ delta;
+ }
+
+ The code contains, indeed, two opening braces for
+ one closing brace; yet there's no reason to panic.
+
+ The solution is to remember yyBraceDepth as it was
+ when #if, #ifdef or #ifndef was met, and to set
+ yyBraceDepth to that value when meeting #elif or
+ #else.
+ */
+ do {
+ yyCh = getChar();
+ } while (isspace(yyCh) && yyCh != '\n');
+
+ switch (yyCh) {
+ case 'd': // define
+ // Skip over the name of the define to avoid it being interpreted as c++ code
+ do { // Rest of "define"
+ yyCh = getChar();
+ if (yyCh == EOF)
+ return Tok_Eof;
+ if (yyCh == '\n')
+ goto restart;
+ } while (!isspace(yyCh));
+ do { // Space beween "define" and macro name
+ yyCh = getChar();
+ if (yyCh == EOF)
+ return Tok_Eof;
+ if (yyCh == '\n')
+ goto restart;
+ } while (isspace(yyCh));
+ do { // Macro name
+ if (yyCh == '(') {
+ // Argument list. Follows the name without a space, and no
+ // paren nesting is possible.
+ do {
+ yyCh = getChar();
+ if (yyCh == EOF)
+ return Tok_Eof;
+ if (yyCh == '\n')
+ goto restart;
+ } while (yyCh != ')');
+ break;
+ }
+ yyCh = getChar();
+ if (yyCh == EOF)
+ return Tok_Eof;
+ if (yyCh == '\n')
+ goto restart;
+ } while (!isspace(yyCh));
+ do { // Shortcut the immediate newline case if no comments follow.
+ yyCh = getChar();
+ if (yyCh == EOF)
+ return Tok_Eof;
+ if (yyCh == '\n')
+ goto restart;
+ } while (isspace(yyCh));
+
+ saveState(&savedState);
+ yyMinBraceDepth = yyBraceDepth;
+ inDefine = true;
+ goto restart;
+ case 'i':
+ yyCh = getChar();
+ if (yyCh == 'f') {
+ // if, ifdef, ifndef
+ yyIfdefStack.push(IfdefState(yyBracketDepth, yyBraceDepth, yyParenDepth));
+ yyCh = getChar();
+ } else if (yyCh == 'n') {
+ // include
+ do {
+ yyCh = getChar();
+ } while (yyCh != EOF && !isspace(yyCh));
+ do {
+ yyCh = getChar();
+ } while (isspace(yyCh));
+ int tChar;
+ if (yyCh == '"')
+ tChar = '"';
+ else if (yyCh == '<')
+ tChar = '>';
+ else
+ break;
+ ushort *ptr = (ushort *)yyWord.unicode();
+ forever {
+ yyCh = getChar();
+ if (yyCh == EOF || yyCh == '\n')
+ break;
+ if (yyCh == tChar) {
+ yyCh = getChar();
+ break;
+ }
+ *ptr++ = yyCh;
+ }
+ yyWord.resize(ptr - (ushort *)yyWord.unicode());
+ return (tChar == '"') ? Tok_QuotedInclude : Tok_AngledInclude;
+ }
+ break;
+ case 'e':
+ yyCh = getChar();
+ if (yyCh == 'l') {
+ // elif, else
+ if (!yyIfdefStack.isEmpty()) {
+ IfdefState &is = yyIfdefStack.top();
+ if (is.elseLine != -1) {
+ if (yyBracketDepth != is.bracketDepth1st
+ || yyBraceDepth != is.braceDepth1st
+ || yyParenDepth != is.parenDepth1st)
+ yyMsg(is.elseLine)
+ << qPrintable(LU::tr("Parenthesis/bracket/brace mismatch between "
+ "#if and #else branches; using #if branch\n"));
+ } else {
+ is.bracketDepth1st = yyBracketDepth;
+ is.braceDepth1st = yyBraceDepth;
+ is.parenDepth1st = yyParenDepth;
+ saveState(&is.state);
+ }
+ is.elseLine = yyLineNo;
+ yyBracketDepth = is.bracketDepth;
+ yyBraceDepth = is.braceDepth;
+ yyParenDepth = is.parenDepth;
+ }
+ yyCh = getChar();
+ } else if (yyCh == 'n') {
+ // endif
+ if (!yyIfdefStack.isEmpty()) {
+ IfdefState is = yyIfdefStack.pop();
+ if (is.elseLine != -1) {
+ if (yyBracketDepth != is.bracketDepth1st
+ || yyBraceDepth != is.braceDepth1st
+ || yyParenDepth != is.parenDepth1st)
+ yyMsg(is.elseLine)
+ << qPrintable(LU::tr("Parenthesis/brace mismatch between "
+ "#if and #else branches; using #if branch\n"));
+ yyBracketDepth = is.bracketDepth1st;
+ yyBraceDepth = is.braceDepth1st;
+ yyParenDepth = is.parenDepth1st;
+ loadState(&is.state);
+ }
+ }
+ yyCh = getChar();
+ }
+ break;
+ }
+ // Optimization: skip over rest of preprocessor directive
+ do {
+ if (yyCh == '/') {
+ yyCh = getChar();
+ if (yyCh == '/') {
+ do {
+ yyCh = getChar();
+ } while (yyCh != EOF && yyCh != '\n');
+ break;
+ } else if (yyCh == '*') {
+ bool metAster = false;
+
+ forever {
+ yyCh = getChar();
+ if (yyCh == EOF) {
+ yyMsg() << qPrintable(LU::tr("Unterminated C++ comment\n"));
+ break;
+ }
+
+ if (yyCh == '*') {
+ metAster = true;
+ } else if (metAster && yyCh == '/') {
+ yyCh = getChar();
+ break;
+ } else {
+ metAster = false;
+ }
+ }
+ }
+ } else {
+ yyCh = getChar();
+ }
+ } while (yyCh != '\n' && yyCh != EOF);
+ yyCh = getChar();
+ } else if ((yyCh >= 'A' && yyCh <= 'Z') || (yyCh >= 'a' && yyCh <= 'z') || yyCh == '_') {
+ ushort *ptr = (ushort *)yyWord.unicode();
+ do {
+ *ptr++ = yyCh;
+ yyCh = getChar();
+ } while ((yyCh >= 'A' && yyCh <= 'Z') || (yyCh >= 'a' && yyCh <= 'z')
+ || (yyCh >= '0' && yyCh <= '9') || yyCh == '_');
+ yyWord.resize(ptr - (ushort *)yyWord.unicode());
+
+ //qDebug() << "IDENT: " << yyWord;
+
+ switch (yyWord.unicode()[0].unicode()) {
+ case 'Q':
+ if (yyWord == strQ_OBJECT)
+ return Tok_Q_OBJECT;
+ if (yyWord == strQ_DECLARE_TR_FUNCTIONS)
+ return Tok_Q_DECLARE_TR_FUNCTIONS;
+ if (yyWord == strQT_TR_NOOP)
+ return Tok_tr;
+ if (yyWord == strQT_TRID_NOOP)
+ return Tok_trid;
+ if (yyWord == strQT_TRANSLATE_NOOP)
+ return Tok_translate;
+ if (yyWord == strQT_TRANSLATE_NOOP3)
+ return Tok_translate;
+ if (yyWord == strQT_TR_NOOP_UTF8)
+ return Tok_trUtf8;
+ if (yyWord == strQT_TRANSLATE_NOOP_UTF8)
+ return Tok_translateUtf8;
+ if (yyWord == strQT_TRANSLATE_NOOP3_UTF8)
+ return Tok_translateUtf8;
+ break;
+ case 'T':
+ // TR() for when all else fails
+ if (yyWord == strTR || yyWord == strTr)
+ return Tok_tr;
+ break;
+ case 'c':
+ if (yyWord == strclass)
+ return Tok_class;
+ break;
+ case 'f':
+ /*
+ QTranslator::findMessage() has the same parameters as
+ QApplication::translate().
+ */
+ if (yyWord == strfindMessage)
+ return Tok_translate;
+ if (yyWord == strfriend)
+ return Tok_friend;
+ break;
+ case 'n':
+ if (yyWord == strnamespace)
+ return Tok_namespace;
+ break;
+ case 'o':
+ if (yyWord == stroperator) {
+ // Operator overload declaration/definition.
+ // We need to prevent those characters from confusing the followup
+ // parsing. Actually using them does not add value, so just eat them.
+ while (isspace(yyCh))
+ yyCh = getChar();
+ while (yyCh == '+' || yyCh == '-' || yyCh == '*' || yyCh == '/' || yyCh == '%'
+ || yyCh == '=' || yyCh == '<' || yyCh == '>' || yyCh == '!'
+ || yyCh == '&' || yyCh == '|' || yyCh == '~' || yyCh == '^'
+ || yyCh == '[' || yyCh == ']')
+ yyCh = getChar();
+ }
+ break;
+ case 'q':
+ if (yyWord == strqtTrId)
+ return Tok_trid;
+ break;
+ case 'r':
+ if (yyWord == strreturn)
+ return Tok_return;
+ break;
+ case 's':
+ if (yyWord == strstruct)
+ return Tok_class;
+ break;
+ case 't':
+ if (yyWord == strtr)
+ return Tok_tr;
+ if (yyWord == strtrUtf8)
+ return Tok_trUtf8;
+ if (yyWord == strtranslate)
+ return Tok_translate;
+ break;
+ case 'u':
+ if (yyWord == strusing)
+ return Tok_using;
+ break;
+ }
+ return Tok_Ident;
+ } else {
+ switch (yyCh) {
+ case '\n':
+ if (inDefine) {
+ loadState(&savedState);
+ prospectiveContext.clear();
+ yyBraceDepth = yyMinBraceDepth;
+ yyMinBraceDepth = 0;
+ inDefine = false;
+ }
+ yyCh = getChar();
+ break;
+ case '/':
+ yyCh = getChar();
+ if (yyCh == '/') {
+ ushort *ptr = (ushort *)yyWord.unicode() + yyWord.length();
+ do {
+ yyCh = getChar();
+ if (yyCh == EOF)
+ break;
+ *ptr++ = yyCh;
+ } while (yyCh != '\n');
+ yyWord.resize(ptr - (ushort *)yyWord.unicode());
+ } else if (yyCh == '*') {
+ bool metAster = false;
+ ushort *ptr = (ushort *)yyWord.unicode() + yyWord.length();
+
+ forever {
+ yyCh = getChar();
+ if (yyCh == EOF) {
+ yyMsg() << qPrintable(LU::tr("Unterminated C++ comment\n"));
+ break;
+ }
+ *ptr++ = yyCh;
+
+ if (yyCh == '*')
+ metAster = true;
+ else if (metAster && yyCh == '/')
+ break;
+ else
+ metAster = false;
+ }
+ yyWord.resize(ptr - (ushort *)yyWord.unicode() - 2);
+
+ yyCh = getChar();
+ }
+ return Tok_Comment;
+ case '"': {
+ ushort *ptr = (ushort *)yyWord.unicode() + yyWord.length();
+ yyCh = getChar();
+ while (yyCh != EOF && yyCh != '\n' && yyCh != '"') {
+ if (yyCh == '\\') {
+ yyCh = getChar();
+ if (yyCh == EOF || yyCh == '\n')
+ break;
+ *ptr++ = '\\';
+ }
+ *ptr++ = yyCh;
+ yyCh = getChar();
+ }
+ yyWord.resize(ptr - (ushort *)yyWord.unicode());
+
+ if (yyCh != '"')
+ yyMsg() << qPrintable(LU::tr("Unterminated C++ string\n"));
+ else
+ yyCh = getChar();
+ return Tok_String;
+ }
+ case '-':
+ yyCh = getChar();
+ if (yyCh == '>') {
+ yyCh = getChar();
+ return Tok_Arrow;
+ }
+ break;
+ case ':':
+ yyCh = getChar();
+ if (yyCh == ':') {
+ yyCh = getChar();
+ return Tok_ColonColon;
+ }
+ return Tok_Colon;
+ // Incomplete: '<' might be part of '<=' or of template syntax.
+ // The main intent of not completely ignoring it is to break
+ // parsing of things like std::cout << QObject::tr() as
+ // context std::cout::QObject (see Task 161106)
+ case '=':
+ yyCh = getChar();
+ return Tok_Equals;
+ case '>':
+ case '<':
+ yyCh = getChar();
+ return Tok_Other;
+ case '\'':
+ yyCh = getChar();
+ if (yyCh == '\\')
+ yyCh = getChar();
+
+ forever {
+ if (yyCh == EOF || yyCh == '\n') {
+ yyMsg() << "Unterminated C++ character\n";
+ break;
+ }
+ yyCh = getChar();
+ if (yyCh == '\'') {
+ yyCh = getChar();
+ break;
+ }
+ }
+ break;
+ case '{':
+ if (yyBraceDepth == 0)
+ yyBraceLineNo = yyCurLineNo;
+ yyBraceDepth++;
+ yyCh = getChar();
+ return Tok_LeftBrace;
+ case '}':
+ if (yyBraceDepth == yyMinBraceDepth) {
+ if (!inDefine)
+ yyMsg(yyCurLineNo)
+ << qPrintable(LU::tr("Excess closing brace in C++ code"
+ " (or abuse of the C++ preprocessor)\n"));
+ // Avoid things getting messed up even more
+ yyCh = getChar();
+ return Tok_Semicolon;
+ }
+ yyBraceDepth--;
+ yyCh = getChar();
+ return Tok_RightBrace;
+ case '(':
+ if (yyParenDepth == 0)
+ yyParenLineNo = yyCurLineNo;
+ yyParenDepth++;
+ yyCh = getChar();
+ return Tok_LeftParen;
+ case ')':
+ if (yyParenDepth == 0)
+ yyMsg(yyCurLineNo)
+ << qPrintable(LU::tr("Excess closing parenthesis in C++ code"
+ " (or abuse of the C++ preprocessor)\n"));
+ else
+ yyParenDepth--;
+ yyCh = getChar();
+ return Tok_RightParen;
+ case '[':
+ if (yyBracketDepth == 0)
+ yyBracketLineNo = yyCurLineNo;
+ yyBracketDepth++;
+ yyCh = getChar();
+ return Tok_LeftBracket;
+ case ']':
+ if (yyBracketDepth == 0)
+ yyMsg(yyCurLineNo)
+ << qPrintable(LU::tr("Excess closing bracket in C++ code"
+ " (or abuse of the C++ preprocessor)\n"));
+ else
+ yyBracketDepth--;
+ yyCh = getChar();
+ return Tok_RightBracket;
+ case ',':
+ yyCh = getChar();
+ return Tok_Comma;
+ case ';':
+ yyCh = getChar();
+ return Tok_Semicolon;
+ case '0':
+ yyCh = getChar();
+ if (yyCh == 'x') {
+ do {
+ yyCh = getChar();
+ } while ((yyCh >= '0' && yyCh <= '9')
+ || (yyCh >= 'a' && yyCh <= 'f') || (yyCh >= 'A' && yyCh <= 'F'));
+ return Tok_Integer;
+ }
+ if (yyCh < '0' || yyCh > '9')
+ return Tok_Null;
+ // Fallthrough
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ do {
+ yyCh = getChar();
+ } while (yyCh >= '0' && yyCh <= '9');
+ return Tok_Integer;
+ default:
+ yyCh = getChar();
+ break;
+ }
+ }
+ }
+ return Tok_Eof;
+}
+
+/*
+ The second part of this source file are namespace/class related
+ utilities for the third part.
+*/
+
+void CppParser::saveState(SavedState *state)
+{
+ state->namespaces = namespaces;
+ state->namespaceDepths = namespaceDepths;
+ state->functionContext = functionContext;
+ state->functionContextUnresolved = functionContextUnresolved;
+ state->pendingContext = pendingContext;
+}
+
+void CppParser::loadState(const SavedState *state)
+{
+ namespaces = state->namespaces;
+ namespaceDepths = state->namespaceDepths;
+ functionContext = state->functionContext;
+ functionContextUnresolved = state->functionContextUnresolved;
+ pendingContext = state->pendingContext;
+}
+
+Namespace *CppParser::modifyNamespace(NamespaceList *namespaces, bool haveLast)
+{
+ Namespace *pns, *ns = &results->rootNamespace;
+ for (int i = 1; i < namespaces->count(); ++i) {
+ pns = ns;
+ if (!(ns = pns->children.value(namespaces->at(i)))) {
+ do {
+ ns = new Namespace;
+ if (haveLast || i < namespaces->count() - 1)
+ if (const Namespace *ons = findNamespace(*namespaces, i + 1))
+ ns->classDef = ons->classDef;
+ pns->children.insert(namespaces->at(i), ns);
+ pns = ns;
+ } while (++i < namespaces->count());
+ break;
+ }
+ }
+ return ns;
+}
+
+QString CppParser::stringifyNamespace(const NamespaceList &namespaces)
+{
+ QString ret;
+ for (int i = 1; i < namespaces.count(); ++i) {
+ if (i > 1)
+ ret += QLatin1String("::");
+ ret += namespaces.at(i).value();
+ }
+ return ret;
+}
+
+QStringList CppParser::stringListifyNamespace(const NamespaceList &namespaces)
+{
+ QStringList ret;
+ for (int i = 1; i < namespaces.count(); ++i)
+ ret << namespaces.at(i).value();
+ return ret;
+}
+
+bool CppParser::visitNamespace(const NamespaceList &namespaces, int nsCount,
+ VisitNamespaceCallback callback, void *context,
+ VisitRecorder &vr, const ParseResults *rslt) const
+{
+ const Namespace *ns = &rslt->rootNamespace;
+ for (int i = 1; i < nsCount; ++i)
+ if (!(ns = ns->children.value(namespaces.at(i))))
+ goto supers;
+ if ((this->*callback)(ns, context))
+ return true;
+supers:
+ foreach (const ParseResults *sup, rslt->includes)
+ if (vr.tryVisit(sup->fileId)
+ && visitNamespace(namespaces, nsCount, callback, context, vr, sup))
+ return true;
+ return false;
+}
+
+bool CppParser::visitNamespace(const NamespaceList &namespaces, int nsCount,
+ VisitNamespaceCallback callback, void *context) const
+{
+ VisitRecorder vr;
+ return visitNamespace(namespaces, nsCount, callback, context, vr, results);
+}
+
+QStringList CppParser::stringListifySegments(const QList<HashString> &segments)
+{
+ QStringList ret;
+ for (int i = 0; i < segments.count(); ++i)
+ ret << segments.at(i).value();
+ return ret;
+}
+
+struct QualifyOneData {
+ QualifyOneData(const NamespaceList &ns, int nsc, const HashString &seg, NamespaceList *rslvd,
+ QSet<HashStringList> *visited)
+ : namespaces(ns), nsCount(nsc), segment(seg), resolved(rslvd), visitedUsings(visited)
+ {}
+
+ const NamespaceList &namespaces;
+ int nsCount;
+ const HashString &segment;
+ NamespaceList *resolved;
+ QSet<HashStringList> *visitedUsings;
+};
+
+bool CppParser::qualifyOneCallbackOwn(const Namespace *ns, void *context) const
+{
+ QualifyOneData *data = (QualifyOneData *)context;
+ if (ns->children.contains(data->segment)) {
+ *data->resolved = data->namespaces.mid(0, data->nsCount);
+ *data->resolved << data->segment;
+ return true;
+ }
+ QHash<HashString, NamespaceList>::ConstIterator nsai = ns->aliases.constFind(data->segment);
+ if (nsai != ns->aliases.constEnd()) {
+ const NamespaceList &nsl = *nsai;
+ if (nsl.last().value().isEmpty()) { // Delayed alias resolution
+ NamespaceList &nslIn = *const_cast<NamespaceList *>(&nsl);
+ nslIn.removeLast();
+ NamespaceList nslOut;
+ if (!fullyQualify(data->namespaces, data->nsCount, nslIn, false, &nslOut, 0)) {
+ const_cast<Namespace *>(ns)->aliases.remove(data->segment);
+ return false;
+ }
+ nslIn = nslOut;
+ }
+ *data->resolved = nsl;
+ return true;
+ }
+ return false;
+}
+
+bool CppParser::qualifyOneCallbackUsing(const Namespace *ns, void *context) const
+{
+ QualifyOneData *data = (QualifyOneData *)context;
+ foreach (const HashStringList &use, ns->usings)
+ if (!data->visitedUsings->contains(use)) {
+ data->visitedUsings->insert(use);
+ if (qualifyOne(use.value(), use.value().count(), data->segment, data->resolved,
+ data->visitedUsings))
+ return true;
+ }
+ return false;
+}
+
+bool CppParser::qualifyOne(const NamespaceList &namespaces, int nsCnt, const HashString &segment,
+ NamespaceList *resolved, QSet<HashStringList> *visitedUsings) const
+{
+ QualifyOneData data(namespaces, nsCnt, segment, resolved, visitedUsings);
+
+ if (visitNamespace(namespaces, nsCnt, &CppParser::qualifyOneCallbackOwn, &data))
+ return true;
+
+ return visitNamespace(namespaces, nsCnt, &CppParser::qualifyOneCallbackUsing, &data);
+}
+
+bool CppParser::qualifyOne(const NamespaceList &namespaces, int nsCnt, const HashString &segment,
+ NamespaceList *resolved) const
+{
+ QSet<HashStringList> visitedUsings;
+
+ return qualifyOne(namespaces, nsCnt, segment, resolved, &visitedUsings);
+}
+
+bool CppParser::fullyQualify(const NamespaceList &namespaces, int nsCnt,
+ const QList<HashString> &segments, bool isDeclaration,
+ NamespaceList *resolved, QStringList *unresolved) const
+{
+ int nsIdx;
+ int initSegIdx;
+
+ if (segments.first().value().isEmpty()) {
+ // fully qualified
+ if (segments.count() == 1) {
+ resolved->clear();
+ *resolved << HashString(QString());
+ return true;
+ }
+ initSegIdx = 1;
+ nsIdx = 0;
+ } else {
+ initSegIdx = 0;
+ nsIdx = nsCnt - 1;
+ }
+
+ do {
+ if (qualifyOne(namespaces, nsIdx + 1, segments[initSegIdx], resolved)) {
+ int segIdx = initSegIdx;
+ while (++segIdx < segments.count()) {
+ if (!qualifyOne(*resolved, resolved->count(), segments[segIdx], resolved)) {
+ if (unresolved)
+ *unresolved = stringListifySegments(segments.mid(segIdx));
+ return false;
+ }
+ }
+ return true;
+ }
+ } while (!isDeclaration && --nsIdx >= 0);
+ resolved->clear();
+ *resolved << HashString(QString());
+ if (unresolved)
+ *unresolved = stringListifySegments(segments.mid(initSegIdx));
+ return false;
+}
+
+bool CppParser::fullyQualify(const NamespaceList &namespaces,
+ const QList<HashString> &segments, bool isDeclaration,
+ NamespaceList *resolved, QStringList *unresolved) const
+{
+ return fullyQualify(namespaces, namespaces.count(),
+ segments, isDeclaration, resolved, unresolved);
+}
+
+bool CppParser::fullyQualify(const NamespaceList &namespaces,
+ const QString &quali, bool isDeclaration,
+ NamespaceList *resolved, QStringList *unresolved) const
+{
+ static QString strColons(QLatin1String("::"));
+
+ QList<HashString> segments;
+ foreach (const QString &str, quali.split(strColons)) // XXX slow, but needs to be fast(?)
+ segments << HashString(str);
+ return fullyQualify(namespaces, segments, isDeclaration, resolved, unresolved);
+}
+
+bool CppParser::findNamespaceCallback(const Namespace *ns, void *context) const
+{
+ *((const Namespace **)context) = ns;
+ return true;
+}
+
+const Namespace *CppParser::findNamespace(const NamespaceList &namespaces, int nsCount) const
+{
+ const Namespace *ns = 0;
+ if (nsCount == -1)
+ nsCount = namespaces.count();
+ visitNamespace(namespaces, nsCount, &CppParser::findNamespaceCallback, &ns);
+ return ns;
+}
+
+void CppParser::enterNamespace(NamespaceList *namespaces, const HashString &name)
+{
+ *namespaces << name;
+ if (!findNamespace(*namespaces))
+ modifyNamespace(namespaces, false);
+}
+
+void CppParser::truncateNamespaces(NamespaceList *namespaces, int length)
+{
+ if (namespaces->count() > length)
+ namespaces->erase(namespaces->begin() + length, namespaces->end());
+}
+
+/*
+ Functions for processing include files.
+*/
+
+ParseResultHash &CppFiles::parsedFiles()
+{
+ static ParseResultHash parsed;
+
+ return parsed;
+}
+
+TranslatorHash &CppFiles::translatedFiles()
+{
+ static TranslatorHash tors;
+
+ return tors;
+}
+
+QSet<QString> &CppFiles::blacklistedFiles()
+{
+ static QSet<QString> blacklisted;
+
+ return blacklisted;
+}
+
+const ParseResults *CppFiles::getResults(const QString &cleanFile)
+{
+ return parsedFiles().value(cleanFile);
+}
+
+void CppFiles::setResults(const QString &cleanFile, const ParseResults *results)
+{
+ parsedFiles().insert(cleanFile, results);
+}
+
+const Translator *CppFiles::getTranslator(const QString &cleanFile)
+{
+ return translatedFiles().value(cleanFile);
+}
+
+void CppFiles::setTranslator(const QString &cleanFile, const Translator *tor)
+{
+ translatedFiles().insert(cleanFile, tor);
+}
+
+bool CppFiles::isBlacklisted(const QString &cleanFile)
+{
+ return blacklistedFiles().contains(cleanFile);
+}
+
+void CppFiles::setBlacklisted(const QString &cleanFile)
+{
+ blacklistedFiles().insert(cleanFile);
+}
+
+static bool isHeader(const QString &name)
+{
+ QString fileExt = QFileInfo(name).suffix();
+ return fileExt.isEmpty() || fileExt.startsWith(QLatin1Char('h'), Qt::CaseInsensitive);
+}
+
+void CppParser::processInclude(const QString &file, ConversionData &cd,
+ QSet<QString> &inclusions)
+{
+ QString cleanFile = QDir::cleanPath(file);
+
+ if (inclusions.contains(cleanFile)) {
+ yyMsg() << qPrintable(LU::tr("circular inclusion of %1\n").arg(cleanFile));
+ return;
+ }
+
+ // If the #include is in any kind of namespace, has been blacklisted previously,
+ // or is not a header file (stdc++ extensionless or *.h*), then really include
+ // it. Otherwise it is safe to process it stand-alone and re-use the parsed
+ // namespace data for inclusion into other files.
+ bool isIndirect = false;
+ if (namespaces.count() == 1 && functionContext.count() == 1
+ && functionContextUnresolved.isEmpty() && pendingContext.isEmpty()
+ && !CppFiles::isBlacklisted(cleanFile)
+ && isHeader(cleanFile)) {
+
+ if (const ParseResults *res = CppFiles::getResults(cleanFile)) {
+ results->includes.insert(res);
+ return;
+ }
+
+ isIndirect = true;
+ }
+
+ QFile f(cleanFile);
+ if (!f.open(QIODevice::ReadOnly)) {
+ yyMsg() << qPrintable(LU::tr("Cannot open %1: %2\n").arg(cleanFile, f.errorString()));
+ return;
+ }
+
+ QTextStream ts(&f);
+ ts.setCodec(yySourceCodec);
+ ts.setAutoDetectUnicode(true);
+
+ inclusions.insert(cleanFile);
+ if (isIndirect) {
+ CppParser parser;
+ foreach (const QString &projectRoot, cd.m_projectRoots)
+ if (cleanFile.startsWith(projectRoot)) {
+ parser.setTranslator(new Translator);
+ break;
+ }
+ parser.setInput(ts, cleanFile);
+ parser.parse(cd.m_defaultContext, cd, inclusions);
+ results->includes.insert(parser.recordResults(true));
+ } else {
+ CppParser parser(results);
+ parser.namespaces = namespaces;
+ parser.functionContext = functionContext;
+ parser.functionContextUnresolved = functionContextUnresolved;
+ parser.pendingContext = pendingContext;
+ parser.setInput(ts, cleanFile);
+ parser.parseInternal(cd, inclusions);
+ // Avoid that messages obtained by direct scanning are used
+ CppFiles::setBlacklisted(cleanFile);
+ }
+ inclusions.remove(cleanFile);
+}
+
+/*
+ The third part of this source file is the parser. It accomplishes
+ a very easy task: It finds all strings inside a tr() or translate()
+ call, and possibly finds out the context of the call. It supports
+ three cases: (1) the context is specified, as in
+ FunnyDialog::tr("Hello") or translate("FunnyDialog", "Hello");
+ (2) the call appears within an inlined function; (3) the call
+ appears within a function defined outside the class definition.
+*/
+
+bool CppParser::match(uint t)
+{
+ bool matches = (yyTok == t);
+ if (matches)
+ yyTok = getToken();
+ return matches;
+}
+
+bool CppParser::matchString(QString *s)
+{
+ bool matches = false;
+ s->clear();
+ forever {
+ while (yyTok == Tok_Comment)
+ yyTok = getToken();
+ if (yyTok != Tok_String)
+ return matches;
+ matches = true;
+ *s += yyWord;
+ s->detach();
+ yyTok = getToken();
+ }
+}
+
+STRING(QApplication);
+STRING(QCoreApplication);
+STRING(UnicodeUTF8);
+STRING(DefaultCodec);
+STRING(CodecForTr);
+
+bool CppParser::matchEncoding(bool *utf8)
+{
+ if (yyTok != Tok_Ident)
+ return false;
+ if (yyWord == strQApplication || yyWord == strQCoreApplication) {
+ yyTok = getToken();
+ if (yyTok == Tok_ColonColon)
+ yyTok = getToken();
+ }
+ if (yyWord == strUnicodeUTF8) {
+ *utf8 = true;
+ yyTok = getToken();
+ return true;
+ }
+ if (yyWord == strDefaultCodec || yyWord == strCodecForTr) {
+ *utf8 = false;
+ yyTok = getToken();
+ return true;
+ }
+ return false;
+}
+
+bool CppParser::matchStringOrNull(QString *s)
+{
+ return matchString(s) || match(Tok_Null);
+}
+
+/*
+ * match any expression that can return a number, which can be
+ * 1. Literal number (e.g. '11')
+ * 2. simple identifier (e.g. 'm_count')
+ * 3. simple function call (e.g. 'size()' )
+ * 4. function call on an object (e.g. 'list.size()')
+ * 5. function call on an object (e.g. 'list->size()')
+ *
+ * Other cases:
+ * size(2,4)
+ * list().size()
+ * list(a,b).size(2,4)
+ * etc...
+ */
+bool CppParser::matchExpression()
+{
+ if (match(Tok_Null) || match(Tok_Integer))
+ return true;
+
+ int parenlevel = 0;
+ while (match(Tok_Ident) || parenlevel > 0) {
+ if (yyTok == Tok_RightParen) {
+ if (parenlevel == 0) break;
+ --parenlevel;
+ yyTok = getToken();
+ } else if (yyTok == Tok_LeftParen) {
+ yyTok = getToken();
+ if (yyTok == Tok_RightParen) {
+ yyTok = getToken();
+ } else {
+ ++parenlevel;
+ }
+ } else if (yyTok == Tok_Ident) {
+ continue;
+ } else if (yyTok == Tok_Arrow) {
+ yyTok = getToken();
+ } else if (parenlevel == 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+QString CppParser::transcode(const QString &str, bool utf8)
+{
+ static const char tab[] = "abfnrtv";
+ static const char backTab[] = "\a\b\f\n\r\t\v";
+ // This function has to convert back to bytes, as C's \0* sequences work at that level.
+ const QByteArray in = yyForceUtf8 ? str.toUtf8() : tor->codec()->fromUnicode(str);
+ QByteArray out;
+
+ out.reserve(in.length());
+ for (int i = 0; i < in.length();) {
+ uchar c = in[i++];
+ if (c == '\\') {
+ if (i >= in.length())
+ break;
+ c = in[i++];
+
+ if (c == '\n')
+ continue;
+
+ if (c == 'x') {
+ QByteArray hex;
+ while (i < in.length() && isxdigit((c = in[i]))) {
+ hex += c;
+ i++;
+ }
+ out += hex.toUInt(0, 16);
+ } else if (c >= '0' && c < '8') {
+ QByteArray oct;
+ int n = 0;
+ oct += c;
+ while (n < 2 && i < in.length() && (c = in[i]) >= '0' && c < '8') {
+ i++;
+ n++;
+ oct += c;
+ }
+ out += oct.toUInt(0, 8);
+ } else {
+ const char *p = strchr(tab, c);
+ out += !p ? c : backTab[p - tab];
+ }
+ } else {
+ out += c;
+ }
+ }
+ return (utf8 || yyForceUtf8) ? QString::fromUtf8(out.constData(), out.length())
+ : tor->codec()->toUnicode(out);
+}
+
+void CppParser::recordMessage(
+ int line, const QString &context, const QString &text, const QString &comment,
+ const QString &extracomment, const QString &msgid, const TranslatorMessage::ExtraData &extra,
+ bool utf8, bool plural)
+{
+ TranslatorMessage msg(
+ transcode(context, utf8), transcode(text, utf8), transcode(comment, utf8), QString(),
+ yyFileName, line, QStringList(),
+ TranslatorMessage::Unfinished, plural);
+ msg.setExtraComment(transcode(extracomment.simplified(), utf8));
+ msg.setId(msgid);
+ msg.setExtras(extra);
+ if ((utf8 || yyForceUtf8) && !yyCodecIsUtf8 && msg.needs8Bit())
+ msg.setUtf8(true);
+ tor->append(msg);
+}
+
+void CppParser::parse(const QString &initialContext, ConversionData &cd,
+ QSet<QString> &inclusions)
+{
+ if (tor)
+ yyCodecIsUtf8 = (tor->codecName() == "UTF-8");
+
+ namespaces << HashString();
+ functionContext = namespaces;
+ functionContextUnresolved = initialContext;
+
+ parseInternal(cd, inclusions);
+}
+
+void CppParser::parseInternal(ConversionData &cd, QSet<QString> &inclusions)
+{
+ static QString strColons(QLatin1String("::"));
+
+ QString context;
+ QString text;
+ QString comment;
+ QString extracomment;
+ QString msgid;
+ QString sourcetext;
+ TranslatorMessage::ExtraData extra;
+ QString prefix;
+#ifdef DIAGNOSE_RETRANSLATABILITY
+ QString functionName;
+#endif
+ int line;
+ bool utf8;
+ bool yyTokColonSeen = false; // Start of c'tor's initializer list
+
+ yyWord.reserve(yyInStr.size()); // Rather insane. That's because we do no length checking.
+ yyInPtr = (const ushort *)yyInStr.unicode();
+ yyCh = getChar();
+ yyTok = getToken();
+ while (yyTok != Tok_Eof) {
+ // these are array indexing operations. we ignore them entirely
+ // so they don't confuse our scoping of static initializers.
+ // we enter the loop by either reading a left bracket or by an
+ // #else popping the state.
+ while (yyBracketDepth)
+ yyTok = getToken();
+ //qDebug() << "TOKEN: " << yyTok;
+ switch (yyTok) {
+ case Tok_QuotedInclude: {
+ text = QDir(QFileInfo(yyFileName).absolutePath()).absoluteFilePath(yyWord);
+ text.detach();
+ if (QFileInfo(text).isFile()) {
+ processInclude(text, cd, inclusions);
+ yyTok = getToken();
+ break;
+ }
+ }
+ /* fall through */
+ case Tok_AngledInclude: {
+ QStringList cSources = cd.m_allCSources.values(yyWord);
+ if (!cSources.isEmpty()) {
+ foreach (const QString &cSource, cSources)
+ processInclude(cSource, cd, inclusions);
+ goto incOk;
+ }
+ foreach (const QString &incPath, cd.m_includePath) {
+ text = QDir(incPath).absoluteFilePath(yyWord);
+ text.detach();
+ if (QFileInfo(text).isFile()) {
+ processInclude(text, cd, inclusions);
+ goto incOk;
+ }
+ }
+ incOk:
+ yyTok = getToken();
+ break;
+ }
+ case Tok_friend:
+ yyTok = getToken();
+ // These are forward declarations, so ignore them.
+ if (yyTok == Tok_class)
+ yyTok = getToken();
+ break;
+ case Tok_class:
+ yyTokColonSeen = false;
+ /*
+ Partial support for inlined functions.
+ */
+ yyTok = getToken();
+ if (yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) {
+ QList<HashString> quali;
+ HashString fct;
+ do {
+ /*
+ This code should execute only once, but we play
+ safe with impure definitions such as
+ 'class Q_EXPORT QMessageBox', in which case
+ 'QMessageBox' is the class name, not 'Q_EXPORT'.
+ */
+ text = yyWord;
+ text.detach();
+ fct.setValue(text);
+ yyTok = getToken();
+ } while (yyTok == Tok_Ident);
+ while (yyTok == Tok_ColonColon) {
+ yyTok = getToken();
+ if (yyTok != Tok_Ident)
+ break; // Oops ...
+ quali << fct;
+ text = yyWord;
+ text.detach();
+ fct.setValue(text);
+ yyTok = getToken();
+ }
+ while (yyTok == Tok_Comment)
+ yyTok = getToken();
+ if (yyTok == Tok_Colon) {
+ // Skip any token until '{' since we might do things wrong if we find
+ // a '::' token here.
+ do {
+ yyTok = getToken();
+ } while (yyTok != Tok_LeftBrace && yyTok != Tok_Eof);
+ } else {
+ if (yyTok != Tok_LeftBrace) {
+ // Obviously a forward declaration. We skip those, as they
+ // don't create actually usable namespaces.
+ break;
+ }
+ }
+
+ if (!quali.isEmpty()) {
+ // Forward-declared class definitions can be namespaced.
+ NamespaceList nsl;
+ if (!fullyQualify(namespaces, quali, true, &nsl, 0)) {
+ yyMsg() << "Ignoring definition of undeclared qualified class\n";
+ break;
+ }
+ namespaceDepths.push(namespaces.count());
+ namespaces = nsl;
+ } else {
+ namespaceDepths.push(namespaces.count());
+ }
+ enterNamespace(&namespaces, fct);
+
+ functionContext = namespaces;
+ functionContextUnresolved.clear(); // Pointless
+ prospectiveContext.clear();
+ pendingContext.clear();
+
+ yyTok = getToken();
+ }
+ break;
+ case Tok_namespace:
+ yyTokColonSeen = false;
+ yyTok = getToken();
+ if (yyTok == Tok_Ident) {
+ text = yyWord;
+ text.detach();
+ HashString ns = HashString(text);
+ yyTok = getToken();
+ if (yyTok == Tok_LeftBrace) {
+ namespaceDepths.push(namespaces.count());
+ enterNamespace(&namespaces, ns);
+
+ functionContext = namespaces;
+ functionContextUnresolved.clear();
+ prospectiveContext.clear();
+ pendingContext.clear();
+ yyTok = getToken();
+ } else if (yyTok == Tok_Equals) {
+ // e.g. namespace Is = OuterSpace::InnerSpace;
+ QList<HashString> fullName;
+ yyTok = getToken();
+ if (yyTok == Tok_ColonColon)
+ fullName.append(HashString(QString()));
+ while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
+ if (yyTok == Tok_Ident) {
+ text = yyWord;
+ text.detach();
+ fullName.append(HashString(text));
+ }
+ yyTok = getToken();
+ }
+ if (fullName.isEmpty())
+ break;
+ fullName.append(HashString(QString())); // Mark as unresolved
+ modifyNamespace(&namespaces)->aliases[ns] = fullName;
+ }
+ } else if (yyTok == Tok_LeftBrace) {
+ // Anonymous namespace
+ namespaceDepths.push(namespaces.count());
+ yyTok = getToken();
+ }
+ break;
+ case Tok_using:
+ yyTok = getToken();
+ // XXX this should affect only the current scope, not the entire current namespace
+ if (yyTok == Tok_namespace) {
+ QList<HashString> fullName;
+ yyTok = getToken();
+ if (yyTok == Tok_ColonColon)
+ fullName.append(HashString(QString()));
+ while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
+ if (yyTok == Tok_Ident) {
+ text = yyWord;
+ text.detach();
+ fullName.append(HashString(text));
+ }
+ yyTok = getToken();
+ }
+ NamespaceList nsl;
+ if (fullyQualify(namespaces, fullName, false, &nsl, 0))
+ modifyNamespace(&namespaces)->usings << HashStringList(nsl);
+ } else {
+ QList<HashString> fullName;
+ if (yyTok == Tok_ColonColon)
+ fullName.append(HashString(QString()));
+ while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) {
+ if (yyTok == Tok_Ident) {
+ text = yyWord;
+ text.detach();
+ fullName.append(HashString(text));
+ }
+ yyTok = getToken();
+ }
+ if (fullName.isEmpty())
+ break;
+ // using-declarations cannot rename classes, so the last element of
+ // fullName is already the resolved name we actually want.
+ // As we do no resolution here, we'll collect useless usings of data
+ // members and methods as well. This is no big deal.
+ HashString &ns = fullName.last();
+ fullName.append(HashString(QString())); // Mark as unresolved
+ modifyNamespace(&namespaces)->aliases[ns] = fullName;
+ }
+ break;
+ case Tok_tr:
+ case Tok_trUtf8:
+ if (!tor)
+ goto case_default;
+ if (!sourcetext.isEmpty())
+ yyMsg() << qPrintable(LU::tr("//% cannot be used with tr() / QT_TR_NOOP(). Ignoring\n"));
+ utf8 = (yyTok == Tok_trUtf8);
+ line = yyLineNo;
+ yyTok = getToken();
+ if (match(Tok_LeftParen) && matchString(&text) && !text.isEmpty()) {
+ comment.clear();
+ bool plural = false;
+
+ if (match(Tok_RightParen)) {
+ // no comment
+ } else if (match(Tok_Comma) && matchStringOrNull(&comment)) { //comment
+ if (match(Tok_RightParen)) {
+ // ok,
+ } else if (match(Tok_Comma)) {
+ plural = true;
+ }
+ }
+ if (!pendingContext.isEmpty() && !prefix.startsWith(strColons)) {
+ QStringList unresolved;
+ if (!fullyQualify(namespaces, pendingContext, true, &functionContext, &unresolved)) {
+ functionContextUnresolved = unresolved.join(strColons);
+ yyMsg() << qPrintable(LU::tr("Qualifying with unknown namespace/class %1::%2\n")
+ .arg(stringifyNamespace(functionContext)).arg(unresolved.first()));
+ }
+ pendingContext.clear();
+ }
+ if (prefix.isEmpty()) {
+ if (functionContextUnresolved.isEmpty()) {
+ int idx = functionContext.length();
+ if (idx < 2) {
+ yyMsg() << qPrintable(LU::tr("tr() cannot be called without context\n"));
+ break;
+ }
+ Namespace *fctx;
+ while (!(fctx = findNamespace(functionContext, idx)->classDef)->hasTrFunctions) {
+ if (idx == 1) {
+ context = stringifyNamespace(functionContext);
+ fctx = findNamespace(functionContext)->classDef;
+ if (!fctx->complained) {
+ yyMsg() << qPrintable(LU::tr("Class '%1' lacks Q_OBJECT macro\n")
+ .arg(context));
+ fctx->complained = true;
+ }
+ goto gotctx;
+ }
+ --idx;
+ }
+ if (fctx->trQualification.isEmpty()) {
+ context.clear();
+ for (int i = 1;;) {
+ context += functionContext.at(i).value();
+ if (++i == idx)
+ break;
+ context += strColons;
+ }
+ fctx->trQualification = context;
+ } else {
+ context = fctx->trQualification;
+ }
+ } else {
+ context = (stringListifyNamespace(functionContext)
+ << functionContextUnresolved).join(strColons);
+ }
+ } else {
+#ifdef DIAGNOSE_RETRANSLATABILITY
+ int last = prefix.lastIndexOf(strColons);
+ QString className = prefix.mid(last == -1 ? 0 : last + 2);
+ if (!className.isEmpty() && className == functionName) {
+ yyMsg() << qPrintable(LU::tr("It is not recommended to call tr() from within a constructor '%1::%2'\n")
+ .arg(className).arg(functionName));
+ }
+#endif
+ prefix.chop(2);
+ NamespaceList nsl;
+ QStringList unresolved;
+ if (fullyQualify(functionContext, prefix, false, &nsl, &unresolved)) {
+ Namespace *fctx = findNamespace(nsl)->classDef;
+ if (fctx->trQualification.isEmpty()) {
+ context = stringifyNamespace(nsl);
+ fctx->trQualification = context;
+ } else {
+ context = fctx->trQualification;
+ }
+ if (!fctx->hasTrFunctions && !fctx->complained) {
+ yyMsg() << qPrintable(LU::tr("Class '%1' lacks Q_OBJECT macro\n").arg(context));
+ fctx->complained = true;
+ }
+ } else {
+ context = (stringListifyNamespace(nsl) + unresolved).join(strColons);
+ }
+ prefix.clear();
+ }
+
+ gotctx:
+ recordMessage(line, context, text, comment, extracomment, msgid, extra, utf8, plural);
+ }
+ sourcetext.clear(); // Will have warned about that already
+ extracomment.clear();
+ msgid.clear();
+ extra.clear();
+ break;
+ case Tok_translateUtf8:
+ case Tok_translate:
+ if (!tor)
+ goto case_default;
+ if (!sourcetext.isEmpty())
+ yyMsg() << qPrintable(LU::tr("//% cannot be used with translate() / QT_TRANSLATE_NOOP(). Ignoring\n"));
+ utf8 = (yyTok == Tok_translateUtf8);
+ line = yyLineNo;
+ yyTok = getToken();
+ if (match(Tok_LeftParen)
+ && matchString(&context)
+ && match(Tok_Comma)
+ && matchString(&text) && !text.isEmpty())
+ {
+ comment.clear();
+ bool plural = false;
+ if (!match(Tok_RightParen)) {
+ // look for comment
+ if (match(Tok_Comma) && matchStringOrNull(&comment)) {
+ if (!match(Tok_RightParen)) {
+ // look for encoding
+ if (match(Tok_Comma)) {
+ if (matchEncoding(&utf8)) {
+ if (!match(Tok_RightParen)) {
+ // look for the plural quantifier,
+ // this can be a number, an identifier or
+ // a function call,
+ // so for simplicity we mark it as plural if
+ // we know we have a comma instead of an
+ // right parentheses.
+ plural = match(Tok_Comma);
+ }
+ } else {
+ // This can be a QTranslator::translate("context",
+ // "source", "comment", n) plural translation
+ if (matchExpression() && match(Tok_RightParen)) {
+ plural = true;
+ } else {
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ recordMessage(line, context, text, comment, extracomment, msgid, extra, utf8, plural);
+ }
+ sourcetext.clear(); // Will have warned about that already
+ extracomment.clear();
+ msgid.clear();
+ extra.clear();
+ break;
+ case Tok_trid:
+ if (!tor)
+ goto case_default;
+ if (!msgid.isEmpty())
+ yyMsg() << qPrintable(LU::tr("//= cannot be used with qtTrId() / QT_TRID_NOOP(). Ignoring\n"));
+ //utf8 = false; // Maybe use //%% or something like that
+ line = yyLineNo;
+ yyTok = getToken();
+ if (match(Tok_LeftParen) && matchString(&msgid) && !msgid.isEmpty()) {
+ bool plural = match(Tok_Comma);
+ recordMessage(line, QString(), sourcetext, QString(), extracomment,
+ msgid, extra, false, plural);
+ }
+ sourcetext.clear();
+ extracomment.clear();
+ msgid.clear();
+ extra.clear();
+ break;
+ case Tok_Q_DECLARE_TR_FUNCTIONS:
+ if (getMacroArgs()) {
+ Namespace *ns = modifyNamespace(&namespaces);
+ ns->hasTrFunctions = true;
+ ns->trQualification = yyWord;
+ ns->trQualification.detach();
+ }
+ yyTok = getToken();
+ break;
+ case Tok_Q_OBJECT:
+ modifyNamespace(&namespaces)->hasTrFunctions = true;
+ yyTok = getToken();
+ break;
+ case Tok_Ident:
+ prefix += yyWord;
+ prefix.detach();
+ yyTok = getToken();
+ if (yyTok != Tok_ColonColon) {
+ prefix.clear();
+ if (yyTok == Tok_Ident && !yyParenDepth)
+ prospectiveContext.clear();
+ }
+ break;
+ case Tok_Comment: {
+ if (!tor)
+ goto case_default;
+ const QChar *ptr = yyWord.unicode();
+ if (*ptr == QLatin1Char(':') && ptr[1].isSpace()) {
+ yyWord.remove(0, 2);
+ extracomment += yyWord;
+ extracomment.detach();
+ } else if (*ptr == QLatin1Char('=') && ptr[1].isSpace()) {
+ yyWord.remove(0, 2);
+ msgid = yyWord.simplified();
+ msgid.detach();
+ } else if (*ptr == QLatin1Char('~') && ptr[1].isSpace()) {
+ yyWord.remove(0, 2);
+ text = yyWord.trimmed();
+ int k = text.indexOf(QLatin1Char(' '));
+ if (k > -1)
+ extra.insert(text.left(k), text.mid(k + 1).trimmed());
+ text.clear();
+ } else if (*ptr == QLatin1Char('%') && ptr[1].isSpace()) {
+ sourcetext.reserve(sourcetext.length() + yyWord.length() - 2);
+ ushort *ptr = (ushort *)sourcetext.data() + sourcetext.length();
+ int p = 2, c;
+ forever {
+ if (p >= yyWord.length())
+ break;
+ c = yyWord.unicode()[p++].unicode();
+ if (isspace(c))
+ continue;
+ if (c != '"') {
+ yyMsg() << qPrintable(LU::tr("Unexpected character in meta string\n"));
+ break;
+ }
+ forever {
+ if (p >= yyWord.length()) {
+ whoops:
+ yyMsg() << qPrintable(LU::tr("Unterminated meta string\n"));
+ break;
+ }
+ c = yyWord.unicode()[p++].unicode();
+ if (c == '"')
+ break;
+ if (c == '\\') {
+ if (p >= yyWord.length())
+ goto whoops;
+ c = yyWord.unicode()[p++].unicode();
+ if (c == '\n')
+ goto whoops;
+ *ptr++ = '\\';
+ }
+ *ptr++ = c;
+ }
+ }
+ sourcetext.resize(ptr - (ushort *)sourcetext.data());
+ } else {
+ const ushort *uc = (const ushort *)yyWord.unicode(); // Is zero-terminated
+ int idx = 0;
+ ushort c;
+ while ((c = uc[idx]) == ' ' || c == '\t' || c == '\n')
+ ++idx;
+ if (!memcmp(uc + idx, MagicComment.unicode(), MagicComment.length() * 2)) {
+ idx += MagicComment.length();
+ comment = QString::fromRawData(yyWord.unicode() + idx,
+ yyWord.length() - idx).simplified();
+ int k = comment.indexOf(QLatin1Char(' '));
+ if (k == -1) {
+ context = comment;
+ } else {
+ context = comment.left(k);
+ comment.remove(0, k + 1);
+ TranslatorMessage msg(
+ transcode(context, false), QString(),
+ transcode(comment, false), QString(),
+ yyFileName, yyLineNo, QStringList(),
+ TranslatorMessage::Finished, false);
+ msg.setExtraComment(transcode(extracomment.simplified(), false));
+ extracomment.clear();
+ tor->append(msg);
+ tor->setExtras(extra);
+ extra.clear();
+ }
+ }
+ }
+ yyTok = getToken();
+ break;
+ }
+ case Tok_Arrow:
+ yyTok = getToken();
+ if (yyTok == Tok_tr || yyTok == Tok_trUtf8)
+ yyMsg() << qPrintable(LU::tr("Cannot invoke tr() like this\n"));
+ break;
+ case Tok_ColonColon:
+ if (yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0 && !yyTokColonSeen)
+ prospectiveContext = prefix;
+ prefix += strColons;
+ yyTok = getToken();
+#ifdef DIAGNOSE_RETRANSLATABILITY
+ if (yyTok == Tok_Ident && yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) {
+ functionName = yyWord;
+ functionName.detach();
+ }
+#endif
+ break;
+ case Tok_RightBrace:
+ if (yyBraceDepth + 1 == namespaceDepths.count()) // class or namespace
+ truncateNamespaces(&namespaces, namespaceDepths.pop());
+ if (yyBraceDepth == namespaceDepths.count()) {
+ // function, class or namespace
+ if (!yyBraceDepth && !directInclude) {
+ truncateNamespaces(&functionContext, 1);
+ functionContextUnresolved = cd.m_defaultContext;
+ } else {
+ functionContext = namespaces;
+ functionContextUnresolved.clear();
+ }
+ pendingContext.clear();
+ }
+ // fallthrough
+ case Tok_Semicolon:
+ prospectiveContext.clear();
+ prefix.clear();
+ if (!sourcetext.isEmpty() || !extracomment.isEmpty() || !msgid.isEmpty() || !extra.isEmpty()) {
+ yyMsg() << qPrintable(LU::tr("Discarding unconsumed meta data\n"));
+ sourcetext.clear();
+ extracomment.clear();
+ msgid.clear();
+ extra.clear();
+ }
+ yyTokColonSeen = false;
+ yyTok = getToken();
+ break;
+ case Tok_Colon:
+ if (!prospectiveContext.isEmpty()
+ && yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0)
+ pendingContext = prospectiveContext;
+ yyTokColonSeen = true;
+ yyTok = getToken();
+ break;
+ case Tok_LeftBrace:
+ if (!prospectiveContext.isEmpty()
+ && yyBraceDepth == namespaceDepths.count() + 1 && yyParenDepth == 0)
+ pendingContext = prospectiveContext;
+ // fallthrough
+ case Tok_LeftParen:
+ case Tok_RightParen:
+ yyTokColonSeen = false;
+ yyTok = getToken();
+ break;
+ default:
+ if (!yyParenDepth)
+ prospectiveContext.clear();
+ // fallthrough
+ case Tok_Equals: // for static initializers; other cases make no difference
+ case Tok_RightBracket: // ignoring indexing; same reason
+ case_default:
+ yyTok = getToken();
+ break;
+ }
+ }
+
+ if (yyBraceDepth != 0)
+ yyMsg(yyBraceLineNo)
+ << qPrintable(LU::tr("Unbalanced opening brace in C++ code"
+ " (or abuse of the C++ preprocessor)\n"));
+ else if (yyParenDepth != 0)
+ yyMsg(yyParenLineNo)
+ << qPrintable(LU::tr("Unbalanced opening parenthesis in C++ code"
+ " (or abuse of the C++ preprocessor)\n"));
+ else if (yyBracketDepth != 0)
+ yyMsg(yyBracketLineNo)
+ << qPrintable(LU::tr("Unbalanced opening bracket in C++ code"
+ " (or abuse of the C++ preprocessor)\n"));
+}
+
+const ParseResults *CppParser::recordResults(bool isHeader)
+{
+ if (tor) {
+ if (tor->messageCount()) {
+ CppFiles::setTranslator(yyFileName, tor);
+ } else {
+ delete tor;
+ tor = 0;
+ }
+ }
+ if (isHeader) {
+ const ParseResults *pr;
+ if (!tor && results->includes.count() == 1
+ && results->rootNamespace.children.isEmpty()
+ && results->rootNamespace.aliases.isEmpty()
+ && results->rootNamespace.usings.isEmpty()) {
+ // This is a forwarding header. Slash it.
+ pr = *results->includes.begin();
+ delete results;
+ } else {
+ results->fileId = nextFileId++;
+ pr = results;
+ }
+ CppFiles::setResults(yyFileName, pr);
+ return pr;
+ } else {
+ delete results;
+ return 0;
+ }
+}
+
+/*
+ Fetches tr() calls in C++ code in UI files (inside "<function>"
+ tag). This mechanism is obsolete.
+*/
+void fetchtrInlinedCpp(const QString &in, Translator &translator, const QString &context)
+{
+ CppParser parser;
+ parser.setInput(in);
+ ConversionData cd;
+ QSet<QString> inclusions;
+ parser.setTranslator(&translator);
+ parser.parse(context, cd, inclusions);
+ parser.deleteResults();
+}
+
+void loadCPP(Translator &translator, const QStringList &filenames, ConversionData &cd)
+{
+ QByteArray codecName = cd.m_codecForSource.isEmpty()
+ ? translator.codecName() : cd.m_codecForSource;
+ QTextCodec *codec = QTextCodec::codecForName(codecName);
+
+ foreach (const QString &filename, filenames) {
+ if (CppFiles::getResults(filename) || CppFiles::isBlacklisted(filename))
+ continue;
+
+ QFile file(filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ cd.appendError(LU::tr("Cannot open %1: %2").arg(filename, file.errorString()));
+ continue;
+ }
+
+ CppParser parser;
+ QTextStream ts(&file);
+ ts.setCodec(codec);
+ ts.setAutoDetectUnicode(true);
+ parser.setInput(ts, filename);
+ if (cd.m_outputCodec.isEmpty() && ts.codec()->name() == "UTF-16")
+ translator.setCodecName("System");
+ Translator *tor = new Translator;
+ tor->setCodecName(translator.codecName());
+ parser.setTranslator(tor);
+ QSet<QString> inclusions;
+ parser.parse(cd.m_defaultContext, cd, inclusions);
+ parser.recordResults(isHeader(filename));
+ }
+
+ foreach (const QString &filename, filenames)
+ if (!CppFiles::isBlacklisted(filename))
+ if (const Translator *tor = CppFiles::getTranslator(filename))
+ foreach (const TranslatorMessage &msg, tor->messages())
+ translator.extend(msg);
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/lupdate/java.cpp b/src/linguist/lupdate/java.cpp
new file mode 100644
index 000000000..bc3649912
--- /dev/null
+++ b/src/linguist/lupdate/java.cpp
@@ -0,0 +1,645 @@
+/****************************************************************************
+**
+** 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 "lupdate.h"
+
+#include <translator.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QFile>
+#include <QtCore/QRegExp>
+#include <QtCore/QStack>
+#include <QtCore/QStack>
+#include <QtCore/QString>
+#include <QtCore/QTextCodec>
+#include <QtCore/QCoreApplication>
+
+#include <iostream>
+
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+class LU {
+ Q_DECLARE_TR_FUNCTIONS(LUpdate)
+};
+
+enum { Tok_Eof, Tok_class, Tok_return, Tok_tr,
+ Tok_translate, Tok_Ident, Tok_Package,
+ Tok_Comment, Tok_String, Tok_Colon, Tok_Dot,
+ Tok_LeftBrace, Tok_RightBrace, Tok_LeftParen,
+ Tok_RightParen, Tok_Comma, Tok_Semicolon,
+ Tok_Integer, Tok_Plus, Tok_PlusPlus, Tok_PlusEq, Tok_null };
+
+class Scope
+{
+ public:
+ QString name;
+ enum Type {Clazz, Function, Other} type;
+ int line;
+
+ Scope(const QString & name, Type type, int line) :
+ name(name),
+ type(type),
+ line(line)
+ {}
+
+ ~Scope()
+ {}
+};
+
+/*
+ The tokenizer maintains the following global variables. The names
+ should be self-explanatory.
+*/
+
+static QString yyFileName;
+static QChar yyCh;
+static QString yyIdent;
+static QString yyComment;
+static QString yyString;
+
+
+static qlonglong yyInteger;
+static int yyParenDepth;
+static int yyLineNo;
+static int yyCurLineNo;
+static int yyParenLineNo;
+static int yyTok;
+
+// the string to read from and current position in the string
+static QString yyInStr;
+static int yyInPos;
+
+// The parser maintains the following global variables.
+static QString yyPackage;
+static QStack<Scope*> yyScope;
+static QString yyDefaultContext;
+
+std::ostream &yyMsg(int line = 0)
+{
+ return std::cerr << qPrintable(yyFileName) << ':' << (line ? line : yyLineNo) << ": ";
+}
+
+static QChar getChar()
+{
+ if (yyInPos >= yyInStr.size())
+ return EOF;
+ QChar c = yyInStr[yyInPos++];
+ if (c.unicode() == '\n')
+ ++yyCurLineNo;
+ return c.unicode();
+}
+
+static int getToken()
+{
+ const char tab[] = "bfnrt\"\'\\";
+ const char backTab[] = "\b\f\n\r\t\"\'\\";
+
+ yyIdent.clear();
+ yyComment.clear();
+ yyString.clear();
+
+ while ( yyCh != EOF ) {
+ yyLineNo = yyCurLineNo;
+
+ if ( yyCh.isLetter() || yyCh.toLatin1() == '_' ) {
+ do {
+ yyIdent.append(yyCh);
+ yyCh = getChar();
+ } while ( yyCh.isLetterOrNumber() || yyCh.toLatin1() == '_' );
+
+ if (yyTok != Tok_Dot) {
+ switch ( yyIdent.at(0).toLatin1() ) {
+ case 'r':
+ if ( yyIdent == QLatin1String("return") )
+ return Tok_return;
+ break;
+ case 'c':
+ if ( yyIdent == QLatin1String("class") )
+ return Tok_class;
+ break;
+ case 'n':
+ if ( yyIdent == QLatin1String("null") )
+ return Tok_null;
+ break;
+ }
+ }
+ switch ( yyIdent.at(0).toLatin1() ) {
+ case 'T':
+ // TR() for when all else fails
+ if ( yyIdent == QLatin1String("TR") )
+ return Tok_tr;
+ break;
+ case 'p':
+ if( yyIdent == QLatin1String("package") )
+ return Tok_Package;
+ break;
+ case 't':
+ if ( yyIdent == QLatin1String("tr") )
+ return Tok_tr;
+ if ( yyIdent == QLatin1String("translate") )
+ return Tok_translate;
+ }
+ return Tok_Ident;
+ } else {
+ switch ( yyCh.toLatin1() ) {
+
+ case '/':
+ yyCh = getChar();
+ if ( yyCh == QLatin1Char('/') ) {
+ do {
+ yyCh = getChar();
+ if (yyCh == EOF)
+ break;
+ yyComment.append(yyCh);
+ } while (yyCh != QLatin1Char('\n'));
+ return Tok_Comment;
+
+ } else if ( yyCh == QLatin1Char('*') ) {
+ bool metAster = false;
+ bool metAsterSlash = false;
+
+ while ( !metAsterSlash ) {
+ yyCh = getChar();
+ if ( yyCh == EOF ) {
+ yyMsg() << qPrintable(LU::tr("Unterminated Java comment.\n"));
+ return Tok_Comment;
+ }
+
+ yyComment.append( yyCh );
+
+ if ( yyCh == QLatin1Char('*') )
+ metAster = true;
+ else if ( metAster && yyCh == QLatin1Char('/') )
+ metAsterSlash = true;
+ else
+ metAster = false;
+ }
+ yyComment.chop(2);
+ yyCh = getChar();
+
+ return Tok_Comment;
+ }
+ break;
+ case '"':
+ yyCh = getChar();
+
+ while ( yyCh != EOF && yyCh != QLatin1Char('\n') && yyCh != QLatin1Char('"') ) {
+ if ( yyCh == QLatin1Char('\\') ) {
+ yyCh = getChar();
+ if ( yyCh == QLatin1Char('u') ) {
+ yyCh = getChar();
+ uint unicode(0);
+ for (int i = 4; i > 0; --i) {
+ unicode = unicode << 4;
+ if( yyCh.isDigit() ) {
+ unicode += yyCh.digitValue();
+ }
+ else {
+ int sub(yyCh.toLower().toAscii() - 87);
+ if( sub > 15 || sub < 10) {
+ yyMsg() << qPrintable(LU::tr("Invalid Unicode value.\n"));
+ break;
+ }
+ unicode += sub;
+ }
+ yyCh = getChar();
+ }
+ yyString.append(QChar(unicode));
+ }
+ else if ( yyCh == QLatin1Char('\n') ) {
+ yyCh = getChar();
+ }
+ else {
+ yyString.append( QLatin1Char(backTab[strchr( tab, yyCh.toAscii() ) - tab]) );
+ yyCh = getChar();
+ }
+ } else {
+ yyString.append(yyCh);
+ yyCh = getChar();
+ }
+ }
+
+ if ( yyCh != QLatin1Char('"') )
+ yyMsg() << qPrintable(LU::tr("Unterminated string.\n"));
+
+ yyCh = getChar();
+
+ return Tok_String;
+
+ case ':':
+ yyCh = getChar();
+ return Tok_Colon;
+ case '\'':
+ yyCh = getChar();
+
+ if ( yyCh == QLatin1Char('\\') )
+ yyCh = getChar();
+ do {
+ yyCh = getChar();
+ } while ( yyCh != EOF && yyCh != QLatin1Char('\'') );
+ yyCh = getChar();
+ break;
+ case '{':
+ yyCh = getChar();
+ return Tok_LeftBrace;
+ case '}':
+ yyCh = getChar();
+ return Tok_RightBrace;
+ case '(':
+ if (yyParenDepth == 0)
+ yyParenLineNo = yyCurLineNo;
+ yyParenDepth++;
+ yyCh = getChar();
+ return Tok_LeftParen;
+ case ')':
+ if (yyParenDepth == 0)
+ yyParenLineNo = yyCurLineNo;
+ yyParenDepth--;
+ yyCh = getChar();
+ return Tok_RightParen;
+ case ',':
+ yyCh = getChar();
+ return Tok_Comma;
+ case '.':
+ yyCh = getChar();
+ return Tok_Dot;
+ case ';':
+ yyCh = getChar();
+ return Tok_Semicolon;
+ case '+':
+ yyCh = getChar();
+ if (yyCh == QLatin1Char('+')) {
+ yyCh = getChar();
+ return Tok_PlusPlus;
+ }
+ if( yyCh == QLatin1Char('=') ){
+ yyCh = getChar();
+ return Tok_PlusEq;
+ }
+ return Tok_Plus;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ QByteArray ba;
+ ba += yyCh.toLatin1();
+ yyCh = getChar();
+ bool hex = yyCh == QLatin1Char('x');
+ if ( hex ) {
+ ba += yyCh.toLatin1();
+ yyCh = getChar();
+ }
+ while ( hex ? isxdigit(yyCh.toLatin1()) : yyCh.isDigit() ) {
+ ba += yyCh.toLatin1();
+ yyCh = getChar();
+ }
+ bool ok;
+ yyInteger = ba.toLongLong(&ok);
+ if (ok) return Tok_Integer;
+ break;
+ }
+ default:
+ yyCh = getChar();
+ }
+ }
+ }
+ return Tok_Eof;
+}
+
+static bool match( int t )
+{
+ bool matches = ( yyTok == t );
+ if ( matches )
+ yyTok = getToken();
+ return matches;
+}
+
+static bool matchString( QString &s )
+{
+ if ( yyTok != Tok_String )
+ return false;
+
+ s = yyString;
+ yyTok = getToken();
+ while ( yyTok == Tok_Plus ) {
+ yyTok = getToken();
+ if (yyTok == Tok_String)
+ s += yyString;
+ else {
+ yyMsg() << qPrintable(LU::tr(
+ "String used in translation can contain only literals"
+ " concatenated with other literals, not expressions or numbers.\n"));
+ return false;
+ }
+ yyTok = getToken();
+ }
+ return true;
+}
+
+static bool matchStringOrNull(QString &s)
+{
+ bool matches = matchString(s);
+ if (!matches) {
+ matches = (yyTok == Tok_null);
+ if (matches)
+ yyTok = getToken();
+ }
+ return matches;
+}
+
+/*
+ * match any expression that can return a number, which can be
+ * 1. Literal number (e.g. '11')
+ * 2. simple identifier (e.g. 'm_count')
+ * 3. simple function call (e.g. 'size()' )
+ * 4. function call on an object (e.g. 'list.size()')
+ * 5. function call on an object (e.g. 'list->size()')
+ *
+ * Other cases:
+ * size(2,4)
+ * list().size()
+ * list(a,b).size(2,4)
+ * etc...
+ */
+static bool matchExpression()
+{
+ if (match(Tok_Integer)) {
+ return true;
+ }
+
+ int parenlevel = 0;
+ while (match(Tok_Ident) || parenlevel > 0) {
+ if (yyTok == Tok_RightParen) {
+ if (parenlevel == 0) break;
+ --parenlevel;
+ yyTok = getToken();
+ } else if (yyTok == Tok_LeftParen) {
+ yyTok = getToken();
+ if (yyTok == Tok_RightParen) {
+ yyTok = getToken();
+ } else {
+ ++parenlevel;
+ }
+ } else if (yyTok == Tok_Ident) {
+ continue;
+ } else if (parenlevel == 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static const QString context()
+{
+ QString context(yyPackage);
+ bool innerClass = false;
+ for (int i = 0; i < yyScope.size(); ++i) {
+ if (yyScope.at(i)->type == Scope::Clazz) {
+ if (innerClass)
+ context.append(QLatin1String("$"));
+ else
+ context.append(QLatin1String("."));
+
+ context.append(yyScope.at(i)->name);
+ innerClass = true;
+ }
+ }
+ return context.isEmpty() ? yyDefaultContext : context;
+}
+
+static void recordMessage(
+ Translator *tor, const QString &context, const QString &text, const QString &comment,
+ const QString &extracomment, bool plural)
+{
+ TranslatorMessage msg(
+ context, text, comment, QString(),
+ yyFileName, yyLineNo, QStringList(),
+ TranslatorMessage::Unfinished, plural);
+ msg.setExtraComment(extracomment.simplified());
+ tor->extend(msg);
+}
+
+static void parse( Translator *tor )
+{
+ QString text;
+ QString com;
+ QString extracomment;
+
+ yyCh = getChar();
+
+ yyTok = getToken();
+ while ( yyTok != Tok_Eof ) {
+ switch ( yyTok ) {
+ case Tok_class:
+ yyTok = getToken();
+ if(yyTok == Tok_Ident) {
+ yyScope.push(new Scope(yyIdent, Scope::Clazz, yyLineNo));
+ }
+ else {
+ yyMsg() << qPrintable(LU::tr("'class' must be followed by a class name.\n"));
+ break;
+ }
+ while (!match(Tok_LeftBrace)) {
+ yyTok = getToken();
+ }
+ break;
+
+ case Tok_tr:
+ yyTok = getToken();
+ if ( match(Tok_LeftParen) && matchString(text) ) {
+ com.clear();
+ bool plural = false;
+
+ if ( match(Tok_RightParen) ) {
+ // no comment
+ } else if (match(Tok_Comma) && matchStringOrNull(com)) { //comment
+ if ( match(Tok_RightParen)) {
+ // ok,
+ } else if (match(Tok_Comma)) {
+ plural = true;
+ }
+ }
+ if (!text.isEmpty())
+ recordMessage(tor, context(), text, com, extracomment, plural);
+ }
+ break;
+ case Tok_translate:
+ {
+ QString contextOverride;
+ yyTok = getToken();
+ if ( match(Tok_LeftParen) &&
+ matchString(contextOverride) &&
+ match(Tok_Comma) &&
+ matchString(text) ) {
+
+ com.clear();
+ bool plural = false;
+ if (!match(Tok_RightParen)) {
+ // look for comment
+ if ( match(Tok_Comma) && matchStringOrNull(com)) {
+ if (!match(Tok_RightParen)) {
+ if (match(Tok_Comma) && matchExpression() && match(Tok_RightParen)) {
+ plural = true;
+ } else {
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ if (!text.isEmpty())
+ recordMessage(tor, contextOverride, text, com, extracomment, plural);
+ }
+ }
+ break;
+
+ case Tok_Ident:
+ yyTok = getToken();
+ break;
+
+ case Tok_Comment:
+ if (yyComment.startsWith(QLatin1Char(':'))) {
+ yyComment.remove(0, 1);
+ extracomment.append(yyComment);
+ }
+ yyTok = getToken();
+ break;
+
+ case Tok_RightBrace:
+ if ( yyScope.isEmpty() ) {
+ yyMsg() << qPrintable(LU::tr("Excess closing brace.\n"));
+ }
+ else
+ delete (yyScope.pop());
+ extracomment.clear();
+ yyTok = getToken();
+ break;
+
+ case Tok_LeftBrace:
+ yyScope.push(new Scope(QString(), Scope::Other, yyLineNo));
+ yyTok = getToken();
+ break;
+
+ case Tok_Semicolon:
+ extracomment.clear();
+ yyTok = getToken();
+ break;
+
+ case Tok_Package:
+ yyTok = getToken();
+ while(!match(Tok_Semicolon)) {
+ switch(yyTok) {
+ case Tok_Ident:
+ yyPackage.append(yyIdent);
+ break;
+ case Tok_Dot:
+ yyPackage.append(QLatin1String("."));
+ break;
+ default:
+ yyMsg() << qPrintable(LU::tr("'package' must be followed by package name.\n"));
+ break;
+ }
+ yyTok = getToken();
+ }
+ break;
+
+ default:
+ yyTok = getToken();
+ }
+ }
+
+ if ( !yyScope.isEmpty() )
+ yyMsg(yyScope.top()->line) << qPrintable(LU::tr("Unbalanced opening brace.\n"));
+ else if ( yyParenDepth != 0 )
+ yyMsg(yyParenLineNo) << qPrintable(LU::tr("Unbalanced opening parenthesis.\n"));
+}
+
+
+bool loadJava(Translator &translator, const QString &filename, ConversionData &cd)
+{
+ QFile file(filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ cd.appendError(LU::tr("Cannot open %1: %2").arg(filename, file.errorString()));
+ return false;
+ }
+
+ yyDefaultContext = cd.m_defaultContext;
+ yyInPos = -1;
+ yyFileName = filename;
+ yyPackage.clear();
+ yyScope.clear();
+ yyTok = -1;
+ yyParenDepth = 0;
+ yyCurLineNo = 0;
+ yyParenLineNo = 1;
+
+ QTextStream ts(&file);
+ QByteArray codecName;
+ if (!cd.m_codecForSource.isEmpty())
+ codecName = cd.m_codecForSource;
+ else
+ codecName = translator.codecName(); // Just because it should be latin1 already
+ ts.setCodec(QTextCodec::codecForName(codecName));
+ ts.setAutoDetectUnicode(true);
+ yyInStr = ts.readAll();
+ yyInPos = 0;
+ yyFileName = filename;
+ yyCurLineNo = 1;
+ yyParenLineNo = 1;
+
+ parse(&translator);
+
+ // Java uses UTF-16 internally and Jambi makes UTF-8 for tr() purposes of it.
+ translator.setCodecName("UTF-8");
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/lupdate/lupdate.1 b/src/linguist/lupdate/lupdate.1
new file mode 100644
index 000000000..80b7429b3
--- /dev/null
+++ b/src/linguist/lupdate/lupdate.1
@@ -0,0 +1,153 @@
+.TH lupdate 1 "18 October 2001" "Nokia Corporation and/or its subsidiary(-ies)" \" -*- nroff -*-
+.\"
+.\" 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 QtGui module 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$
+.\"
+.SH NAME
+lupdate \- update Qt Linguist translation files
+.SH SYNOPSIS
+.B lupdate
+.RI "[ " options " ] " project-file
+.br
+.B lupdate
+.RI "[ " options " ] " source-files " -ts " ts-files
+.SH DESCRIPTION
+This page documents the
+.B Qt Linguist Update
+tool for the Qt GUI toolkit.
+.B Lupdate
+reads a qmake/tmake project file (.pro file), finds the translatable
+strings in the specified source, header and interface files, and
+updates the translation files (TS files) specified in it. The
+translation files are given to the translator who uses
+.B Qt Linguist
+to read the files and insert the translations.
+.PP
+The TS file format is a simple human-readable XML format that can be
+used with version control systems if required.
+.PP
+.SH OPTIONS
+.TP
+.I "-disable-heuristic {sametext|similartext|number}"
+Disable the named merge heuristic. Can be specified multiple times.
+.TP
+.I "-extensions <ext>[,<ext>...]"
+Process files with the given extensions only.
+The extension list must be separated with commas, not with whitespace.
+Default: 'ui,c,c++,cc,cpp,cxx,ch,h,h++,hh,hpp,hxx'.
+.TP
+.I "-help"
+Display the usage and exit.
+.TP
+.I "-locations {absolute|relative|none}"
+Specify/override how source code references are saved in TS files.
+Default is absolute.
+.TP
+.I "-no-obsolete"
+Drop all obsolete strings.
+.TP
+.I "-no-recursive"
+Do not recursively scan the following directories.
+.TP
+.I "-no-sort"
+Do not sort contexts in TS files.
+.TP
+.I "-pluralonly"
+Only include plural form messages.
+.TP
+.I "-pro <filename>"
+Name of a .pro file. Useful for files with .pro
+file syntax but different file suffix
+.TP
+.I "-recursive"
+Recursively scan the following directories.
+.TP
+.I "-silent"
+Do not explain what is being done.
+.TP
+.I "-source-language <language>[_<region>]"
+Specify/override the language of the source strings. Defaults to
+POSIX if not specified and the file does not name it yet.
+.TP
+.I "-target-language <language>[_<region>]"
+Specify/override the language of the translation.
+The target language is guessed from the file name if this option
+is not specified and the file contents name no language yet.
+.I "-version"
+Display the version of
+.B lupdate
+and exit.
+.SH USAGE
+Here is an example .pro file that can be given to
+.B lupdate:
+.PP
+.in +4
+.nf
+HEADERS = funnydialog.h \\
+ wackywidget.h
+SOURCES = funnydialog.cpp \\
+ main.cpp \\
+ wackywidget.cpp
+FORMS = fancybox.ui
+TRANSLATIONS = gnomovision_dk.ts \\
+ gnomovision_fi.ts \\
+ gnomovision_no.ts \\
+ gnomovision_se.ts
+.fi
+.in -4
+.PP
+When running
+.B lupdate
+on this project file, the translatable strings in all the files
+listed in the HEADERS, SOURCES and FORMS entries will be put in
+the translation files listed in the TRANSLATIONS entry. Previous
+translations will be reused as far as possible, and translated
+strings that have vanished from the source files are marked obsolete.
+.PP
+.B lupdate
+can also be invoked with a list of C++ source files, UI files
+and TS files:
+.PP
+.in +4
+.nf
+lupdate *.cpp *.h *.ui -ts gnomovision_dk.ts
+.fi
+.in -4
+.SH "SEE ALSO"
+.BR lrelease (1)
+and
+.BR http://qt.nokia.com/doc/i18n.html
diff --git a/src/linguist/lupdate/lupdate.exe.manifest b/src/linguist/lupdate/lupdate.exe.manifest
new file mode 100644
index 000000000..e0b2f734a
--- /dev/null
+++ b/src/linguist/lupdate/lupdate.exe.manifest
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
+ manifestVersion="1.0">
+ <!-- Make sure Vista UAC does not believe lupdate is an installer -->
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel
+ level="asInvoker"
+ uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+</assembly> \ No newline at end of file
diff --git a/src/linguist/lupdate/lupdate.h b/src/linguist/lupdate/lupdate.h
new file mode 100644
index 000000000..e8d84d85e
--- /dev/null
+++ b/src/linguist/lupdate/lupdate.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** 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 LUPDATE_H
+#define LUPDATE_H
+
+#include "qglobal.h"
+
+#include <QList>
+
+QT_BEGIN_NAMESPACE
+
+class ConversionData;
+class QString;
+class QStringList;
+class Translator;
+class TranslatorMessage;
+
+enum UpdateOption {
+ Verbose = 1,
+ NoObsolete = 2,
+ PluralOnly = 4,
+ NoSort = 8,
+ HeuristicSameText = 16,
+ HeuristicSimilarText = 32,
+ HeuristicNumber = 64,
+ AbsoluteLocations = 256,
+ RelativeLocations = 512,
+ NoLocations = 1024,
+ NoUiLines = 2048
+};
+
+Q_DECLARE_FLAGS(UpdateOptions, UpdateOption)
+Q_DECLARE_OPERATORS_FOR_FLAGS(UpdateOptions)
+
+Translator merge(const Translator &tor, const Translator &virginTor,
+ UpdateOptions options, QString &err);
+
+void fetchtrInlinedCpp(const QString &in, Translator &translator, const QString &context);
+void loadCPP(Translator &translator, const QStringList &filenames, ConversionData &cd);
+bool loadJava(Translator &translator, const QString &filename, ConversionData &cd);
+bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd);
+bool loadUI(Translator &translator, const QString &filename, ConversionData &cd);
+bool loadQml(Translator &translator, const QString &filename, ConversionData &cd);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/linguist/lupdate/lupdate.pro b/src/linguist/lupdate/lupdate.pro
new file mode 100644
index 000000000..3ed909ae5
--- /dev/null
+++ b/src/linguist/lupdate/lupdate.pro
@@ -0,0 +1,49 @@
+TEMPLATE = app
+TARGET = lupdate
+DESTDIR = ../../../bin
+
+QT -= gui
+
+CONFIG += qt warn_on console
+CONFIG -= app_bundle
+
+build_all:!build_pass {
+ CONFIG -= build_all
+ CONFIG += release
+}
+
+include(../shared/formats.pri)
+include(../shared/proparser.pri)
+
+include($$QT_SOURCE_TREE/src/declarative/qml/parser/parser.pri)
+INCLUDEPATH += $$QT_SOURCE_TREE/src/declarative/qml $$QT_BUILD_TREE/include/QtDeclarative
+
+SOURCES += \
+ main.cpp \
+ merge.cpp \
+ ../shared/simtexth.cpp \
+ \
+ cpp.cpp \
+ java.cpp \
+ qscript.cpp \
+ qdeclarative.cpp \
+ ui.cpp
+
+HEADERS += \
+ lupdate.h \
+ ../shared/simtexth.h
+
+DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII
+
+
+win32:RC_FILE = winmanifest.rc
+
+embed_manifest_exe:win32-msvc2005 {
+ # The default configuration embed_manifest_exe overrides the manifest file
+ # already embedded via RC_FILE. Vs2008 already have the necessary manifest entry
+ QMAKE_POST_LINK += $$quote(mt.exe -updateresource:$$DESTDIR/lupdate.exe -manifest \"$${PWD}\\lupdate.exe.manifest\")
+}
+
+target.path=$$[QT_INSTALL_BINS]
+INSTALLS += target
+
diff --git a/src/linguist/lupdate/main.cpp b/src/linguist/lupdate/main.cpp
new file mode 100644
index 000000000..f8e6a90e3
--- /dev/null
+++ b/src/linguist/lupdate/main.cpp
@@ -0,0 +1,730 @@
+/****************************************************************************
+**
+** 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 "lupdate.h"
+
+#include <translator.h>
+#include <profileevaluator.h>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTranslator>
+#include <QtCore/QLibraryInfo>
+
+#include <iostream>
+
+static QString m_defaultExtensions;
+
+static void printOut(const QString & out)
+{
+ std::cout << qPrintable(out);
+}
+
+static void printErr(const QString & out)
+{
+ std::cerr << qPrintable(out);
+}
+
+class LU {
+ Q_DECLARE_TR_FUNCTIONS(LUpdate)
+};
+
+static void recursiveFileInfoList(const QDir &dir,
+ const QSet<QString> &nameFilters, QDir::Filters filter,
+ QFileInfoList *fileinfolist)
+{
+ foreach (const QFileInfo &fi, dir.entryInfoList(filter))
+ if (fi.isDir())
+ recursiveFileInfoList(QDir(fi.absoluteFilePath()), nameFilters, filter, fileinfolist);
+ else if (nameFilters.contains(fi.suffix()))
+ fileinfolist->append(fi);
+}
+
+static void printUsage()
+{
+ printOut(LU::tr(
+ "Usage:\n"
+ " lupdate [options] [project-file]...\n"
+ " lupdate [options] [source-file|path|@lst-file]... -ts ts-files|@lst-file\n\n"
+ "lupdate is part of Qt's Linguist tool chain. It extracts translatable\n"
+ "messages from Qt UI files, C++, Java and JavaScript/QtScript source code.\n"
+ "Extracted messages are stored in textual translation source files (typically\n"
+ "Qt TS XML). New and modified messages can be merged into existing TS files.\n\n"
+ "Options:\n"
+ " -help Display this information and exit.\n"
+ " -no-obsolete\n"
+ " Drop all obsolete strings.\n"
+ " -extensions <ext>[,<ext>]...\n"
+ " Process files with the given extensions only.\n"
+ " The extension list must be separated with commas, not with whitespace.\n"
+ " Default: '%1'.\n"
+ " -pluralonly\n"
+ " Only include plural form messages.\n"
+ " -silent\n"
+ " Do not explain what is being done.\n"
+ " -no-sort\n"
+ " Do not sort contexts in TS files.\n"
+ " -no-recursive\n"
+ " Do not recursively scan the following directories.\n"
+ " -recursive\n"
+ " Recursively scan the following directories (default).\n"
+ " -I <includepath> or -I<includepath>\n"
+ " Additional location to look for include files.\n"
+ " May be specified multiple times.\n"
+ " -locations {absolute|relative|none}\n"
+ " Specify/override how source code references are saved in TS files.\n"
+ " Default is absolute.\n"
+ " -no-ui-lines\n"
+ " Do not record line numbers in references to UI files.\n"
+ " -disable-heuristic {sametext|similartext|number}\n"
+ " Disable the named merge heuristic. Can be specified multiple times.\n"
+ " -pro <filename>\n"
+ " Name of a .pro file. Useful for files with .pro file syntax but\n"
+ " different file suffix. Projects are recursed into and merged.\n"
+ " -source-language <language>[_<region>]\n"
+ " Specify the language of the source strings for new files.\n"
+ " Defaults to POSIX if not specified.\n"
+ " -target-language <language>[_<region>]\n"
+ " Specify the language of the translations for new files.\n"
+ " Guessed from the file name if not specified.\n"
+ " -ts <ts-file>...\n"
+ " Specify the output file(s). This will override the TRANSLATIONS\n"
+ " and nullify the CODECFORTR from possibly specified project files.\n"
+ " -codecfortr <codec>\n"
+ " Specify the codec assumed for tr() calls. Effective only with -ts.\n"
+ " -version\n"
+ " Display the version of lupdate and exit.\n"
+ " @lst-file\n"
+ " Read additional file names (one per line) from lst-file.\n"
+ ).arg(m_defaultExtensions));
+}
+
+static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFileNames,
+ bool setCodec, const QString &sourceLanguage, const QString &targetLanguage,
+ UpdateOptions options, bool *fail)
+{
+ QDir dir;
+ QString err;
+ foreach (const QString &fileName, tsFileNames) {
+ QString fn = dir.relativeFilePath(fileName);
+ ConversionData cd;
+ Translator tor;
+ cd.m_sortContexts = !(options & NoSort);
+ if (QFile(fileName).exists()) {
+ if (!tor.load(fileName, cd, QLatin1String("auto"))) {
+ printErr(cd.error());
+ *fail = true;
+ continue;
+ }
+ tor.resolveDuplicates();
+ cd.clearErrors();
+ if (setCodec && fetchedTor.codec() != tor.codec())
+ printErr(LU::tr("lupdate warning: Codec for tr() '%1' disagrees with"
+ " existing file's codec '%2'. Expect trouble.\n")
+ .arg(QString::fromLatin1(fetchedTor.codecName()),
+ QString::fromLatin1(tor.codecName())));
+ if (!targetLanguage.isEmpty() && targetLanguage != tor.languageCode())
+ printErr(LU::tr("lupdate warning: Specified target language '%1' disagrees with"
+ " existing file's language '%2'. Ignoring.\n")
+ .arg(targetLanguage, tor.languageCode()));
+ if (!sourceLanguage.isEmpty() && sourceLanguage != tor.sourceLanguageCode())
+ printErr(LU::tr("lupdate warning: Specified source language '%1' disagrees with"
+ " existing file's language '%2'. Ignoring.\n")
+ .arg(sourceLanguage, tor.sourceLanguageCode()));
+ } else {
+ if (setCodec)
+ tor.setCodec(fetchedTor.codec());
+ if (!targetLanguage.isEmpty())
+ tor.setLanguageCode(targetLanguage);
+ else
+ tor.setLanguageCode(Translator::guessLanguageCodeFromFileName(fileName));
+ if (!sourceLanguage.isEmpty())
+ tor.setSourceLanguageCode(sourceLanguage);
+ }
+ tor.makeFileNamesAbsolute(QFileInfo(fileName).absoluteDir());
+ if (options & NoLocations)
+ tor.setLocationsType(Translator::NoLocations);
+ else if (options & RelativeLocations)
+ tor.setLocationsType(Translator::RelativeLocations);
+ else if (options & AbsoluteLocations)
+ tor.setLocationsType(Translator::AbsoluteLocations);
+ if (options & Verbose)
+ printOut(LU::tr("Updating '%1'...\n").arg(fn));
+
+ UpdateOptions theseOptions = options;
+ if (tor.locationsType() == Translator::NoLocations) // Could be set from file
+ theseOptions |= NoLocations;
+ Translator out = merge(tor, fetchedTor, theseOptions, err);
+ if (setCodec)
+ out.setCodec(fetchedTor.codec());
+
+ if ((options & Verbose) && !err.isEmpty()) {
+ printOut(err);
+ err.clear();
+ }
+ if (options & PluralOnly) {
+ if (options & Verbose)
+ printOut(LU::tr("Stripping non plural forms in '%1'...\n").arg(fn));
+ out.stripNonPluralForms();
+ }
+ if (options & NoObsolete)
+ out.stripObsoleteMessages();
+ out.stripEmptyContexts();
+
+ out.normalizeTranslations(cd);
+ if (!cd.errors().isEmpty()) {
+ printErr(cd.error());
+ cd.clearErrors();
+ }
+ if (!out.save(fileName, cd, QLatin1String("auto"))) {
+ printErr(cd.error());
+ *fail = true;
+ }
+ }
+}
+
+static QStringList getSources(const char *var, const char *vvar, const QStringList &baseVPaths,
+ const QString &projectDir, const ProFileEvaluator &visitor)
+{
+ QStringList vPaths = visitor.absolutePathValues(QLatin1String(vvar), projectDir);
+ vPaths += baseVPaths;
+ vPaths.removeDuplicates();
+ return visitor.absoluteFileValues(QLatin1String(var), projectDir, vPaths, 0);
+}
+
+static QStringList getSources(const ProFileEvaluator &visitor, const QString &projectDir)
+{
+ QStringList baseVPaths;
+ baseVPaths += visitor.absolutePathValues(QLatin1String("VPATH"), projectDir);
+ baseVPaths << projectDir; // QMAKE_ABSOLUTE_SOURCE_PATH
+ baseVPaths += visitor.absolutePathValues(QLatin1String("DEPENDPATH"), projectDir);
+ baseVPaths.removeDuplicates();
+
+ QStringList sourceFiles;
+
+ // app/lib template
+ sourceFiles += getSources("SOURCES", "VPATH_SOURCES", baseVPaths, projectDir, visitor);
+
+ sourceFiles += getSources("FORMS", "VPATH_FORMS", baseVPaths, projectDir, visitor);
+ sourceFiles += getSources("FORMS3", "VPATH_FORMS3", baseVPaths, projectDir, visitor);
+
+ QStringList vPathsInc = baseVPaths;
+ vPathsInc += visitor.absolutePathValues(QLatin1String("INCLUDEPATH"), projectDir);
+ vPathsInc.removeDuplicates();
+ sourceFiles += visitor.absoluteFileValues(QLatin1String("HEADERS"), projectDir, vPathsInc, 0);
+
+ sourceFiles.removeDuplicates();
+ sourceFiles.sort();
+
+ return sourceFiles;
+}
+
+static void processSources(Translator &fetchedTor,
+ const QStringList &sourceFiles, ConversionData &cd)
+{
+ QStringList sourceFilesCpp;
+ for (QStringList::const_iterator it = sourceFiles.begin(); it != sourceFiles.end(); ++it) {
+ if (it->endsWith(QLatin1String(".java"), Qt::CaseInsensitive))
+ loadJava(fetchedTor, *it, cd);
+ else if (it->endsWith(QLatin1String(".ui"), Qt::CaseInsensitive)
+ || it->endsWith(QLatin1String(".jui"), Qt::CaseInsensitive))
+ loadUI(fetchedTor, *it, cd);
+ else if (it->endsWith(QLatin1String(".js"), Qt::CaseInsensitive)
+ || it->endsWith(QLatin1String(".qs"), Qt::CaseInsensitive))
+ loadQScript(fetchedTor, *it, cd);
+ else if (it->endsWith(QLatin1String(".qml"), Qt::CaseInsensitive))
+ loadQml(fetchedTor, *it, cd);
+ else
+ sourceFilesCpp << *it;
+ }
+ loadCPP(fetchedTor, sourceFilesCpp, cd);
+ if (!cd.error().isEmpty())
+ printErr(cd.error());
+}
+
+static void processProjects(
+ bool topLevel, bool nestComplain, const QStringList &proFiles,
+ UpdateOptions options, const QByteArray &codecForSource,
+ const QString &targetLanguage, const QString &sourceLanguage,
+ Translator *parentTor, bool *fail);
+
+static void processProject(
+ bool nestComplain, const QFileInfo &pfi, ProFileEvaluator &visitor,
+ UpdateOptions options, const QByteArray &_codecForSource,
+ const QString &targetLanguage, const QString &sourceLanguage,
+ Translator *fetchedTor, bool *fail)
+{
+ QByteArray codecForSource = _codecForSource;
+ QStringList tmp = visitor.values(QLatin1String("CODECFORSRC"));
+ if (!tmp.isEmpty()) {
+ codecForSource = tmp.last().toLatin1();
+ if (!QTextCodec::codecForName(codecForSource)) {
+ printErr(LU::tr("lupdate warning: Codec for source '%1' is invalid."
+ " Falling back to codec for tr().\n")
+ .arg(QString::fromLatin1(codecForSource)));
+ codecForSource.clear();
+ }
+ }
+ if (visitor.templateType() == ProFileEvaluator::TT_Subdirs) {
+ QStringList subProFiles;
+ QDir proDir(pfi.absoluteDir());
+ foreach (const QString &subdir, visitor.values(QLatin1String("SUBDIRS"))) {
+ QString subPro = QDir::cleanPath(proDir.absoluteFilePath(subdir));
+ QFileInfo subInfo(subPro);
+ if (subInfo.isDir())
+ subProFiles << (subPro + QLatin1Char('/')
+ + subInfo.fileName() + QLatin1String(".pro"));
+ else
+ subProFiles << subPro;
+ }
+ processProjects(false, nestComplain, subProFiles, options, codecForSource,
+ targetLanguage, sourceLanguage, fetchedTor, fail);
+ } else {
+ ConversionData cd;
+ cd.m_noUiLines = options & NoUiLines;
+ cd.m_codecForSource = codecForSource;
+ cd.m_includePath = visitor.values(QLatin1String("INCLUDEPATH"));
+ QStringList sourceFiles = getSources(visitor, pfi.absolutePath());
+ QSet<QString> sourceDirs;
+ sourceDirs.insert(QDir::cleanPath(pfi.absolutePath()) + QLatin1Char('/'));
+ foreach (const QString &sf, sourceFiles)
+ sourceDirs.insert(sf.left(sf.lastIndexOf(QLatin1Char('/')) + 1));
+ QStringList rootList = sourceDirs.toList();
+ rootList.sort();
+ for (int prev = 0, curr = 1; curr < rootList.length(); )
+ if (rootList.at(curr).startsWith(rootList.at(prev)))
+ rootList.removeAt(curr);
+ else
+ prev = curr++;
+ cd.m_projectRoots = QSet<QString>::fromList(rootList);
+ processSources(*fetchedTor, sourceFiles, cd);
+ }
+}
+
+static void processProjects(
+ bool topLevel, bool nestComplain, const QStringList &proFiles,
+ UpdateOptions options, const QByteArray &codecForSource,
+ const QString &targetLanguage, const QString &sourceLanguage,
+ Translator *parentTor, bool *fail)
+{
+ foreach (const QString &proFile, proFiles) {
+ ProFileEvaluator visitor;
+ visitor.setVerbose(options & Verbose);
+
+ QHash<QString, QStringList> lupdateConfig;
+ lupdateConfig.insert(QLatin1String("CONFIG"), QStringList(QLatin1String("lupdate_run")));
+ visitor.addVariables(lupdateConfig);
+
+ QFileInfo pfi(proFile);
+ ProFile pro(pfi.absoluteFilePath());
+ if (!visitor.queryProFile(&pro) || !visitor.accept(&pro)) {
+ if (topLevel)
+ *fail = true;
+ continue;
+ }
+
+ if (visitor.contains(QLatin1String("TRANSLATIONS"))) {
+ if (parentTor) {
+ if (topLevel) {
+ printErr(LU::tr("lupdate warning: TS files from command line "
+ "will override TRANSLATIONS in %1.\n").arg(proFile));
+ goto noTrans;
+ } else if (nestComplain) {
+ printErr(LU::tr("lupdate warning: TS files from command line "
+ "prevent recursing into %1.\n").arg(proFile));
+ continue;
+ }
+ }
+ QStringList tsFiles;
+ QDir proDir(pfi.absolutePath());
+ foreach (const QString &tsFile, visitor.values(QLatin1String("TRANSLATIONS")))
+ tsFiles << QFileInfo(proDir, tsFile).filePath();
+ if (tsFiles.isEmpty()) {
+ // This might mean either a buggy PRO file or an intentional detach -
+ // we can't know without seeing the actual RHS of the assignment ...
+ // Just assume correctness and be silent.
+ continue;
+ }
+ Translator tor;
+ bool setCodec = false;
+ QStringList tmp = visitor.values(QLatin1String("CODEC"))
+ + visitor.values(QLatin1String("DEFAULTCODEC"))
+ + visitor.values(QLatin1String("CODECFORTR"));
+ if (!tmp.isEmpty()) {
+ tor.setCodecName(tmp.last().toLatin1());
+ setCodec = true;
+ }
+ processProject(false, pfi, visitor, options, codecForSource,
+ targetLanguage, sourceLanguage, &tor, fail);
+ updateTsFiles(tor, tsFiles, setCodec, sourceLanguage, targetLanguage, options, fail);
+ continue;
+ }
+ noTrans:
+ if (!parentTor) {
+ if (topLevel)
+ printErr(LU::tr("lupdate warning: no TS files specified. Only diagnostics "
+ "will be produced for '%1'.\n").arg(proFile));
+ Translator tor;
+ processProject(nestComplain, pfi, visitor, options, codecForSource,
+ targetLanguage, sourceLanguage, &tor, fail);
+ } else {
+ processProject(nestComplain, pfi, visitor, options, codecForSource,
+ targetLanguage, sourceLanguage, parentTor, fail);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+#ifndef Q_OS_WIN32
+ QTranslator translator;
+ QTranslator qtTranslator;
+ QString sysLocale = QLocale::system().name();
+ QString resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
+ if (translator.load(QLatin1String("linguist_") + sysLocale, resourceDir)
+ && qtTranslator.load(QLatin1String("qt_") + sysLocale, resourceDir)) {
+ app.installTranslator(&translator);
+ app.installTranslator(&qtTranslator);
+ }
+#endif // Q_OS_WIN32
+
+ m_defaultExtensions = QLatin1String("java,jui,ui,c,c++,cc,cpp,cxx,ch,h,h++,hh,hpp,hxx,js,qs,qml");
+
+ QStringList args = app.arguments();
+ QStringList tsFileNames;
+ QStringList proFiles;
+ QMultiHash<QString, QString> allCSources;
+ QSet<QString> projectRoots;
+ QStringList sourceFiles;
+ QStringList includePath;
+ QString targetLanguage;
+ QString sourceLanguage;
+ QByteArray codecForTr;
+
+ UpdateOptions options =
+ Verbose | // verbose is on by default starting with Qt 4.2
+ HeuristicSameText | HeuristicSimilarText | HeuristicNumber;
+ int numFiles = 0;
+ bool metTsFlag = false;
+ bool recursiveScan = true;
+
+ QString extensions = m_defaultExtensions;
+ QSet<QString> extensionsNameFilters;
+
+ for (int i = 1; i < argc; ++i) {
+ QString arg = args.at(i);
+ if (arg == QLatin1String("-help")
+ || arg == QLatin1String("--help")
+ || arg == QLatin1String("-h")) {
+ printUsage();
+ return 0;
+ } else if (arg == QLatin1String("-pluralonly")) {
+ options |= PluralOnly;
+ continue;
+ } else if (arg == QLatin1String("-noobsolete")
+ || arg == QLatin1String("-no-obsolete")) {
+ options |= NoObsolete;
+ continue;
+ } else if (arg == QLatin1String("-silent")) {
+ options &= ~Verbose;
+ continue;
+ } else if (arg == QLatin1String("-target-language")) {
+ ++i;
+ if (i == argc) {
+ printErr(LU::tr("The option -target-language requires a parameter.\n"));
+ return 1;
+ }
+ targetLanguage = args[i];
+ continue;
+ } else if (arg == QLatin1String("-source-language")) {
+ ++i;
+ if (i == argc) {
+ printErr(LU::tr("The option -source-language requires a parameter.\n"));
+ return 1;
+ }
+ sourceLanguage = args[i];
+ continue;
+ } else if (arg == QLatin1String("-disable-heuristic")) {
+ ++i;
+ if (i == argc) {
+ printErr(LU::tr("The option -disable-heuristic requires a parameter.\n"));
+ return 1;
+ }
+ arg = args[i];
+ if (arg == QLatin1String("sametext")) {
+ options &= ~HeuristicSameText;
+ } else if (arg == QLatin1String("similartext")) {
+ options &= ~HeuristicSimilarText;
+ } else if (arg == QLatin1String("number")) {
+ options &= ~HeuristicNumber;
+ } else {
+ printErr(LU::tr("Invalid heuristic name passed to -disable-heuristic.\n"));
+ return 1;
+ }
+ continue;
+ } else if (arg == QLatin1String("-locations")) {
+ ++i;
+ if (i == argc) {
+ printErr(LU::tr("The option -locations requires a parameter.\n"));
+ return 1;
+ }
+ if (args[i] == QLatin1String("none")) {
+ options |= NoLocations;
+ } else if (args[i] == QLatin1String("relative")) {
+ options |= RelativeLocations;
+ } else if (args[i] == QLatin1String("absolute")) {
+ options |= AbsoluteLocations;
+ } else {
+ printErr(LU::tr("Invalid parameter passed to -locations.\n"));
+ return 1;
+ }
+ continue;
+ } else if (arg == QLatin1String("-no-ui-lines")) {
+ options |= NoUiLines;
+ continue;
+ } else if (arg == QLatin1String("-verbose")) {
+ options |= Verbose;
+ continue;
+ } else if (arg == QLatin1String("-no-recursive")) {
+ recursiveScan = false;
+ continue;
+ } else if (arg == QLatin1String("-recursive")) {
+ recursiveScan = true;
+ continue;
+ } else if (arg == QLatin1String("-no-sort")
+ || arg == QLatin1String("-nosort")) {
+ options |= NoSort;
+ continue;
+ } else if (arg == QLatin1String("-version")) {
+ printOut(QObject::tr("lupdate version %1\n").arg(QLatin1String(QT_VERSION_STR)));
+ return 0;
+ } else if (arg == QLatin1String("-codecfortr")) {
+ ++i;
+ if (i == argc) {
+ printErr(LU::tr("The -codecfortr option should be followed by a codec name.\n"));
+ return 1;
+ }
+ codecForTr = args[i].toLatin1();
+ continue;
+ } else if (arg == QLatin1String("-ts")) {
+ metTsFlag = true;
+ continue;
+ } else if (arg == QLatin1String("-extensions")) {
+ ++i;
+ if (i == argc) {
+ printErr(LU::tr("The -extensions option should be followed by an extension list.\n"));
+ return 1;
+ }
+ extensions = args[i];
+ continue;
+ } else if (arg == QLatin1String("-pro")) {
+ ++i;
+ if (i == argc) {
+ printErr(LU::tr("The -pro option should be followed by a filename of .pro file.\n"));
+ return 1;
+ }
+ proFiles += args[i];
+ numFiles++;
+ continue;
+ } else if (arg.startsWith(QLatin1String("-I"))) {
+ if (arg.length() == 2) {
+ ++i;
+ if (i == argc) {
+ printErr(LU::tr("The -I option should be followed by a path.\n"));
+ return 1;
+ }
+ includePath += args[i];
+ } else {
+ includePath += args[i].mid(2);
+ }
+ continue;
+ } else if (arg.startsWith(QLatin1String("-")) && arg != QLatin1String("-")) {
+ printErr(LU::tr("Unrecognized option '%1'.\n").arg(arg));
+ return 1;
+ }
+
+ QStringList files;
+ if (arg.startsWith(QLatin1String("@"))) {
+ QFile lstFile(arg.mid(1));
+ if (!lstFile.open(QIODevice::ReadOnly)) {
+ printErr(LU::tr("lupdate error: List file '%1' is not readable.\n")
+ .arg(lstFile.fileName()));
+ return 1;
+ }
+ while (!lstFile.atEnd())
+ files << QString::fromLocal8Bit(lstFile.readLine().trimmed());
+ } else {
+ files << arg;
+ }
+ if (metTsFlag) {
+ foreach (const QString &file, files) {
+ bool found = false;
+ foreach (const Translator::FileFormat &fmt, Translator::registeredFileFormats()) {
+ if (file.endsWith(QLatin1Char('.') + fmt.extension, Qt::CaseInsensitive)) {
+ QFileInfo fi(file);
+ if (!fi.exists() || fi.isWritable()) {
+ tsFileNames.append(QFileInfo(file).absoluteFilePath());
+ } else {
+ printErr(LU::tr("lupdate warning: For some reason, '%1' is not writable.\n")
+ .arg(file));
+ }
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ printErr(LU::tr("lupdate error: File '%1' has no recognized extension.\n")
+ .arg(file));
+ return 1;
+ }
+ }
+ numFiles++;
+ } else {
+ foreach (const QString &file, files) {
+ QFileInfo fi(file);
+ if (!fi.exists()) {
+ printErr(LU::tr("lupdate error: File '%1' does not exist.\n").arg(file));
+ return 1;
+ }
+ if (file.endsWith(QLatin1String(".pro"), Qt::CaseInsensitive)
+ || file.endsWith(QLatin1String(".pri"), Qt::CaseInsensitive)) {
+ proFiles << file;
+ } else if (fi.isDir()) {
+ if (options & Verbose)
+ printOut(LU::tr("Scanning directory '%1'...\n").arg(file));
+ QDir dir = QDir(fi.filePath());
+ projectRoots.insert(dir.absolutePath() + QLatin1Char('/'));
+ if (extensionsNameFilters.isEmpty()) {
+ foreach (QString ext, extensions.split(QLatin1Char(','))) {
+ ext = ext.trimmed();
+ if (ext.startsWith(QLatin1Char('.')))
+ ext.remove(0, 1);
+ extensionsNameFilters.insert(ext);
+ }
+ }
+ QDir::Filters filters = QDir::Files | QDir::NoSymLinks;
+ if (recursiveScan)
+ filters |= QDir::AllDirs | QDir::NoDotAndDotDot;
+ QFileInfoList fileinfolist;
+ recursiveFileInfoList(dir, extensionsNameFilters, filters, &fileinfolist);
+ int scanRootLen = dir.absolutePath().length();
+ foreach (const QFileInfo &fi, fileinfolist) {
+ QString fn = QDir::cleanPath(fi.absoluteFilePath());
+ sourceFiles << fn;
+
+ if (!fn.endsWith(QLatin1String(".java"))
+ && !fn.endsWith(QLatin1String(".jui"))
+ && !fn.endsWith(QLatin1String(".ui"))
+ && !fn.endsWith(QLatin1String(".js"))
+ && !fn.endsWith(QLatin1String(".qs"))
+ && !fn.endsWith(QLatin1String(".qml"))) {
+ int offset = 0;
+ int depth = 0;
+ do {
+ offset = fn.lastIndexOf(QLatin1Char('/'), offset - 1);
+ QString ffn = fn.mid(offset + 1);
+ allCSources.insert(ffn, fn);
+ } while (++depth < 3 && offset > scanRootLen);
+ }
+ }
+ } else {
+ sourceFiles << QDir::cleanPath(fi.absoluteFilePath());;
+ projectRoots.insert(fi.absolutePath() + QLatin1Char('/'));
+ }
+ }
+ numFiles++;
+ }
+ } // for args
+
+ if (numFiles == 0) {
+ printUsage();
+ return 1;
+ }
+
+ if (!targetLanguage.isEmpty() && tsFileNames.count() != 1)
+ printErr(LU::tr("lupdate warning: -target-language usually only"
+ " makes sense with exactly one TS file.\n"));
+ if (!codecForTr.isEmpty() && tsFileNames.isEmpty())
+ printErr(LU::tr("lupdate warning: -codecfortr has no effect without -ts.\n"));
+
+ bool fail = false;
+ if (proFiles.isEmpty()) {
+ if (tsFileNames.isEmpty())
+ printErr(LU::tr("lupdate warning:"
+ " no TS files specified. Only diagnostics will be produced.\n"));
+
+ Translator fetchedTor;
+ ConversionData cd;
+ cd.m_noUiLines = options & NoUiLines;
+ cd.m_projectRoots = projectRoots;
+ cd.m_includePath = includePath;
+ cd.m_allCSources = allCSources;
+ fetchedTor.setCodecName(codecForTr);
+ processSources(fetchedTor, sourceFiles, cd);
+ updateTsFiles(fetchedTor, tsFileNames, !codecForTr.isEmpty(),
+ sourceLanguage, targetLanguage, options, &fail);
+ } else {
+ if (!sourceFiles.isEmpty() || !includePath.isEmpty()) {
+ printErr(LU::tr("lupdate error:"
+ " Both project and source files / include paths specified.\n"));
+ return 1;
+ }
+ if (!tsFileNames.isEmpty()) {
+ Translator fetchedTor;
+ fetchedTor.setCodecName(codecForTr);
+ processProjects(true, true, proFiles, options, QByteArray(),
+ targetLanguage, sourceLanguage, &fetchedTor, &fail);
+ updateTsFiles(fetchedTor, tsFileNames, !codecForTr.isEmpty(),
+ sourceLanguage, targetLanguage, options, &fail);
+ } else {
+ processProjects(true, false, proFiles, options, QByteArray(),
+ targetLanguage, sourceLanguage, 0, &fail);
+ }
+ }
+ return fail ? 1 : 0;
+}
diff --git a/src/linguist/lupdate/merge.cpp b/src/linguist/lupdate/merge.cpp
new file mode 100644
index 000000000..39de603eb
--- /dev/null
+++ b/src/linguist/lupdate/merge.cpp
@@ -0,0 +1,515 @@
+/****************************************************************************
+**
+** 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 "lupdate.h"
+
+#include "simtexth.h"
+#include "translator.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QMap>
+#include <QtCore/QStringList>
+#include <QtCore/QTextCodec>
+#include <QtCore/QVector>
+
+QT_BEGIN_NAMESPACE
+
+class LU {
+ Q_DECLARE_TR_FUNCTIONS(LUpdate)
+};
+
+static bool isDigitFriendly(QChar c)
+{
+ return c.isPunct() || c.isSpace();
+}
+
+static int numberLength(const QString &s, int i)
+{
+ if (i >= s.size() || !s.at(i).isDigit())
+ return 0;
+
+ int pos = i;
+ do {
+ ++i;
+ } while (i < s.size()
+ && (s.at(i).isDigit()
+ || (isDigitFriendly(s[i])
+ && i + 1 < s.size()
+ && (s[i + 1].isDigit()
+ || (isDigitFriendly(s[i + 1])
+ && i + 2 < s.size()
+ && s[i + 2].isDigit())))));
+ return i - pos;
+}
+
+
+/*
+ Returns a version of 'key' where all numbers have been replaced by zeroes. If
+ there were none, returns "".
+*/
+static QString zeroKey(const QString &key)
+{
+ QString zeroed;
+ bool metSomething = false;
+
+ for (int i = 0; i < key.size(); ++i) {
+ int len = numberLength(key, i);
+ if (len > 0) {
+ i += len;
+ zeroed.append(QLatin1Char('0'));
+ metSomething = true;
+ } else {
+ zeroed.append(key.at(i));
+ }
+ }
+ return metSomething ? zeroed : QString();
+}
+
+static QString translationAttempt(const QString &oldTranslation,
+ const QString &oldSource, const QString &newSource)
+{
+ int p = zeroKey(oldSource).count(QLatin1Char('0'));
+ QString attempt;
+ QStringList oldNumbers;
+ QStringList newNumbers;
+ QVector<bool> met(p);
+ QVector<int> matchedYet(p);
+ int i, j;
+ int k = 0, ell, best;
+ int m, n;
+ int pass;
+
+ /*
+ This algorithm is hard to follow, so we'll consider an example
+ all along: oldTranslation is "XeT 3.0", oldSource is "TeX 3.0"
+ and newSource is "XeT 3.1".
+
+ First, we set up two tables: oldNumbers and newNumbers. In our
+ example, oldNumber[0] is "3.0" and newNumber[0] is "3.1".
+ */
+ for (i = 0, j = 0; i < oldSource.size(); i++, j++) {
+ m = numberLength(oldSource, i);
+ n = numberLength(newSource, j);
+ if (m > 0) {
+ oldNumbers.append(oldSource.mid(i, m + 1));
+ newNumbers.append(newSource.mid(j, n + 1));
+ i += m;
+ j += n;
+ met[k] = false;
+ matchedYet[k] = 0;
+ k++;
+ }
+ }
+
+ /*
+ We now go over the old translation, "XeT 3.0", one letter at a
+ time, looking for numbers found in oldNumbers. Whenever such a
+ number is met, it is replaced with its newNumber equivalent. In
+ our example, the "3.0" of "XeT 3.0" becomes "3.1".
+ */
+ for (i = 0; i < oldTranslation.length(); i++) {
+ attempt += oldTranslation[i];
+ for (k = 0; k < p; k++) {
+ if (oldTranslation[i] == oldNumbers[k][matchedYet[k]])
+ matchedYet[k]++;
+ else
+ matchedYet[k] = 0;
+ }
+
+ /*
+ Let's find out if the last character ended a match. We make
+ two passes over the data. In the first pass, we try to
+ match only numbers that weren't matched yet; if that fails,
+ the second pass does the trick. This is useful in some
+ suspicious cases, flagged below.
+ */
+ for (pass = 0; pass < 2; pass++) {
+ best = p; // an impossible value
+ for (k = 0; k < p; k++) {
+ if ((!met[k] || pass > 0) &&
+ matchedYet[k] == oldNumbers[k].length() &&
+ numberLength(oldTranslation, i + 1 - matchedYet[k]) == matchedYet[k]) {
+ // the longer the better
+ if (best == p || matchedYet[k] > matchedYet[best])
+ best = k;
+ }
+ }
+ if (best != p) {
+ attempt.truncate(attempt.length() - matchedYet[best]);
+ attempt += newNumbers[best];
+ met[best] = true;
+ for (k = 0; k < p; k++)
+ matchedYet[k] = 0;
+ break;
+ }
+ }
+ }
+
+ /*
+ We flag two kinds of suspicious cases. They are identified as
+ such with comments such as "{2000?}" at the end.
+
+ Example of the first kind: old source text "TeX 3.0" translated
+ as "XeT 2.0" is flagged "TeX 2.0 {3.0?}", no matter what the
+ new text is.
+ */
+ for (k = 0; k < p; k++) {
+ if (!met[k])
+ attempt += QLatin1String(" {") + newNumbers[k] + QLatin1String("?}");
+ }
+
+ /*
+ Example of the second kind: "1 of 1" translated as "1 af 1",
+ with new source text "1 of 2", generates "1 af 2 {1 or 2?}"
+ because it's not clear which of "1 af 2" and "2 af 1" is right.
+ */
+ for (k = 0; k < p; k++) {
+ for (ell = 0; ell < p; ell++) {
+ if (k != ell && oldNumbers[k] == oldNumbers[ell] &&
+ newNumbers[k] < newNumbers[ell])
+ attempt += QLatin1String(" {") + newNumbers[k] + QLatin1String(" or ") +
+ newNumbers[ell] + QLatin1String("?}");
+ }
+ }
+ return attempt;
+}
+
+
+/*
+ Augments a Translator with translations easily derived from
+ similar existing (probably obsolete) translations.
+
+ For example, if "TeX 3.0" is translated as "XeT 3.0" and "TeX 3.1"
+ has no translation, "XeT 3.1" is added to the translator and is
+ marked Unfinished.
+
+ Returns the number of additional messages that this heuristic translated.
+*/
+int applyNumberHeuristic(Translator &tor)
+{
+ QMap<QString, QPair<QString, QString> > translated;
+ QVector<bool> untranslated(tor.messageCount());
+ int inserted = 0;
+
+ for (int i = 0; i < tor.messageCount(); ++i) {
+ const TranslatorMessage &msg = tor.message(i);
+ bool hasTranslation = msg.isTranslated();
+ if (msg.type() == TranslatorMessage::Unfinished) {
+ if (!hasTranslation)
+ untranslated[i] = true;
+ } else if (hasTranslation && msg.translations().count() == 1) {
+ const QString &key = zeroKey(msg.sourceText());
+ if (!key.isEmpty())
+ translated.insert(key, qMakePair(msg.sourceText(), msg.translation()));
+ }
+ }
+
+ for (int i = 0; i < tor.messageCount(); ++i) {
+ if (untranslated[i]) {
+ TranslatorMessage &msg = tor.message(i);
+ const QString &key = zeroKey(msg.sourceText());
+ if (!key.isEmpty()) {
+ QMap<QString, QPair<QString, QString> >::ConstIterator t =
+ translated.constFind(key);
+ if (t != translated.constEnd() && t->first != msg.sourceText()) {
+ msg.setTranslation(translationAttempt(t->second, t->first,
+ msg.sourceText()));
+ inserted++;
+ }
+ }
+ }
+ }
+ return inserted;
+}
+
+
+/*
+ Augments a Translator with trivially derived translations.
+
+ For example, if "Enabled:" is consistendly translated as "Eingeschaltet:" no
+ matter the context or the comment, "Eingeschaltet:" is added as the
+ translation of any untranslated "Enabled:" text and is marked Unfinished.
+
+ Returns the number of additional messages that this heuristic translated.
+*/
+
+int applySameTextHeuristic(Translator &tor)
+{
+ QMap<QString, QStringList> translated;
+ QMap<QString, bool> avoid; // Want a QTreeSet, in fact
+ QVector<bool> untranslated(tor.messageCount());
+ int inserted = 0;
+
+ for (int i = 0; i < tor.messageCount(); ++i) {
+ const TranslatorMessage &msg = tor.message(i);
+ if (!msg.isTranslated()) {
+ if (msg.type() == TranslatorMessage::Unfinished)
+ untranslated[i] = true;
+ } else {
+ const QString &key = msg.sourceText();
+ QMap<QString, QStringList>::ConstIterator t = translated.constFind(key);
+ if (t != translated.constEnd()) {
+ /*
+ The same source text is translated at least two
+ different ways. Do nothing then.
+ */
+ if (*t != msg.translations()) {
+ translated.remove(key);
+ avoid.insert(key, true);
+ }
+ } else if (!avoid.contains(key)) {
+ translated.insert(key, msg.translations());
+ }
+ }
+ }
+
+ for (int i = 0; i < tor.messageCount(); ++i) {
+ if (untranslated[i]) {
+ TranslatorMessage &msg = tor.message(i);
+ QMap<QString, QStringList>::ConstIterator t = translated.constFind(msg.sourceText());
+ if (t != translated.constEnd()) {
+ msg.setTranslations(*t);
+ ++inserted;
+ }
+ }
+ }
+ return inserted;
+}
+
+
+
+/*
+ Merges two Translator objects. The first one
+ is a set of source texts and translations for a previous version of
+ the internationalized program; the second one is a set of fresh
+ source texts newly extracted from the source code, without any
+ translation yet.
+*/
+
+Translator merge(const Translator &tor, const Translator &virginTor,
+ UpdateOptions options, QString &err)
+{
+ int known = 0;
+ int neww = 0;
+ int obsoleted = 0;
+ int similarTextHeuristicCount = 0;
+
+ Translator outTor;
+ outTor.setLanguageCode(tor.languageCode());
+ outTor.setSourceLanguageCode(tor.sourceLanguageCode());
+ outTor.setLocationsType(tor.locationsType());
+ outTor.setCodecName(tor.codecName());
+
+ /*
+ The types of all the messages from the vernacular translator
+ are updated according to the virgin translator.
+ */
+ foreach (TranslatorMessage m, tor.messages()) {
+ TranslatorMessage::Type newType = TranslatorMessage::Finished;
+
+ if (m.sourceText().isEmpty() && m.id().isEmpty()) {
+ // context/file comment
+ TranslatorMessage mv = virginTor.find(m.context());
+ if (!mv.isNull())
+ m.setComment(mv.comment());
+ } else {
+ TranslatorMessage mv;
+ int mvi = virginTor.find(m);
+ if (mvi < 0) {
+ if (!(options & HeuristicSimilarText)) {
+ makeObsolete:
+ newType = TranslatorMessage::Obsolete;
+ if (m.type() != TranslatorMessage::Obsolete)
+ obsoleted++;
+ m.clearReferences();
+ } else {
+ mv = virginTor.find(m.context(), m.comment(), m.allReferences());
+ if (mv.isNull()) {
+ // did not find it in the virgin, mark it as obsolete
+ goto makeObsolete;
+ } else {
+ // Do not just accept it if its on the same line number,
+ // but different source text.
+ // Also check if the texts are more or less similar before
+ // we consider them to represent the same message...
+ if (getSimilarityScore(m.sourceText(), mv.sourceText()) >= textSimilarityThreshold) {
+ // It is just slightly modified, assume that it is the same string
+
+ // Mark it as unfinished. (Since the source text
+ // was changed it might require re-translating...)
+ newType = TranslatorMessage::Unfinished;
+ ++similarTextHeuristicCount;
+ neww++;
+
+ outdateSource:
+ m.setOldSourceText(m.sourceText());
+ m.setSourceText(mv.sourceText());
+ const QString &oldpluralsource = m.extra(QLatin1String("po-msgid_plural"));
+ if (!oldpluralsource.isEmpty()) {
+ m.setExtra(QLatin1String("po-old_msgid_plural"), oldpluralsource);
+ m.unsetExtra(QLatin1String("po-msgid_plural"));
+ }
+ goto copyAttribs; // Update secondary references
+ } else {
+ // The virgin and vernacular sourceTexts are so
+ // different that we could not find it.
+ goto makeObsolete;
+ }
+ }
+ }
+ } else {
+ mv = virginTor.message(mvi);
+ if (!mv.id().isEmpty()
+ && (mv.context() != m.context()
+ || mv.sourceText() != m.sourceText()
+ || mv.comment() != m.comment())) {
+ known++;
+ newType = TranslatorMessage::Unfinished;
+ m.setContext(mv.context());
+ m.setComment(mv.comment());
+ if (mv.sourceText() != m.sourceText())
+ goto outdateSource;
+ } else {
+ switch (m.type()) {
+ case TranslatorMessage::Finished:
+ default:
+ if (m.isPlural() == mv.isPlural()) {
+ newType = TranslatorMessage::Finished;
+ } else {
+ newType = TranslatorMessage::Unfinished;
+ }
+ known++;
+ break;
+ case TranslatorMessage::Unfinished:
+ newType = TranslatorMessage::Unfinished;
+ known++;
+ break;
+ case TranslatorMessage::Obsolete:
+ newType = TranslatorMessage::Unfinished;
+ neww++;
+ }
+ }
+
+ // Always get the filename and linenumber info from the
+ // virgin Translator, in case it has changed location.
+ // This should also enable us to read a file that does not
+ // have the <location> element.
+ // why not use operator=()? Because it overwrites e.g. userData.
+ copyAttribs:
+ m.setReferences(mv.allReferences());
+ m.setPlural(mv.isPlural());
+ m.setUtf8(mv.isUtf8());
+ m.setExtraComment(mv.extraComment());
+ m.setId(mv.id());
+ }
+ }
+
+ m.setType(newType);
+ outTor.append(m);
+ }
+
+ /*
+ Messages found only in the virgin translator are added to the
+ vernacular translator.
+ */
+ foreach (const TranslatorMessage &mv, virginTor.messages()) {
+ if (mv.sourceText().isEmpty() && mv.id().isEmpty()) {
+ if (tor.contains(mv.context()))
+ continue;
+ } else {
+ if (tor.find(mv) >= 0)
+ continue;
+ if (options & HeuristicSimilarText) {
+ TranslatorMessage m = tor.find(mv.context(), mv.comment(), mv.allReferences());
+ if (!m.isNull()) {
+ if (getSimilarityScore(m.sourceText(), mv.sourceText()) >= textSimilarityThreshold)
+ continue;
+ }
+ }
+ }
+ if (options & NoLocations)
+ outTor.append(mv);
+ else
+ outTor.appendSorted(mv);
+ if (!mv.sourceText().isEmpty() || !mv.id().isEmpty())
+ ++neww;
+ }
+
+ /*
+ The same-text heuristic handles cases where a message has an
+ obsolete counterpart with a different context or comment.
+ */
+ int sameTextHeuristicCount = (options & HeuristicSameText) ? applySameTextHeuristic(outTor) : 0;
+
+ /*
+ The number heuristic handles cases where a message has an
+ obsolete counterpart with mostly numbers differing in the
+ source text.
+ */
+ int sameNumberHeuristicCount = (options & HeuristicNumber) ? applyNumberHeuristic(outTor) : 0;
+
+ if (options & Verbose) {
+ int totalFound = neww + known;
+ err += LU::tr(" Found %n source text(s) (%1 new and %2 already existing)\n", 0, totalFound).arg(neww).arg(known);
+
+ if (obsoleted) {
+ if (options & NoObsolete) {
+ err += LU::tr(" Removed %n obsolete entries\n", 0, obsoleted);
+ } else {
+ err += LU::tr(" Kept %n obsolete entries\n", 0, obsoleted);
+ }
+ }
+
+ if (sameNumberHeuristicCount)
+ err += LU::tr(" Number heuristic provided %n translation(s)\n",
+ 0, sameNumberHeuristicCount);
+ if (sameTextHeuristicCount)
+ err += LU::tr(" Same-text heuristic provided %n translation(s)\n",
+ 0, sameTextHeuristicCount);
+ if (similarTextHeuristicCount)
+ err += LU::tr(" Similar-text heuristic provided %n translation(s)\n",
+ 0, similarTextHeuristicCount);
+ }
+ return outTor;
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/lupdate/qdeclarative.cpp b/src/linguist/lupdate/qdeclarative.cpp
new file mode 100644
index 000000000..6bf9cf469
--- /dev/null
+++ b/src/linguist/lupdate/qdeclarative.cpp
@@ -0,0 +1,433 @@
+/****************************************************************************
+**
+** 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 "lupdate.h"
+
+#include <translator.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QFile>
+#include <QtCore/QString>
+
+#include "parser/qdeclarativejsengine_p.h"
+#include "parser/qdeclarativejsparser_p.h"
+#include "parser/qdeclarativejslexer_p.h"
+#include "parser/qdeclarativejsnodepool_p.h"
+#include "parser/qdeclarativejsastvisitor_p.h"
+#include "parser/qdeclarativejsast_p.h"
+
+#include <QCoreApplication>
+#include <QFile>
+#include <QFileInfo>
+#include <QtDebug>
+#include <QStringList>
+
+#include <iostream>
+#include <cstdlib>
+#include <cctype>
+
+QT_BEGIN_NAMESPACE
+
+class LU {
+ Q_DECLARE_TR_FUNCTIONS(LUpdate)
+};
+
+using namespace QDeclarativeJS;
+
+class Comment
+{
+public:
+ Comment() : lastLine(-1) {}
+ QString extracomment;
+ QString msgid;
+ TranslatorMessage::ExtraData extra;
+ QString sourcetext;
+ int lastLine;
+
+ bool isValid() const
+ { return !extracomment.isEmpty() || !msgid.isEmpty() || !sourcetext.isEmpty() || !extra.isEmpty(); }
+};
+
+class FindTrCalls: protected AST::Visitor
+{
+public:
+ void operator()(Translator *translator, const QString &fileName, AST::Node *node)
+ {
+ m_translator = translator;
+ m_fileName = fileName;
+ m_component = QFileInfo(fileName).baseName(); //matches qsTr usage in QScriptEngine
+ accept(node);
+ }
+
+ QList<Comment> comments;
+
+protected:
+ using AST::Visitor::visit;
+ using AST::Visitor::endVisit;
+
+ void accept(AST::Node *node)
+ { AST::Node::acceptChild(node, this); }
+
+ virtual void endVisit(AST::CallExpression *node)
+ {
+ m_bSource.clear();
+ if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(node->base)) {
+ if (idExpr->name->asString() == QLatin1String("qsTr") ||
+ idExpr->name->asString() == QLatin1String("QT_TR_NOOP")) {
+ if (!node->arguments)
+ return;
+ AST::BinaryExpression *binary = AST::cast<AST::BinaryExpression *>(node->arguments->expression);
+ if (binary) {
+ if (!createString(binary))
+ m_bSource.clear();
+ }
+ AST::StringLiteral *literal = AST::cast<AST::StringLiteral *>(node->arguments->expression);
+ if (literal || !m_bSource.isEmpty()) {
+ const QString source = literal ? literal->value->asString() : m_bSource;
+
+ QString comment;
+ bool plural = false;
+ AST::ArgumentList *commentNode = node->arguments->next;
+ if (commentNode && AST::cast<AST::StringLiteral *>(commentNode->expression)) {
+ literal = AST::cast<AST::StringLiteral *>(commentNode->expression);
+ comment = literal->value->asString();
+
+ AST::ArgumentList *nNode = commentNode->next;
+ if (nNode)
+ plural = true;
+ }
+
+ QString id;
+ QString extracomment;
+ TranslatorMessage::ExtraData extra;
+ Comment scomment = findComment(node->firstSourceLocation().startLine);
+ if (scomment.isValid()) {
+ extracomment = scomment.extracomment;
+ extra = scomment.extra;
+ id = scomment.msgid;
+ }
+
+ TranslatorMessage msg(m_component, source,
+ comment, QString(), m_fileName,
+ node->firstSourceLocation().startLine, QStringList(),
+ TranslatorMessage::Unfinished, plural);
+ msg.setExtraComment(extracomment.simplified());
+ msg.setId(id);
+ msg.setExtras(extra);
+ m_translator->extend(msg);
+ }
+ } else if (idExpr->name->asString() == QLatin1String("qsTranslate") ||
+ idExpr->name->asString() == QLatin1String("QT_TRANSLATE_NOOP")) {
+ if (node->arguments && AST::cast<AST::StringLiteral *>(node->arguments->expression)) {
+ AST::StringLiteral *literal = AST::cast<AST::StringLiteral *>(node->arguments->expression);
+ const QString context = literal->value->asString();
+
+ QString source;
+ QString comment;
+ bool plural = false;
+ AST::ArgumentList *sourceNode = node->arguments->next;
+ if (!sourceNode)
+ return;
+ literal = AST::cast<AST::StringLiteral *>(sourceNode->expression);
+ AST::BinaryExpression *binary = AST::cast<AST::BinaryExpression *>(sourceNode->expression);
+ if (binary) {
+ if (!createString(binary))
+ m_bSource.clear();
+ }
+ if (!literal && m_bSource.isEmpty())
+ return;
+
+ QString id;
+ QString extracomment;
+ TranslatorMessage::ExtraData extra;
+ Comment scomment = findComment(node->firstSourceLocation().startLine);
+ if (scomment.isValid()) {
+ extracomment = scomment.extracomment;
+ extra = scomment.extra;
+ id = scomment.msgid;
+ }
+
+ source = literal ? literal->value->asString() : m_bSource;
+ AST::ArgumentList *commentNode = sourceNode->next;
+ if (commentNode && AST::cast<AST::StringLiteral *>(commentNode->expression)) {
+ literal = AST::cast<AST::StringLiteral *>(commentNode->expression);
+ comment = literal->value->asString();
+
+ AST::ArgumentList *nNode = commentNode->next;
+ if (nNode)
+ plural = true;
+ }
+
+ TranslatorMessage msg(context, source,
+ comment, QString(), m_fileName,
+ node->firstSourceLocation().startLine, QStringList(),
+ TranslatorMessage::Unfinished, plural);
+ msg.setExtraComment(extracomment.simplified());
+ msg.setId(id);
+ msg.setExtras(extra);
+ m_translator->extend(msg);
+ }
+ } else if (idExpr->name->asString() == QLatin1String("qsTrId") ||
+ idExpr->name->asString() == QLatin1String("QT_TRID_NOOP")) {
+ if (!node->arguments)
+ return;
+
+ AST::StringLiteral *literal = AST::cast<AST::StringLiteral *>(node->arguments->expression);
+ if (literal) {
+
+ QString extracomment;
+ QString sourcetext;
+ TranslatorMessage::ExtraData extra;
+ Comment comment = findComment(node->firstSourceLocation().startLine);
+ if (comment.isValid()) {
+ extracomment = comment.extracomment;
+ sourcetext = comment.sourcetext;
+ extra = comment.extra;
+ }
+
+ const QString id = literal->value->asString();
+ bool plural = node->arguments->next;
+
+ TranslatorMessage msg(QString(), sourcetext,
+ QString(), QString(), m_fileName,
+ node->firstSourceLocation().startLine, QStringList(),
+ TranslatorMessage::Unfinished, plural);
+ msg.setExtraComment(extracomment.simplified());
+ msg.setId(id);
+ msg.setExtras(extra);
+ m_translator->extend(msg);
+ }
+ }
+ }
+ }
+
+private:
+ bool createString(AST::BinaryExpression *b)
+ {
+ if (!b || b->op != 0)
+ return false;
+ AST::BinaryExpression *l = AST::cast<AST::BinaryExpression *>(b->left);
+ AST::BinaryExpression *r = AST::cast<AST::BinaryExpression *>(b->right);
+ AST::StringLiteral *ls = AST::cast<AST::StringLiteral *>(b->left);
+ AST::StringLiteral *rs = AST::cast<AST::StringLiteral *>(b->right);
+ if ((!l && !ls) || (!r && !rs))
+ return false;
+ if (l) {
+ if (!createString(l))
+ return false;
+ } else
+ m_bSource.prepend(ls->value->asString());
+
+ if (r) {
+ if (!createString(r))
+ return false;
+ } else
+ m_bSource.append(rs->value->asString());
+
+ return true;
+ }
+
+ Comment findComment(int loc)
+ {
+ if (comments.isEmpty())
+ return Comment();
+
+ int i = 0;
+ int commentLoc = comments.at(i).lastLine;
+ while (commentLoc <= loc) {
+ if (commentLoc == loc)
+ return comments.at(i);
+ if (i == comments.count()-1)
+ break;
+ commentLoc = comments.at(++i).lastLine;
+ }
+ return Comment();
+ }
+
+ Translator *m_translator;
+ QString m_fileName;
+ QString m_component;
+ QString m_bSource;
+};
+
+QString createErrorString(const QString &filename, const QString &code, Parser &parser)
+{
+ // print out error
+ QStringList lines = code.split(QLatin1Char('\n'));
+ lines.append(QLatin1String("\n")); // sentinel.
+ QString errorString;
+
+ foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) {
+
+ if (m.isWarning())
+ continue;
+
+ QString error = filename + QLatin1Char(':') + QString::number(m.loc.startLine)
+ + QLatin1Char(':') + QString::number(m.loc.startColumn) + QLatin1String(": error: ")
+ + m.message + QLatin1Char('\n');
+
+ int line = 0;
+ if (m.loc.startLine > 0)
+ line = m.loc.startLine - 1;
+
+ const QString textLine = lines.at(line);
+
+ error += textLine + QLatin1Char('\n');
+
+ int column = m.loc.startColumn - 1;
+ if (column < 0)
+ column = 0;
+
+ column = qMin(column, textLine.length());
+
+ for (int i = 0; i < column; ++i) {
+ const QChar ch = textLine.at(i);
+ if (ch.isSpace())
+ error += ch.unicode();
+ else
+ error += QLatin1Char(' ');
+ }
+ error += QLatin1String("^\n");
+ errorString += error;
+ }
+ return errorString;
+}
+
+bool processComment(const QChar *chars, int length, Comment &comment)
+{
+ // Try to match the logic of the QtScript parser.
+ if (!length)
+ return comment.isValid();
+ if (*chars == QLatin1Char(':') && chars[1].isSpace()) {
+ comment.extracomment += QString(chars+1, length-1);
+ } else if (*chars == QLatin1Char('=') && chars[1].isSpace()) {
+ comment.msgid = QString(chars+2, length-2).simplified();
+ } else if (*chars == QLatin1Char('~') && chars[1].isSpace()) {
+ QString text = QString(chars+2, length-2).trimmed();
+ int k = text.indexOf(QLatin1Char(' '));
+ if (k > -1)
+ comment.extra.insert(text.left(k), text.mid(k + 1).trimmed());
+ } else if (*chars == QLatin1Char('%') && chars[1].isSpace()) {
+ comment.sourcetext.reserve(comment.sourcetext.length() + length-2);
+ ushort *ptr = (ushort *)comment.sourcetext.data() + comment.sourcetext.length();
+ int p = 2, c;
+ forever {
+ if (p >= length)
+ break;
+ c = chars[p++].unicode();
+ if (isspace(c))
+ continue;
+ if (c != '"')
+ break;
+ forever {
+ if (p >= length)
+ break;
+ c = chars[p++].unicode();
+ if (c == '"')
+ break;
+ if (c == '\\') {
+ if (p >= length)
+ break;
+ c = chars[p++].unicode();
+ if (c == '\n')
+ break;
+ *ptr++ = '\\';
+ }
+ *ptr++ = c;
+ }
+ }
+ comment.sourcetext.resize(ptr - (ushort *)comment.sourcetext.data());
+ }
+ return comment.isValid();
+}
+
+bool loadQml(Translator &translator, const QString &filename, ConversionData &cd)
+{
+ cd.m_sourceFileName = filename;
+ QFile file(filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ cd.appendError(LU::tr("Cannot open %1: %2").arg(filename, file.errorString()));
+ return false;
+ }
+
+ const QString code = QTextStream(&file).readAll();
+
+ Engine driver;
+ Parser parser(&driver);
+
+ NodePool nodePool(filename, &driver);
+ driver.setNodePool(&nodePool);
+
+ Lexer lexer(&driver);
+ lexer.setCode(code, /*line = */ 1);
+ driver.setLexer(&lexer);
+
+ if (parser.parse()) {
+ FindTrCalls trCalls;
+
+ // build up a list of comments that contain translation information.
+ for (int i = 0; i < driver.comments().size(); ++i) {
+ AST::SourceLocation loc = driver.comments().at(i);
+ QString commentStr = code.mid(loc.offset, loc.length);
+
+ if (trCalls.comments.isEmpty() || trCalls.comments.last().lastLine != int(loc.startLine)) {
+ Comment comment;
+ comment.lastLine = loc.startLine+1;
+ if (processComment(commentStr.constData(), commentStr.length(), comment))
+ trCalls.comments.append(comment);
+ } else {
+ Comment &lastComment = trCalls.comments.last();
+ lastComment.lastLine += 1;
+ processComment(commentStr.constData(), commentStr.length(), lastComment);
+ }
+ }
+
+ //find all tr calls in the code
+ trCalls(&translator, filename, parser.ast());
+ } else {
+ QString error = createErrorString(filename, code, parser);
+ cd.appendError(error);
+ return false;
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/lupdate/qscript.cpp b/src/linguist/lupdate/qscript.cpp
new file mode 100644
index 000000000..e53c9dc95
--- /dev/null
+++ b/src/linguist/lupdate/qscript.cpp
@@ -0,0 +1,2612 @@
+// This file was generated by qlalr - DO NOT EDIT!
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+
+#define Q_SCRIPT_REGEXPLITERAL_RULE1 7
+
+#define Q_SCRIPT_REGEXPLITERAL_RULE2 8
+
+#include <translator.h>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/qdebug.h>
+#include <QtCore/qnumeric.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qtextcodec.h>
+#include <QtCore/qvariant.h>
+
+#include <iostream>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+class LU {
+ Q_DECLARE_TR_FUNCTIONS(LUpdate)
+};
+
+static QString MagicComment(QLatin1String("TRANSLATOR"));
+
+class QScriptGrammar
+{
+public:
+ enum {
+ EOF_SYMBOL = 0,
+ T_AND = 1,
+ T_AND_AND = 2,
+ T_AND_EQ = 3,
+ T_AUTOMATIC_SEMICOLON = 62,
+ T_BREAK = 4,
+ T_CASE = 5,
+ T_CATCH = 6,
+ T_COLON = 7,
+ T_COMMA = 8,
+ T_CONST = 81,
+ T_CONTINUE = 9,
+ T_DEBUGGER = 82,
+ T_DEFAULT = 10,
+ T_DELETE = 11,
+ T_DIVIDE_ = 12,
+ T_DIVIDE_EQ = 13,
+ T_DO = 14,
+ T_DOT = 15,
+ T_ELSE = 16,
+ T_EQ = 17,
+ T_EQ_EQ = 18,
+ T_EQ_EQ_EQ = 19,
+ T_FALSE = 80,
+ T_FINALLY = 20,
+ T_FOR = 21,
+ T_FUNCTION = 22,
+ T_GE = 23,
+ T_GT = 24,
+ T_GT_GT = 25,
+ T_GT_GT_EQ = 26,
+ T_GT_GT_GT = 27,
+ T_GT_GT_GT_EQ = 28,
+ T_IDENTIFIER = 29,
+ T_IF = 30,
+ T_IN = 31,
+ T_INSTANCEOF = 32,
+ T_LBRACE = 33,
+ T_LBRACKET = 34,
+ T_LE = 35,
+ T_LPAREN = 36,
+ T_LT = 37,
+ T_LT_LT = 38,
+ T_LT_LT_EQ = 39,
+ T_MINUS = 40,
+ T_MINUS_EQ = 41,
+ T_MINUS_MINUS = 42,
+ T_NEW = 43,
+ T_NOT = 44,
+ T_NOT_EQ = 45,
+ T_NOT_EQ_EQ = 46,
+ T_NULL = 78,
+ T_NUMERIC_LITERAL = 47,
+ T_OR = 48,
+ T_OR_EQ = 49,
+ T_OR_OR = 50,
+ T_PLUS = 51,
+ T_PLUS_EQ = 52,
+ T_PLUS_PLUS = 53,
+ T_QUESTION = 54,
+ T_RBRACE = 55,
+ T_RBRACKET = 56,
+ T_REMAINDER = 57,
+ T_REMAINDER_EQ = 58,
+ T_RESERVED_WORD = 83,
+ T_RETURN = 59,
+ T_RPAREN = 60,
+ T_SEMICOLON = 61,
+ T_STAR = 63,
+ T_STAR_EQ = 64,
+ T_STRING_LITERAL = 65,
+ T_SWITCH = 66,
+ T_THIS = 67,
+ T_THROW = 68,
+ T_TILDE = 69,
+ T_TRUE = 79,
+ T_TRY = 70,
+ T_TYPEOF = 71,
+ T_VAR = 72,
+ T_VOID = 73,
+ T_WHILE = 74,
+ T_WITH = 75,
+ T_XOR = 76,
+ T_XOR_EQ = 77,
+
+ ACCEPT_STATE = 236,
+ RULE_COUNT = 267,
+ STATE_COUNT = 465,
+ TERMINAL_COUNT = 84,
+ NON_TERMINAL_COUNT = 88,
+
+ GOTO_INDEX_OFFSET = 465,
+ GOTO_INFO_OFFSET = 1374,
+ GOTO_CHECK_OFFSET = 1374
+ };
+
+ static const char *const spell [];
+ static const int lhs [];
+ static const int rhs [];
+ static const int goto_default [];
+ static const int action_default [];
+ static const int action_index [];
+ static const int action_info [];
+ static const int action_check [];
+
+ static inline int nt_action (int state, int nt)
+ {
+ const int *const goto_index = &action_index [GOTO_INDEX_OFFSET];
+ const int *const goto_check = &action_check [GOTO_CHECK_OFFSET];
+
+ const int yyn = goto_index [state] + nt;
+
+ if (yyn < 0 || goto_check [yyn] != nt)
+ return goto_default [nt];
+
+ const int *const goto_info = &action_info [GOTO_INFO_OFFSET];
+ return goto_info [yyn];
+ }
+
+ static inline int t_action (int state, int token)
+ {
+ const int yyn = action_index [state] + token;
+
+ if (yyn < 0 || action_check [yyn] != token)
+ return - action_default [state];
+
+ return action_info [yyn];
+ }
+};
+
+const char *const QScriptGrammar::spell [] = {
+ "end of file", "&", "&&", "&=", "break", "case", "catch", ":", ";", "continue",
+ "default", "delete", "/", "/=", "do", ".", "else", "=", "==", "===",
+ "finally", "for", "function", ">=", ">", ">>", ">>=", ">>>", ">>>=", "identifier",
+ "if", "in", "instanceof", "{", "[", "<=", "(", "<", "<<", "<<=",
+ "-", "-=", "--", "new", "!", "!=", "!==", "numeric literal", "|", "|=",
+ "||", "+", "+=", "++", "?", "}", "]", "%", "%=", "return",
+ ")", ";", 0, "*", "*=", "string literal", "switch", "this", "throw", "~",
+ "try", "typeof", "var", "void", "while", "with", "^", "^=", "null", "true",
+ "false", "const", "debugger", "reserved word"};
+
+const int QScriptGrammar::lhs [] = {
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 87, 87, 91, 91, 86, 86,
+ 92, 92, 93, 93, 93, 93, 94, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94, 94, 95, 95, 96,
+ 96, 96, 96, 96, 99, 99, 100, 100, 100, 100,
+ 98, 98, 101, 101, 102, 102, 103, 103, 103, 104,
+ 104, 104, 104, 104, 104, 104, 104, 104, 104, 105,
+ 105, 105, 105, 106, 106, 106, 107, 107, 107, 107,
+ 108, 108, 108, 108, 108, 108, 108, 109, 109, 109,
+ 109, 109, 109, 110, 110, 110, 110, 110, 111, 111,
+ 111, 111, 111, 112, 112, 113, 113, 114, 114, 115,
+ 115, 116, 116, 117, 117, 118, 118, 119, 119, 120,
+ 120, 121, 121, 122, 122, 123, 123, 90, 90, 124,
+ 124, 125, 125, 125, 125, 125, 125, 125, 125, 125,
+ 125, 125, 125, 89, 89, 126, 126, 127, 127, 128,
+ 128, 129, 129, 129, 129, 129, 129, 129, 129, 129,
+ 129, 129, 129, 129, 129, 129, 130, 146, 146, 145,
+ 145, 131, 131, 147, 147, 148, 148, 150, 150, 149,
+ 151, 154, 152, 152, 155, 153, 153, 132, 133, 133,
+ 134, 134, 135, 135, 135, 135, 135, 135, 135, 136,
+ 136, 136, 136, 137, 137, 137, 137, 138, 138, 139,
+ 141, 156, 156, 159, 159, 157, 157, 160, 158, 140,
+ 142, 142, 143, 143, 143, 161, 162, 144, 163, 97,
+ 167, 167, 164, 164, 165, 165, 168, 84, 169, 169,
+ 170, 170, 166, 166, 88, 88, 171};
+
+const int QScriptGrammar:: rhs[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 3,
+ 3, 5, 3, 3, 2, 4, 1, 2, 0, 1,
+ 3, 5, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 4, 3, 3, 1, 2, 2, 2, 4, 3,
+ 2, 3, 1, 3, 1, 1, 1, 2, 2, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
+ 3, 3, 3, 1, 3, 3, 1, 3, 3, 3,
+ 1, 3, 3, 3, 3, 3, 3, 1, 3, 3,
+ 3, 3, 3, 1, 3, 3, 3, 3, 1, 3,
+ 3, 3, 3, 1, 3, 1, 3, 1, 3, 1,
+ 3, 1, 3, 1, 3, 1, 3, 1, 3, 1,
+ 3, 1, 3, 1, 5, 1, 5, 1, 3, 1,
+ 3, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 3, 0, 1, 1, 3, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 3, 1, 2, 0,
+ 1, 3, 3, 1, 1, 1, 3, 1, 3, 2,
+ 2, 2, 0, 1, 2, 0, 1, 1, 2, 2,
+ 7, 5, 7, 7, 5, 9, 10, 7, 8, 2,
+ 2, 3, 3, 2, 2, 3, 3, 3, 3, 5,
+ 5, 3, 5, 1, 2, 0, 1, 4, 3, 3,
+ 3, 3, 3, 3, 4, 5, 2, 1, 8, 8,
+ 1, 3, 0, 1, 0, 1, 1, 1, 1, 2,
+ 1, 1, 0, 1, 0, 1, 2};
+
+const int QScriptGrammar::action_default [] = {
+ 0, 97, 164, 128, 136, 132, 172, 179, 76, 148,
+ 178, 186, 174, 124, 0, 175, 262, 61, 176, 177,
+ 182, 77, 140, 144, 65, 94, 75, 80, 60, 0,
+ 114, 180, 101, 259, 258, 261, 183, 0, 194, 0,
+ 248, 0, 8, 9, 0, 5, 0, 263, 2, 0,
+ 265, 19, 0, 0, 0, 0, 0, 3, 6, 0,
+ 0, 166, 208, 7, 0, 1, 0, 0, 4, 0,
+ 0, 195, 0, 0, 0, 184, 185, 90, 0, 173,
+ 181, 0, 0, 77, 96, 263, 2, 265, 79, 78,
+ 0, 0, 0, 92, 93, 91, 0, 264, 253, 254,
+ 0, 251, 0, 252, 0, 255, 256, 0, 257, 250,
+ 260, 0, 266, 0, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 23,
+ 41, 42, 43, 44, 45, 25, 46, 47, 24, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 0,
+ 21, 0, 0, 0, 22, 13, 95, 0, 125, 0,
+ 0, 0, 0, 115, 0, 0, 0, 0, 0, 0,
+ 105, 0, 0, 0, 99, 100, 98, 103, 107, 106,
+ 104, 102, 117, 116, 118, 0, 133, 0, 129, 68,
+ 0, 0, 0, 70, 59, 58, 0, 0, 69, 165,
+ 0, 73, 71, 0, 72, 74, 209, 210, 0, 161,
+ 154, 152, 159, 160, 158, 157, 163, 156, 155, 153,
+ 162, 149, 0, 137, 0, 0, 141, 0, 0, 145,
+ 67, 0, 0, 63, 0, 62, 267, 224, 0, 225,
+ 226, 227, 220, 0, 221, 222, 223, 81, 0, 0,
+ 0, 0, 0, 213, 214, 170, 168, 130, 138, 134,
+ 150, 126, 171, 0, 77, 142, 146, 119, 108, 0,
+ 0, 127, 0, 0, 0, 0, 120, 0, 0, 0,
+ 0, 0, 112, 110, 113, 111, 109, 122, 121, 123,
+ 0, 135, 0, 131, 0, 169, 77, 0, 151, 166,
+ 167, 0, 166, 0, 0, 216, 0, 0, 0, 218,
+ 0, 139, 0, 0, 143, 0, 0, 147, 206, 0,
+ 198, 207, 201, 0, 205, 0, 166, 199, 0, 166,
+ 0, 0, 217, 0, 0, 0, 219, 264, 253, 0,
+ 0, 255, 0, 249, 0, 240, 0, 0, 0, 212,
+ 0, 211, 188, 191, 0, 27, 30, 31, 248, 34,
+ 35, 5, 39, 40, 2, 41, 44, 3, 6, 166,
+ 7, 48, 1, 50, 4, 52, 53, 54, 55, 56,
+ 57, 189, 187, 65, 66, 64, 0, 228, 229, 0,
+ 0, 0, 231, 236, 234, 237, 0, 0, 235, 236,
+ 0, 232, 0, 233, 190, 239, 0, 190, 238, 0,
+ 241, 242, 0, 190, 243, 244, 0, 0, 245, 0,
+ 0, 0, 246, 247, 83, 82, 0, 0, 0, 215,
+ 0, 0, 0, 230, 0, 20, 0, 17, 19, 11,
+ 0, 16, 12, 18, 15, 10, 0, 14, 87, 85,
+ 89, 86, 84, 88, 203, 196, 0, 204, 200, 0,
+ 202, 192, 0, 193, 197};
+
+const int QScriptGrammar::goto_default [] = {
+ 29, 28, 436, 434, 113, 14, 2, 435, 112, 111,
+ 114, 193, 24, 17, 189, 26, 8, 200, 21, 27,
+ 77, 25, 1, 32, 30, 267, 13, 261, 3, 257,
+ 5, 259, 4, 258, 22, 265, 23, 266, 9, 260,
+ 256, 297, 386, 262, 263, 35, 6, 79, 12, 15,
+ 18, 19, 10, 7, 31, 80, 20, 36, 75, 76,
+ 11, 354, 353, 78, 456, 455, 319, 320, 458, 322,
+ 457, 321, 392, 396, 399, 395, 394, 414, 415, 16,
+ 100, 107, 96, 99, 106, 108, 33, 0};
+
+const int QScriptGrammar::action_index [] = {
+ 1210, 59, -84, 71, 41, -1, -84, -84, 148, -84,
+ -84, -84, -84, 201, 130, -84, -84, -84, -84, -84,
+ -84, 343, 67, 62, 122, 109, -84, -84, -84, 85,
+ 273, -84, 184, -84, 1210, -84, -84, 119, -84, 112,
+ -84, 521, -84, -84, 1130, -84, 45, 54, 58, 38,
+ 1290, 50, 521, 521, 521, 376, 521, -84, -84, 521,
+ 521, 521, -84, -84, 25, -84, 521, 521, -84, 43,
+ 521, -84, 521, 18, 15, -84, -84, -84, 24, -84,
+ -84, 521, 521, 64, 153, 27, -84, 1050, -84, -84,
+ 521, 521, 521, -84, -84, -84, 28, -84, 37, 55,
+ 19, -84, 33, -84, 34, 1210, -84, 16, 1210, -84,
+ -84, 39, 52, -3, -84, -84, -84, -84, -84, -84,
+ -84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
+ -84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
+ -84, -84, -84, -84, -84, -84, -84, -84, -84, 521,
+ -84, 1050, 125, 521, -84, -84, 155, 521, 189, 521,
+ 521, 521, 521, 248, 521, 521, 521, 521, 521, 521,
+ 243, 521, 521, 521, 75, 82, 94, 177, 184, 184,
+ 184, 184, 263, 283, 298, 521, 44, 521, 77, -84,
+ 970, 521, 817, -84, -84, -84, 95, 521, -84, -84,
+ 93, -84, -84, 521, -84, -84, -84, -84, 521, -84,
+ -84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
+ -84, -84, 521, 41, 521, 521, 68, 66, 521, -84,
+ -84, 970, 521, -84, 103, -84, -84, -84, 63, -84,
+ -84, -84, -84, 69, -84, -84, -84, -84, -27, 12,
+ 521, 92, 100, -84, -84, 890, -84, 31, -13, -45,
+ -84, 210, 32, -28, 387, 20, 73, 304, 117, -5,
+ 521, 212, 521, 521, 521, 521, 213, 521, 521, 521,
+ 521, 521, 151, 150, 176, 158, 168, 304, 304, 228,
+ 521, -72, 521, 4, 521, -84, 306, 521, -84, 521,
+ 8, -50, 521, -48, 1130, -84, 521, 80, 1130, -84,
+ 521, -33, 521, 521, 5, 48, 521, -84, 17, 88,
+ 11, -84, -84, 521, -84, -29, 521, -84, -41, 521,
+ -39, 1130, -84, 521, 87, 1130, -84, -8, -2, -35,
+ 10, 1210, -16, -84, 1130, -84, 521, 86, 1130, -14,
+ 1130, -84, -84, 1130, -36, 107, -21, 165, 3, 521,
+ 1130, 6, 14, 61, 7, -19, 448, -4, -6, 671,
+ 29, 13, 23, 521, 30, -10, 521, 9, 521, -30,
+ -18, -84, -84, 164, -84, -84, 46, -84, -84, 521,
+ 111, -24, -84, 36, -84, 40, 99, 521, -84, 21,
+ 22, -84, -11, -84, 1130, -84, 106, 1130, -84, 178,
+ -84, -84, 98, 1130, 57, -84, 56, 60, -84, 51,
+ 26, 35, -84, -84, -84, -84, 521, 97, 1130, -84,
+ 521, 90, 1130, -84, 79, 76, 744, -84, 49, -84,
+ 594, -84, -84, -84, -84, -84, 83, -84, -84, -84,
+ -84, -84, -84, -84, 42, -84, 162, -84, -84, 521,
+ -84, -84, 53, -84, -84,
+
+ -61, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -4, -88, -88, 22, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -51, -88, -88, -88, -88, -88,
+ -88, 105, -88, -88, -12, -88, -88, -88, -88, -88,
+ -7, -88, 35, 132, 62, 154, 79, -88, -88, 100,
+ 75, 36, -88, -88, -88, -88, 37, 70, -88, -1,
+ 86, -88, 92, -88, -88, -88, -88, -88, -88, -88,
+ -88, 90, 95, -88, -88, -88, -88, -88, -88, -88,
+ 87, 82, 74, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -47, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, 28,
+ -88, 20, -88, 19, -88, -88, -88, 39, -88, 42,
+ 43, 106, 61, -88, 63, 55, 52, 53, 91, 125,
+ -88, 120, 123, 118, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, 116, -88, 59, -88, -88,
+ 16, 18, 15, -88, -88, -88, -88, 21, -88, -88,
+ -88, -88, -88, 24, -88, -88, -88, -88, 38, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, 97, -88, 115, 25, -88, -88, 26, -88,
+ -88, 111, 14, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ 23, -88, -88, -88, -88, 108, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ 160, -88, 171, 163, 145, 179, -88, 135, 45, 41,
+ 66, 80, -88, -88, -88, -88, -88, -88, -88, -88,
+ 172, -88, 156, -88, 142, -88, -88, 144, -88, 122,
+ -88, -88, 114, -88, -23, -88, 48, -88, 29, -88,
+ 224, -88, 157, 175, -88, -88, 182, -88, -88, -88,
+ -88, -88, -88, 183, -88, -21, 134, -88, -88, 49,
+ -88, 3, -88, 44, -88, 2, -88, -88, -37, -88,
+ -88, -31, -88, -88, 10, -88, 47, -88, 17, -88,
+ 27, -88, -88, 13, -88, -88, -88, -88, -88, 117,
+ 6, -88, -88, -88, -88, -88, 154, -88, -88, 1,
+ -88, -88, -88, 7, -88, -35, 137, -88, 141, -88,
+ -88, -88, -88, -6, -88, -88, -88, -88, -88, 78,
+ -88, -88, -88, -88, -88, -69, -88, 11, -88, -59,
+ -88, -88, -88, -88, 83, -88, -88, 56, -88, -88,
+ -88, -88, -88, -40, -58, -88, -88, -29, -88, -88,
+ -88, -45, -88, -88, -88, -88, -3, -88, -42, -88,
+ -5, -88, -32, -88, -88, -88, 9, -88, 8, -88,
+ -2, -88, -88, -88, -88, -88, -88, -88, -88, -88,
+ -88, -88, -88, -88, -88, -88, -88, -88, -88, 12,
+ -88, -88, -56, -88, -88};
+
+const int QScriptGrammar::action_info [] = {
+ 318, -25, 350, -45, 292, 270, 426, 310, -194, 393,
+ -32, 302, 304, -37, 344, 290, 197, 346, 430, 382,
+ 329, 331, 310, 413, 318, 340, 397, 101, 338, 404,
+ -49, 292, 270, 299, 323, 290, -24, -51, -195, 343,
+ 294, 397, 333, 341, 403, 397, 149, 249, 250, 389,
+ 255, 430, 155, 454, 426, 316, 97, 437, 437, 459,
+ 151, 389, 103, 102, 98, 344, 101, 105, 413, 222,
+ 222, 109, 157, 228, 346, 187, 413, 417, 157, 104,
+ 420, 255, 454, 337, 443, 236, 421, 438, 197, 185,
+ 97, 197, 419, 413, 197, 197, 325, -263, 197, 81,
+ 197, 203, 0, 197, 416, 197, 88, 388, 387, 400,
+ 82, 197, 224, 407, 197, 81, 225, 89, 417, 197,
+ 187, 90, 81, 312, 241, 240, 82, 313, 0, 0,
+ 246, 245, 153, 82, 81, 439, 238, 231, 197, 0,
+ 308, 243, 171, 447, 172, 82, 348, 335, 238, 326,
+ 432, 198, 252, 204, 401, 173, 232, 428, 192, 235,
+ 0, 254, 253, 190, 0, 90, 91, 90, 239, 237,
+ 462, 391, 92, 244, 242, 171, 171, 172, 172, 231,
+ 239, 237, 191, 171, 192, 172, 197, 0, 173, 173,
+ 0, 207, 206, 171, 243, 172, 173, 0, 232, 0,
+ 192, 171, 171, 172, 172, 0, 173, 159, 160, 171,
+ 91, 172, 91, 0, 173, 173, 92, 0, 92, 159,
+ 160, 0, 173, 463, 461, 0, 244, 242, 272, 273,
+ 272, 273, 0, 0, 161, 162, 277, 278, 0, 411,
+ 410, 0, 0, 0, 0, 279, 161, 162, 280, 0,
+ 281, 277, 278, 0, 0, 274, 275, 274, 275, 0,
+ 279, 0, 0, 280, 0, 281, 0, 0, 171, 0,
+ 172, 164, 165, 0, 0, 0, 0, 0, 0, 166,
+ 167, 173, 0, 168, 0, 169, 164, 165, 0, 0,
+ 0, 0, 0, 0, 166, 167, 164, 165, 168, 0,
+ 169, 0, 0, 0, 166, 167, 164, 165, 168, 209,
+ 169, 0, 0, 0, 166, 167, 0, 0, 168, 210,
+ 169, 164, 165, 211, 0, 0, 0, 277, 278, 166,
+ 167, 0, 212, 168, 213, 169, 279, 0, 0, 280,
+ 0, 281, 0, 0, 0, 214, 209, 215, 88, 0,
+ 0, 0, 0, 0, 0, 216, 210, 0, 217, 89,
+ 211, 0, 0, 0, 218, 0, 0, 0, 0, 212,
+ 219, 213, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 214, 220, 215, 88, 0, 0, 42, 43,
+ 209, 0, 216, 0, 0, 217, 89, 0, 85, 0,
+ 210, 218, 0, 0, 211, 86, 0, 219, 0, 87,
+ 51, 0, 52, 212, 0, 213, 0, 0, 306, 55,
+ 220, 0, 0, 58, 0, 0, 214, 0, 215, 88,
+ 0, 0, 0, 0, 0, 0, 216, 0, 0, 217,
+ 89, 63, 0, 65, 0, 218, 0, 0, 0, 0,
+ 0, 219, 0, 0, 57, 68, 45, 0, 0, 0,
+ 42, 43, 0, 0, 220, 0, 0, 0, 0, 0,
+ 85, 0, 0, 0, 0, 0, 0, 86, 0, 0,
+ 0, 87, 51, 0, 52, 0, 0, 0, 0, 0,
+ 0, 55, 0, 0, 0, 58, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 63, 0, 65, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 57, 68, 45, 0,
+ 0, 0, 41, 42, 43, 0, 0, 0, 0, 0,
+ 0, 0, 0, 85, 0, 0, 0, 0, 0, 0,
+ 86, 0, 0, 0, 87, 51, 0, 52, 0, 0,
+ 0, 53, 0, 54, 55, 56, 0, 0, 58, 0,
+ 0, 0, 59, 0, 60, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 63, 0, 65, 0,
+ 67, 0, 70, 0, 72, 0, 0, 0, 0, 57,
+ 68, 45, 0, 0, 0, 41, 42, 43, 0, 0,
+ 0, 0, 0, 0, 0, 0, 85, 0, 0, 0,
+ 0, 0, 0, 86, 0, 0, 0, 87, 51, 0,
+ 52, 0, 0, 0, 53, 0, 54, 55, 56, 0,
+ 0, 58, 0, 0, 0, 59, 0, 60, 0, 0,
+ 442, 0, 0, 0, 0, 0, 0, 0, 0, 63,
+ 0, 65, 0, 67, 0, 70, 0, 72, 0, 0,
+ 0, 0, 57, 68, 45, 0, 0, 0, -47, 0,
+ 0, 0, 41, 42, 43, 0, 0, 0, 0, 0,
+ 0, 0, 0, 85, 0, 0, 0, 0, 0, 0,
+ 86, 0, 0, 0, 87, 51, 0, 52, 0, 0,
+ 0, 53, 0, 54, 55, 56, 0, 0, 58, 0,
+ 0, 0, 59, 0, 60, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 63, 0, 65, 0,
+ 67, 0, 70, 0, 72, 0, 0, 0, 0, 57,
+ 68, 45, 0, 0, 0, 41, 42, 43, 0, 0,
+ 0, 0, 0, 0, 0, 0, 85, 0, 0, 0,
+ 0, 0, 0, 86, 0, 0, 0, 87, 51, 0,
+ 52, 0, 0, 0, 53, 0, 54, 55, 56, 0,
+ 0, 58, 0, 0, 0, 59, 0, 60, 0, 0,
+ 445, 0, 0, 0, 0, 0, 0, 0, 0, 63,
+ 0, 65, 0, 67, 0, 70, 0, 72, 0, 0,
+ 0, 0, 57, 68, 45, 0, 0, 0, 41, 42,
+ 43, 0, 0, 0, 0, 0, 0, 0, 0, 85,
+ 0, 0, 0, 0, 0, 0, 86, 0, 0, 0,
+ 87, 51, 0, 52, 0, 0, 0, 53, 0, 54,
+ 55, 56, 0, 0, 58, 0, 0, 0, 59, 0,
+ 60, 0, 0, 0, 0, 0, 0, 202, 0, 0,
+ 0, 0, 63, 0, 65, 0, 67, 0, 70, 0,
+ 72, 0, 0, 0, 0, 57, 68, 45, 0, 0,
+ 0, 41, 42, 43, 0, 0, 0, 0, 0, 0,
+ 0, 0, 85, 0, 0, 0, 0, 0, 0, 86,
+ 0, 0, 0, 87, 51, 0, 52, 0, 0, 0,
+ 53, 0, 54, 55, 56, 0, 0, 58, 0, 0,
+ 0, 59, 0, 60, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 63, 0, 65, 0, 67,
+ 0, 70, 269, 72, 0, 0, 0, 0, 57, 68,
+ 45, 0, 0, 0, 115, 116, 117, 0, 0, 119,
+ 121, 122, 0, 0, 123, 0, 124, 0, 0, 0,
+ 126, 127, 128, 0, 0, 0, 0, 0, 0, 195,
+ 130, 131, 132, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 133, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 137,
+ 0, 0, 0, 0, 0, 0, 139, 140, 141, 0,
+ 143, 144, 145, 146, 147, 148, 0, 0, 134, 142,
+ 125, 118, 120, 136, 115, 116, 117, 0, 0, 119,
+ 121, 122, 0, 0, 123, 0, 124, 0, 0, 0,
+ 126, 127, 128, 0, 0, 0, 0, 0, 0, 129,
+ 130, 131, 132, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 133, 0, 0, 0, 135, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 137,
+ 0, 0, 0, 0, 0, 138, 139, 140, 141, 0,
+ 143, 144, 145, 146, 147, 148, 0, 0, 134, 142,
+ 125, 118, 120, 136, 37, 0, 0, 0, 0, 39,
+ 0, 41, 42, 43, 44, 0, 0, 0, 0, 0,
+ 0, 46, 85, 0, 0, 0, 0, 0, 0, 48,
+ 49, 0, 0, 50, 51, 0, 52, 0, 0, 0,
+ 53, 0, 54, 55, 56, 0, 0, 58, 0, 0,
+ 0, 59, 0, 60, 0, 0, 0, 0, 0, 61,
+ 0, 62, 0, 0, 0, 63, 64, 65, 66, 67,
+ 69, 70, 71, 72, 73, 74, 0, 0, 57, 68,
+ 45, 38, 40, 0, 37, 0, 0, 0, 0, 39,
+ 0, 41, 42, 43, 44, 0, 0, 0, 0, 0,
+ 0, 46, 47, 0, 0, 0, 0, 0, 0, 48,
+ 49, 0, 0, 50, 51, 0, 52, 0, 0, 0,
+ 53, 0, 54, 55, 56, 0, 0, 58, 0, 0,
+ 0, 59, 0, 60, 0, 0, 0, 0, 0, 61,
+ 0, 62, 0, 0, 0, 63, 64, 65, 66, 67,
+ 69, 70, 71, 72, 73, 74, 0, 0, 57, 68,
+ 45, 38, 40, 0, 355, 116, 117, 0, 0, 357,
+ 121, 359, 42, 43, 360, 0, 124, 0, 0, 0,
+ 126, 362, 363, 0, 0, 0, 0, 0, 0, 364,
+ 365, 131, 132, 50, 51, 0, 52, 0, 0, 0,
+ 53, 0, 54, 366, 56, 0, 0, 368, 0, 0,
+ 0, 59, 0, 60, 0, -190, 0, 0, 0, 369,
+ 0, 62, 0, 0, 0, 370, 371, 372, 373, 67,
+ 375, 376, 377, 378, 379, 380, 0, 0, 367, 374,
+ 361, 356, 358, 136,
+
+ 431, 422, 427, 429, 441, 352, 300, 398, 385, 464,
+ 440, 412, 409, 433, 402, 444, 406, 423, 460, 234,
+ 418, 201, 305, 196, 34, 154, 194, 199, 251, 152,
+ 205, 227, 229, 248, 150, 110, 230, 208, 352, 110,
+ 446, 300, 409, 339, 221, 412, 327, 336, 332, 334,
+ 342, 248, 347, 307, 300, 345, 0, 83, 381, 83,
+ 83, 83, 349, 83, 284, 158, 163, 182, 283, 0,
+ 83, 83, 351, 83, 309, 178, 179, 83, 177, 83,
+ 83, 83, 449, 390, 83, 184, 170, 188, 83, 285,
+ 453, 330, 83, 83, 95, 452, 0, 83, 83, 450,
+ 83, 352, 94, 286, 83, 83, 424, 93, 83, 83,
+ 83, 84, 425, 83, 180, 83, 156, 408, 83, 300,
+ 451, 194, 233, 83, 83, 247, 264, 300, 352, 223,
+ 183, 268, 0, 83, 83, 83, 83, 247, 83, 300,
+ 176, 83, 174, 83, 405, 175, 186, 0, 181, 226,
+ 83, 0, 448, 83, 0, 83, 303, 424, 282, 83,
+ 296, 425, 296, 83, 301, 268, 383, 268, 268, 384,
+ 288, 0, 0, 0, 83, 83, 328, 0, 83, 268,
+ 268, 83, 295, 268, 298, 293, 268, 271, 287, 83,
+ 83, 0, 314, 296, 268, 268, 276, 83, 268, 0,
+ 296, 296, 268, 291, 289, 268, 268, 0, 0, 0,
+ 0, 0, 0, 0, 0, 315, 0, 0, 0, 0,
+ 0, 0, 317, 324, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 83, 0, 0, 0, 0, 268, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 311, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0};
+
+const int QScriptGrammar::action_check [] = {
+ 29, 7, 16, 7, 76, 1, 36, 2, 29, 33,
+ 7, 61, 60, 7, 7, 48, 8, 36, 36, 55,
+ 61, 60, 2, 33, 29, 60, 5, 29, 36, 7,
+ 7, 76, 1, 61, 17, 48, 7, 7, 29, 55,
+ 8, 5, 31, 33, 55, 5, 7, 74, 36, 36,
+ 36, 36, 55, 29, 36, 7, 29, 8, 8, 17,
+ 8, 36, 29, 8, 36, 7, 29, 33, 33, 2,
+ 2, 55, 1, 7, 36, 76, 33, 20, 1, 60,
+ 29, 36, 29, 29, 8, 0, 60, 8, 8, 48,
+ 29, 8, 36, 33, 8, 8, 8, 36, 8, 40,
+ 8, 8, -1, 8, 6, 8, 42, 61, 62, 10,
+ 51, 8, 50, 7, 8, 40, 54, 53, 20, 8,
+ 76, 12, 40, 50, 61, 62, 51, 54, -1, -1,
+ 61, 62, 7, 51, 40, 56, 29, 15, 8, -1,
+ 60, 29, 25, 60, 27, 51, 60, 60, 29, 61,
+ 60, 56, 60, 60, 55, 38, 34, 60, 36, 56,
+ -1, 61, 62, 15, -1, 12, 57, 12, 61, 62,
+ 8, 60, 63, 61, 62, 25, 25, 27, 27, 15,
+ 61, 62, 34, 25, 36, 27, 8, -1, 38, 38,
+ -1, 61, 62, 25, 29, 27, 38, -1, 34, -1,
+ 36, 25, 25, 27, 27, -1, 38, 18, 19, 25,
+ 57, 27, 57, -1, 38, 38, 63, -1, 63, 18,
+ 19, -1, 38, 61, 62, -1, 61, 62, 18, 19,
+ 18, 19, -1, -1, 45, 46, 23, 24, -1, 61,
+ 62, -1, -1, -1, -1, 32, 45, 46, 35, -1,
+ 37, 23, 24, -1, -1, 45, 46, 45, 46, -1,
+ 32, -1, -1, 35, -1, 37, -1, -1, 25, -1,
+ 27, 23, 24, -1, -1, -1, -1, -1, -1, 31,
+ 32, 38, -1, 35, -1, 37, 23, 24, -1, -1,
+ -1, -1, -1, -1, 31, 32, 23, 24, 35, -1,
+ 37, -1, -1, -1, 31, 32, 23, 24, 35, 3,
+ 37, -1, -1, -1, 31, 32, -1, -1, 35, 13,
+ 37, 23, 24, 17, -1, -1, -1, 23, 24, 31,
+ 32, -1, 26, 35, 28, 37, 32, -1, -1, 35,
+ -1, 37, -1, -1, -1, 39, 3, 41, 42, -1,
+ -1, -1, -1, -1, -1, 49, 13, -1, 52, 53,
+ 17, -1, -1, -1, 58, -1, -1, -1, -1, 26,
+ 64, 28, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 39, 77, 41, 42, -1, -1, 12, 13,
+ 3, -1, 49, -1, -1, 52, 53, -1, 22, -1,
+ 13, 58, -1, -1, 17, 29, -1, 64, -1, 33,
+ 34, -1, 36, 26, -1, 28, -1, -1, 31, 43,
+ 77, -1, -1, 47, -1, -1, 39, -1, 41, 42,
+ -1, -1, -1, -1, -1, -1, 49, -1, -1, 52,
+ 53, 65, -1, 67, -1, 58, -1, -1, -1, -1,
+ -1, 64, -1, -1, 78, 79, 80, -1, -1, -1,
+ 12, 13, -1, -1, 77, -1, -1, -1, -1, -1,
+ 22, -1, -1, -1, -1, -1, -1, 29, -1, -1,
+ -1, 33, 34, -1, 36, -1, -1, -1, -1, -1,
+ -1, 43, -1, -1, -1, 47, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 65, -1, 67, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 78, 79, 80, -1,
+ -1, -1, 11, 12, 13, -1, -1, -1, -1, -1,
+ -1, -1, -1, 22, -1, -1, -1, -1, -1, -1,
+ 29, -1, -1, -1, 33, 34, -1, 36, -1, -1,
+ -1, 40, -1, 42, 43, 44, -1, -1, 47, -1,
+ -1, -1, 51, -1, 53, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 65, -1, 67, -1,
+ 69, -1, 71, -1, 73, -1, -1, -1, -1, 78,
+ 79, 80, -1, -1, -1, 11, 12, 13, -1, -1,
+ -1, -1, -1, -1, -1, -1, 22, -1, -1, -1,
+ -1, -1, -1, 29, -1, -1, -1, 33, 34, -1,
+ 36, -1, -1, -1, 40, -1, 42, 43, 44, -1,
+ -1, 47, -1, -1, -1, 51, -1, 53, -1, -1,
+ 56, -1, -1, -1, -1, -1, -1, -1, -1, 65,
+ -1, 67, -1, 69, -1, 71, -1, 73, -1, -1,
+ -1, -1, 78, 79, 80, -1, -1, -1, 7, -1,
+ -1, -1, 11, 12, 13, -1, -1, -1, -1, -1,
+ -1, -1, -1, 22, -1, -1, -1, -1, -1, -1,
+ 29, -1, -1, -1, 33, 34, -1, 36, -1, -1,
+ -1, 40, -1, 42, 43, 44, -1, -1, 47, -1,
+ -1, -1, 51, -1, 53, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 65, -1, 67, -1,
+ 69, -1, 71, -1, 73, -1, -1, -1, -1, 78,
+ 79, 80, -1, -1, -1, 11, 12, 13, -1, -1,
+ -1, -1, -1, -1, -1, -1, 22, -1, -1, -1,
+ -1, -1, -1, 29, -1, -1, -1, 33, 34, -1,
+ 36, -1, -1, -1, 40, -1, 42, 43, 44, -1,
+ -1, 47, -1, -1, -1, 51, -1, 53, -1, -1,
+ 56, -1, -1, -1, -1, -1, -1, -1, -1, 65,
+ -1, 67, -1, 69, -1, 71, -1, 73, -1, -1,
+ -1, -1, 78, 79, 80, -1, -1, -1, 11, 12,
+ 13, -1, -1, -1, -1, -1, -1, -1, -1, 22,
+ -1, -1, -1, -1, -1, -1, 29, -1, -1, -1,
+ 33, 34, -1, 36, -1, -1, -1, 40, -1, 42,
+ 43, 44, -1, -1, 47, -1, -1, -1, 51, -1,
+ 53, -1, -1, -1, -1, -1, -1, 60, -1, -1,
+ -1, -1, 65, -1, 67, -1, 69, -1, 71, -1,
+ 73, -1, -1, -1, -1, 78, 79, 80, -1, -1,
+ -1, 11, 12, 13, -1, -1, -1, -1, -1, -1,
+ -1, -1, 22, -1, -1, -1, -1, -1, -1, 29,
+ -1, -1, -1, 33, 34, -1, 36, -1, -1, -1,
+ 40, -1, 42, 43, 44, -1, -1, 47, -1, -1,
+ -1, 51, -1, 53, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 65, -1, 67, -1, 69,
+ -1, 71, 72, 73, -1, -1, -1, -1, 78, 79,
+ 80, -1, -1, -1, 4, 5, 6, -1, -1, 9,
+ 10, 11, -1, -1, 14, -1, 16, -1, -1, -1,
+ 20, 21, 22, -1, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 43, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 59,
+ -1, -1, -1, -1, -1, -1, 66, 67, 68, -1,
+ 70, 71, 72, 73, 74, 75, -1, -1, 78, 79,
+ 80, 81, 82, 83, 4, 5, 6, -1, -1, 9,
+ 10, 11, -1, -1, 14, -1, 16, -1, -1, -1,
+ 20, 21, 22, -1, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 43, -1, -1, -1, 47, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 59,
+ -1, -1, -1, -1, -1, 65, 66, 67, 68, -1,
+ 70, 71, 72, 73, 74, 75, -1, -1, 78, 79,
+ 80, 81, 82, 83, 4, -1, -1, -1, -1, 9,
+ -1, 11, 12, 13, 14, -1, -1, -1, -1, -1,
+ -1, 21, 22, -1, -1, -1, -1, -1, -1, 29,
+ 30, -1, -1, 33, 34, -1, 36, -1, -1, -1,
+ 40, -1, 42, 43, 44, -1, -1, 47, -1, -1,
+ -1, 51, -1, 53, -1, -1, -1, -1, -1, 59,
+ -1, 61, -1, -1, -1, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, -1, -1, 78, 79,
+ 80, 81, 82, -1, 4, -1, -1, -1, -1, 9,
+ -1, 11, 12, 13, 14, -1, -1, -1, -1, -1,
+ -1, 21, 22, -1, -1, -1, -1, -1, -1, 29,
+ 30, -1, -1, 33, 34, -1, 36, -1, -1, -1,
+ 40, -1, 42, 43, 44, -1, -1, 47, -1, -1,
+ -1, 51, -1, 53, -1, -1, -1, -1, -1, 59,
+ -1, 61, -1, -1, -1, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, -1, -1, 78, 79,
+ 80, 81, 82, -1, 4, 5, 6, -1, -1, 9,
+ 10, 11, 12, 13, 14, -1, 16, -1, -1, -1,
+ 20, 21, 22, -1, -1, -1, -1, -1, -1, 29,
+ 30, 31, 32, 33, 34, -1, 36, -1, -1, -1,
+ 40, -1, 42, 43, 44, -1, -1, 47, -1, -1,
+ -1, 51, -1, 53, -1, 55, -1, -1, -1, 59,
+ -1, 61, -1, -1, -1, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, -1, -1, 78, 79,
+ 80, 81, 82, 83,
+
+ 5, 46, 5, 45, 6, 45, 5, 76, 14, 65,
+ 2, 46, 5, 45, 73, 6, 5, 46, 6, 5,
+ 78, 6, 45, 5, 85, 6, 10, 6, 5, 9,
+ 6, 6, 6, 45, 6, 86, 14, 41, 45, 86,
+ 5, 5, 5, 80, 6, 46, 67, 45, 45, 5,
+ 81, 45, 5, 5, 5, 45, -1, 18, 45, 18,
+ 18, 18, 45, 18, 23, 26, 24, 24, 23, -1,
+ 18, 18, 45, 18, 45, 23, 23, 18, 23, 18,
+ 18, 18, 20, 5, 18, 24, 23, 28, 18, 23,
+ 20, 42, 18, 18, 20, 20, -1, 18, 18, 20,
+ 18, 45, 20, 23, 18, 18, 20, 20, 18, 18,
+ 18, 21, 20, 18, 23, 18, 21, 61, 18, 5,
+ 20, 10, 11, 18, 18, 20, 18, 5, 45, 32,
+ 24, 23, -1, 18, 18, 18, 18, 20, 18, 5,
+ 22, 18, 22, 18, 61, 22, 30, -1, 23, 34,
+ 18, -1, 20, 18, -1, 18, 42, 20, 23, 18,
+ 18, 20, 18, 18, 42, 23, 12, 23, 23, 15,
+ 25, -1, -1, -1, 18, 18, 42, -1, 18, 23,
+ 23, 18, 40, 23, 40, 29, 23, 27, 25, 18,
+ 18, -1, 35, 18, 23, 23, 25, 18, 23, -1,
+ 18, 18, 23, 31, 25, 23, 23, -1, -1, -1,
+ -1, -1, -1, -1, -1, 40, -1, -1, -1, -1,
+ -1, -1, 40, 40, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 18, -1, -1, -1, -1, 23, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 33, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1};
+
+static void recordMessage(
+ Translator *tor, const QString &context, const QString &text, const QString &comment,
+ const QString &extracomment, const QString &msgid, const TranslatorMessage::ExtraData &extra,
+ bool plural, const QString &fileName, int lineNo)
+{
+ TranslatorMessage msg(
+ context, text, comment, QString(),
+ fileName, lineNo, QStringList(),
+ TranslatorMessage::Unfinished, plural);
+ msg.setExtraComment(extracomment.simplified());
+ msg.setId(msgid);
+ msg.setExtras(extra);
+ tor->extend(msg);
+}
+
+
+namespace QScript
+{
+
+class CommentProcessor
+{
+public:
+ virtual ~CommentProcessor() {}
+ virtual void processComment(const QChar *chars, int length) = 0;
+};
+
+class Lexer
+{
+public:
+ Lexer(CommentProcessor *);
+ ~Lexer();
+
+ void setCode(const QString &c, const QString &fileName, int lineno);
+ int lex();
+
+ QString fileName() const { return yyfilename; }
+ int currentLineNo() const { return yylineno; }
+ int currentColumnNo() const { return yycolumn; }
+
+ int startLineNo() const { return startlineno; }
+ int startColumnNo() const { return startcolumn; }
+
+ int endLineNo() const { return currentLineNo(); }
+ int endColumnNo() const
+ { int col = currentColumnNo(); return (col > 0) ? col - 1 : col; }
+
+ bool prevTerminator() const { return terminator; }
+
+ enum State { Start,
+ Identifier,
+ InIdentifier,
+ InSingleLineComment,
+ InMultiLineComment,
+ InNum,
+ InNum0,
+ InHex,
+ InOctal,
+ InDecimal,
+ InExponentIndicator,
+ InExponent,
+ Hex,
+ Octal,
+ Number,
+ String,
+ Eof,
+ InString,
+ InEscapeSequence,
+ InHexEscape,
+ InUnicodeEscape,
+ Other,
+ Bad };
+
+ enum Error {
+ NoError,
+ IllegalCharacter,
+ UnclosedStringLiteral,
+ IllegalEscapeSequence,
+ IllegalUnicodeEscapeSequence,
+ UnclosedComment,
+ IllegalExponentIndicator,
+ IllegalIdentifier
+ };
+
+ enum ParenthesesState {
+ IgnoreParentheses,
+ CountParentheses,
+ BalancedParentheses
+ };
+
+ enum RegExpBodyPrefix {
+ NoPrefix,
+ EqualPrefix
+ };
+
+ bool scanRegExp(RegExpBodyPrefix prefix = NoPrefix);
+
+ QString pattern;
+ int flags;
+
+ State lexerState() const
+ { return state; }
+
+ QString errorMessage() const
+ { return errmsg; }
+ void setErrorMessage(const QString &err)
+ { errmsg = err; }
+ void setErrorMessage(const char *err)
+ { setErrorMessage(QString::fromLatin1(err)); }
+
+ Error error() const
+ { return err; }
+ void clearError()
+ { err = NoError; }
+
+private:
+ QString yyfilename;
+ int yylineno;
+ bool done;
+ char *buffer8;
+ QChar *buffer16;
+ uint size8, size16;
+ uint pos8, pos16;
+ bool terminator;
+ bool restrKeyword;
+ // encountered delimiter like "'" and "}" on last run
+ bool delimited;
+ int stackToken;
+
+ State state;
+ void setDone(State s);
+ uint pos;
+ void shift(uint p);
+ int lookupKeyword(const char *);
+
+ bool isWhiteSpace() const;
+ bool isLineTerminator() const;
+ bool isHexDigit(ushort c) const;
+ bool isOctalDigit(ushort c) const;
+
+ int matchPunctuator(ushort c1, ushort c2,
+ ushort c3, ushort c4);
+ ushort singleEscape(ushort c) const;
+ ushort convertOctal(ushort c1, ushort c2,
+ ushort c3) const;
+public:
+ static unsigned char convertHex(ushort c1);
+ static unsigned char convertHex(ushort c1, ushort c2);
+ static QChar convertUnicode(ushort c1, ushort c2,
+ ushort c3, ushort c4);
+ static bool isIdentLetter(ushort c);
+ static bool isDecimalDigit(ushort c);
+
+ inline int ival() const { return qsyylval.toInt(); }
+ inline double dval() const { return qsyylval.toDouble(); }
+ inline QString ustr() const { return qsyylval.toString(); }
+ inline QVariant val() const { return qsyylval; }
+
+ const QChar *characterBuffer() const { return buffer16; }
+ int characterCount() const { return pos16; }
+
+private:
+ void record8(ushort c);
+ void record16(QChar c);
+ void recordStartPos();
+
+ int findReservedWord(const QChar *buffer, int size) const;
+
+ void syncProhibitAutomaticSemicolon();
+
+ void processComment(const QChar *, int);
+
+ const QChar *code;
+ uint length;
+ int yycolumn;
+ int startlineno;
+ int startcolumn;
+ int bol; // begin of line
+
+ QVariant qsyylval;
+
+ // current and following unicode characters
+ ushort current, next1, next2, next3;
+
+ struct keyword {
+ const char *name;
+ int token;
+ };
+
+ QString errmsg;
+ Error err;
+
+ bool wantRx;
+ bool check_reserved;
+
+ ParenthesesState parenthesesState;
+ int parenthesesCount;
+ bool prohibitAutomaticSemicolon;
+
+ CommentProcessor *commentProcessor;
+};
+
+} // namespace QScript
+
+extern double qstrtod(const char *s00, char const **se, bool *ok);
+
+#define shiftWindowsLineBreak() if(current == '\r' && next1 == '\n') shift(1);
+
+namespace QScript {
+
+static int toDigit(char c)
+{
+ if ((c >= '0') && (c <= '9'))
+ return c - '0';
+ else if ((c >= 'a') && (c <= 'z'))
+ return 10 + c - 'a';
+ else if ((c >= 'A') && (c <= 'Z'))
+ return 10 + c - 'A';
+ return -1;
+}
+
+double integerFromString(const char *buf, int size, int radix)
+{
+ if (size == 0)
+ return qSNaN();
+
+ double sign = 1.0;
+ int i = 0;
+ if (buf[0] == '+') {
+ ++i;
+ } else if (buf[0] == '-') {
+ sign = -1.0;
+ ++i;
+ }
+
+ if (((size-i) >= 2) && (buf[i] == '0')) {
+ if (((buf[i+1] == 'x') || (buf[i+1] == 'X'))
+ && (radix < 34)) {
+ if ((radix != 0) && (radix != 16))
+ return 0;
+ radix = 16;
+ i += 2;
+ } else {
+ if (radix == 0) {
+ radix = 8;
+ ++i;
+ }
+ }
+ } else if (radix == 0) {
+ radix = 10;
+ }
+
+ int j = i;
+ for ( ; i < size; ++i) {
+ int d = toDigit(buf[i]);
+ if ((d == -1) || (d >= radix))
+ break;
+ }
+ double result;
+ if (j == i) {
+ if (!qstrcmp(buf, "Infinity"))
+ result = qInf();
+ else
+ result = qSNaN();
+ } else {
+ result = 0;
+ double multiplier = 1;
+ for (--i ; i >= j; --i, multiplier *= radix)
+ result += toDigit(buf[i]) * multiplier;
+ }
+ result *= sign;
+ return result;
+}
+
+} // namespace QScript
+
+QScript::Lexer::Lexer(QScript::CommentProcessor *proc)
+ :
+ yylineno(0),
+ size8(128), size16(128), restrKeyword(false),
+ stackToken(-1), pos(0),
+ code(0), length(0),
+ bol(true),
+ current(0), next1(0), next2(0), next3(0),
+ err(NoError),
+ check_reserved(true),
+ parenthesesState(IgnoreParentheses),
+ prohibitAutomaticSemicolon(false),
+ commentProcessor(proc)
+{
+ // allocate space for read buffers
+ buffer8 = new char[size8];
+ buffer16 = new QChar[size16];
+ flags = 0;
+
+}
+
+QScript::Lexer::~Lexer()
+{
+ delete [] buffer8;
+ delete [] buffer16;
+}
+
+void QScript::Lexer::setCode(const QString &c, const QString &fileName, int lineno)
+{
+ errmsg = QString();
+ yyfilename = fileName;
+ yylineno = lineno;
+ yycolumn = 1;
+ restrKeyword = false;
+ delimited = false;
+ stackToken = -1;
+ pos = 0;
+ code = c.unicode();
+ length = c.length();
+ bol = true;
+
+ // read first characters
+ current = (length > 0) ? code[0].unicode() : 0;
+ next1 = (length > 1) ? code[1].unicode() : 0;
+ next2 = (length > 2) ? code[2].unicode() : 0;
+ next3 = (length > 3) ? code[3].unicode() : 0;
+}
+
+void QScript::Lexer::shift(uint p)
+{
+ while (p--) {
+ ++pos;
+ ++yycolumn;
+ current = next1;
+ next1 = next2;
+ next2 = next3;
+ next3 = (pos + 3 < length) ? code[pos+3].unicode() : 0;
+ }
+}
+
+void QScript::Lexer::setDone(State s)
+{
+ state = s;
+ done = true;
+}
+
+int QScript::Lexer::findReservedWord(const QChar *c, int size) const
+{
+ switch (size) {
+ case 2: {
+ if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('o'))
+ return QScriptGrammar::T_DO;
+ else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('f'))
+ return QScriptGrammar::T_IF;
+ else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n'))
+ return QScriptGrammar::T_IN;
+ } break;
+
+ case 3: {
+ if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('o') && c[2] == QLatin1Char('r'))
+ return QScriptGrammar::T_FOR;
+ else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('e') && c[2] == QLatin1Char('w'))
+ return QScriptGrammar::T_NEW;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') && c[2] == QLatin1Char('y'))
+ return QScriptGrammar::T_TRY;
+ else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('a') && c[2] == QLatin1Char('r'))
+ return QScriptGrammar::T_VAR;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') && c[2] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 4: {
+ if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e'))
+ return QScriptGrammar::T_CASE;
+ else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('l')
+ && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e'))
+ return QScriptGrammar::T_ELSE;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('s'))
+ return QScriptGrammar::T_THIS;
+ else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('d'))
+ return QScriptGrammar::T_VOID;
+ else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('i')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('h'))
+ return QScriptGrammar::T_WITH;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('e'))
+ return QScriptGrammar::T_TRUE;
+ else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('u')
+ && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('l'))
+ return QScriptGrammar::T_NULL;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('n')
+ && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('m'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('y')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('l') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('g'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('r'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('g') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('o'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 5: {
+ if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('e') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('k'))
+ return QScriptGrammar::T_BREAK;
+ else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('c')
+ && c[4] == QLatin1Char('h'))
+ return QScriptGrammar::T_CATCH;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('r') && c[3] == QLatin1Char('o')
+ && c[4] == QLatin1Char('w'))
+ return QScriptGrammar::T_THROW;
+ else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('l')
+ && c[4] == QLatin1Char('e'))
+ return QScriptGrammar::T_WHILE;
+ else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('s')
+ && c[4] == QLatin1Char('t'))
+ return QScriptGrammar::T_CONST;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('s')
+ && c[4] == QLatin1Char('e'))
+ return QScriptGrammar::T_FALSE;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('r')
+ && c[4] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('u')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('r'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('i')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('l'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('l')
+ && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('s')
+ && c[4] == QLatin1Char('s'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('l')
+ && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 6: {
+ if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e')
+ && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('e'))
+ return QScriptGrammar::T_DELETE;
+ else if (c[0] == QLatin1Char('r') && c[1] == QLatin1Char('e')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('u')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('n'))
+ return QScriptGrammar::T_RETURN;
+ else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('w')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('c') && c[5] == QLatin1Char('h'))
+ return QScriptGrammar::T_SWITCH;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('y')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('o') && c[5] == QLatin1Char('f'))
+ return QScriptGrammar::T_TYPEOF;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('x')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('t')
+ && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('c'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('b')
+ && c[4] == QLatin1Char('l') && c[5] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('u')
+ && c[2] == QLatin1Char('b') && c[3] == QLatin1Char('l')
+ && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('c'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('i')
+ && c[4] == QLatin1Char('v') && c[5] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('r') && c[3] == QLatin1Char('o')
+ && c[4] == QLatin1Char('w') && c[5] == QLatin1Char('s'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 7: {
+ if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e')
+ && c[2] == QLatin1Char('f') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('u') && c[5] == QLatin1Char('l')
+ && c[6] == QLatin1Char('t'))
+ return QScriptGrammar::T_DEFAULT;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('i')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('l') && c[5] == QLatin1Char('l')
+ && c[6] == QLatin1Char('y'))
+ return QScriptGrammar::T_FINALLY;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('l')
+ && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('a')
+ && c[6] == QLatin1Char('n'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('x')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('n') && c[5] == QLatin1Char('d')
+ && c[6] == QLatin1Char('s'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('c') && c[3] == QLatin1Char('k')
+ && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('g')
+ && c[6] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('v')
+ && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('t')
+ && c[6] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 8: {
+ if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('n')
+ && c[6] == QLatin1Char('u') && c[7] == QLatin1Char('e'))
+ return QScriptGrammar::T_CONTINUE;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('u')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('c')
+ && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('i')
+ && c[6] == QLatin1Char('o') && c[7] == QLatin1Char('n'))
+ return QScriptGrammar::T_FUNCTION;
+ else if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e')
+ && c[2] == QLatin1Char('b') && c[3] == QLatin1Char('u')
+ && c[4] == QLatin1Char('g') && c[5] == QLatin1Char('g')
+ && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('r'))
+ return QScriptGrammar::T_DEBUGGER;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('a') && c[1] == QLatin1Char('b')
+ && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('a')
+ && c[6] == QLatin1Char('c') && c[7] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('i')
+ && c[6] == QLatin1Char('l') && c[7] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 9: {
+ if (check_reserved) {
+ if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('f')
+ && c[6] == QLatin1Char('a') && c[7] == QLatin1Char('c')
+ && c[8] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('n')
+ && c[4] == QLatin1Char('s') && c[5] == QLatin1Char('i')
+ && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('n')
+ && c[8] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('c')
+ && c[6] == QLatin1Char('t') && c[7] == QLatin1Char('e')
+ && c[8] == QLatin1Char('d'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 10: {
+ if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n')
+ && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('n')
+ && c[6] == QLatin1Char('c') && c[7] == QLatin1Char('e')
+ && c[8] == QLatin1Char('o') && c[9] == QLatin1Char('f'))
+ return QScriptGrammar::T_INSTANCEOF;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('l')
+ && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('m')
+ && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('n')
+ && c[8] == QLatin1Char('t') && c[9] == QLatin1Char('s'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 12: {
+ if (check_reserved) {
+ if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('y')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('c')
+ && c[4] == QLatin1Char('h') && c[5] == QLatin1Char('r')
+ && c[6] == QLatin1Char('o') && c[7] == QLatin1Char('n')
+ && c[8] == QLatin1Char('i') && c[9] == QLatin1Char('z')
+ && c[10] == QLatin1Char('e') && c[11] == QLatin1Char('d'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ } // switch
+
+ return -1;
+}
+
+int QScript::Lexer::lex()
+{
+ int token = 0;
+ state = Start;
+ ushort stringType = 0; // either single or double quotes
+ pos8 = pos16 = 0;
+ done = false;
+ terminator = false;
+
+ // did we push a token on the stack previously ?
+ // (after an automatic semicolon insertion)
+ if (stackToken >= 0) {
+ setDone(Other);
+ token = stackToken;
+ stackToken = -1;
+ }
+
+ while (!done) {
+ switch (state) {
+ case Start:
+ if (isWhiteSpace()) {
+ // do nothing
+ } else if (current == '/' && next1 == '/') {
+ recordStartPos();
+ shift(1);
+ Q_ASSERT(pos16 == 0);
+ state = InSingleLineComment;
+ } else if (current == '/' && next1 == '*') {
+ recordStartPos();
+ shift(1);
+ Q_ASSERT(pos16 == 0);
+ state = InMultiLineComment;
+ } else if (current == 0) {
+ syncProhibitAutomaticSemicolon();
+ if (!terminator && !delimited && !prohibitAutomaticSemicolon) {
+ // automatic semicolon insertion if program incomplete
+ token = QScriptGrammar::T_SEMICOLON;
+ stackToken = 0;
+ setDone(Other);
+ } else {
+ setDone(Eof);
+ }
+ } else if (isLineTerminator()) {
+ shiftWindowsLineBreak();
+ yylineno++;
+ yycolumn = 0;
+ bol = true;
+ terminator = true;
+ syncProhibitAutomaticSemicolon();
+ if (restrKeyword) {
+ token = QScriptGrammar::T_SEMICOLON;
+ setDone(Other);
+ }
+ } else if (current == '"' || current == '\'') {
+ recordStartPos();
+ state = InString;
+ stringType = current;
+ } else if (isIdentLetter(current)) {
+ recordStartPos();
+ record16(current);
+ state = InIdentifier;
+ } else if (current == '0') {
+ recordStartPos();
+ record8(current);
+ state = InNum0;
+ } else if (isDecimalDigit(current)) {
+ recordStartPos();
+ record8(current);
+ state = InNum;
+ } else if (current == '.' && isDecimalDigit(next1)) {
+ recordStartPos();
+ record8(current);
+ state = InDecimal;
+ } else {
+ recordStartPos();
+ token = matchPunctuator(current, next1, next2, next3);
+ if (token != -1) {
+ if (terminator && !delimited && !prohibitAutomaticSemicolon
+ && (token == QScriptGrammar::T_PLUS_PLUS
+ || token == QScriptGrammar::T_MINUS_MINUS)) {
+ // automatic semicolon insertion
+ stackToken = token;
+ token = QScriptGrammar::T_SEMICOLON;
+ }
+ setDone(Other);
+ }
+ else {
+ setDone(Bad);
+ err = IllegalCharacter;
+ errmsg = LU::tr("Illegal character");
+ }
+ }
+ break;
+ case InString:
+ if (current == stringType) {
+ shift(1);
+ setDone(String);
+ } else if (current == 0 || isLineTerminator()) {
+ setDone(Bad);
+ err = UnclosedStringLiteral;
+ errmsg = LU::tr("Unclosed string at end of line");
+ } else if (current == '\\') {
+ state = InEscapeSequence;
+ } else {
+ record16(current);
+ }
+ break;
+ // Escape Sequences inside of strings
+ case InEscapeSequence:
+ if (isOctalDigit(current)) {
+ if (current >= '0' && current <= '3' &&
+ isOctalDigit(next1) && isOctalDigit(next2)) {
+ record16(convertOctal(current, next1, next2));
+ shift(2);
+ state = InString;
+ } else if (isOctalDigit(current) &&
+ isOctalDigit(next1)) {
+ record16(convertOctal('0', current, next1));
+ shift(1);
+ state = InString;
+ } else if (isOctalDigit(current)) {
+ record16(convertOctal('0', '0', current));
+ state = InString;
+ } else {
+ setDone(Bad);
+ err = IllegalEscapeSequence;
+ errmsg = LU::tr("Illegal escape sequence");
+ }
+ } else if (current == 'x')
+ state = InHexEscape;
+ else if (current == 'u')
+ state = InUnicodeEscape;
+ else {
+ record16(singleEscape(current));
+ state = InString;
+ }
+ break;
+ case InHexEscape:
+ if (isHexDigit(current) && isHexDigit(next1)) {
+ state = InString;
+ record16(QLatin1Char(convertHex(current, next1)));
+ shift(1);
+ } else if (current == stringType) {
+ record16(QLatin1Char('x'));
+ shift(1);
+ setDone(String);
+ } else {
+ record16(QLatin1Char('x'));
+ record16(current);
+ state = InString;
+ }
+ break;
+ case InUnicodeEscape:
+ if (isHexDigit(current) && isHexDigit(next1) &&
+ isHexDigit(next2) && isHexDigit(next3)) {
+ record16(convertUnicode(current, next1, next2, next3));
+ shift(3);
+ state = InString;
+ } else if (current == stringType) {
+ record16(QLatin1Char('u'));
+ shift(1);
+ setDone(String);
+ } else {
+ setDone(Bad);
+ err = IllegalUnicodeEscapeSequence;
+ errmsg = LU::tr("Illegal unicode escape sequence");
+ }
+ break;
+ case InSingleLineComment:
+ if (isLineTerminator()) {
+ record16(current); // include newline
+ processComment(buffer16, pos16);
+ shiftWindowsLineBreak();
+ yylineno++;
+ yycolumn = 0;
+ pos16 = 0;
+ terminator = true;
+ bol = true;
+ if (restrKeyword) {
+ token = QScriptGrammar::T_SEMICOLON;
+ setDone(Other);
+ } else
+ state = Start;
+ } else if (current == 0) {
+ setDone(Eof);
+ } else {
+ record16(current);
+ }
+ break;
+ case InMultiLineComment:
+ if (current == 0) {
+ setDone(Bad);
+ err = UnclosedComment;
+ errmsg = LU::tr("Unclosed comment at end of file");
+ } else if (isLineTerminator()) {
+ shiftWindowsLineBreak();
+ yylineno++;
+ } else if (current == '*' && next1 == '/') {
+ processComment(buffer16, pos16);
+ pos16 = 0;
+ state = Start;
+ shift(1);
+ } else {
+ record16(current);
+ }
+ break;
+ case InIdentifier:
+ if (isIdentLetter(current) || isDecimalDigit(current)) {
+ record16(current);
+ break;
+ }
+ setDone(Identifier);
+ break;
+ case InNum0:
+ if (current == 'x' || current == 'X') {
+ record8(current);
+ state = InHex;
+ } else if (current == '.') {
+ record8(current);
+ state = InDecimal;
+ } else if (current == 'e' || current == 'E') {
+ record8(current);
+ state = InExponentIndicator;
+ } else if (isOctalDigit(current)) {
+ record8(current);
+ state = InOctal;
+ } else if (isDecimalDigit(current)) {
+ record8(current);
+ state = InDecimal;
+ } else {
+ setDone(Number);
+ }
+ break;
+ case InHex:
+ if (isHexDigit(current))
+ record8(current);
+ else
+ setDone(Hex);
+ break;
+ case InOctal:
+ if (isOctalDigit(current)) {
+ record8(current);
+ } else if (isDecimalDigit(current)) {
+ record8(current);
+ state = InDecimal;
+ } else {
+ setDone(Octal);
+ }
+ break;
+ case InNum:
+ if (isDecimalDigit(current)) {
+ record8(current);
+ } else if (current == '.') {
+ record8(current);
+ state = InDecimal;
+ } else if (current == 'e' || current == 'E') {
+ record8(current);
+ state = InExponentIndicator;
+ } else {
+ setDone(Number);
+ }
+ break;
+ case InDecimal:
+ if (isDecimalDigit(current)) {
+ record8(current);
+ } else if (current == 'e' || current == 'E') {
+ record8(current);
+ state = InExponentIndicator;
+ } else {
+ setDone(Number);
+ }
+ break;
+ case InExponentIndicator:
+ if (current == '+' || current == '-') {
+ record8(current);
+ } else if (isDecimalDigit(current)) {
+ record8(current);
+ state = InExponent;
+ } else {
+ setDone(Bad);
+ err = IllegalExponentIndicator;
+ errmsg = LU::tr("Illegal syntax for exponential number");
+ }
+ break;
+ case InExponent:
+ if (isDecimalDigit(current)) {
+ record8(current);
+ } else {
+ setDone(Number);
+ }
+ break;
+ default:
+ Q_ASSERT_X(0, "Lexer::lex", "Unhandled state in switch statement");
+ }
+
+ // move on to the next character
+ if (!done)
+ shift(1);
+ if (state != Start && state != InSingleLineComment)
+ bol = false;
+ }
+
+ // no identifiers allowed directly after numeric literal, e.g. "3in" is bad
+ if ((state == Number || state == Octal || state == Hex)
+ && isIdentLetter(current)) {
+ state = Bad;
+ err = IllegalIdentifier;
+ errmsg = LU::tr("Identifier cannot start with numeric literal");
+ }
+
+ // terminate string
+ buffer8[pos8] = '\0';
+
+ double dval = 0;
+ if (state == Number) {
+ dval = qstrtod(buffer8, 0, 0);
+ } else if (state == Hex) { // scan hex numbers
+ dval = QScript::integerFromString(buffer8, pos8, 16);
+ state = Number;
+ } else if (state == Octal) { // scan octal number
+ dval = QScript::integerFromString(buffer8, pos8, 8);
+ state = Number;
+ }
+
+ restrKeyword = false;
+ delimited = false;
+
+ switch (parenthesesState) {
+ case IgnoreParentheses:
+ break;
+ case CountParentheses:
+ if (token == QScriptGrammar::T_RPAREN) {
+ --parenthesesCount;
+ if (parenthesesCount == 0)
+ parenthesesState = BalancedParentheses;
+ } else if (token == QScriptGrammar::T_LPAREN) {
+ ++parenthesesCount;
+ }
+ break;
+ case BalancedParentheses:
+ parenthesesState = IgnoreParentheses;
+ break;
+ }
+
+ switch (state) {
+ case Eof:
+ return 0;
+ case Other:
+ if(token == QScriptGrammar::T_RBRACE || token == QScriptGrammar::T_SEMICOLON)
+ delimited = true;
+ return token;
+ case Identifier:
+ if ((token = findReservedWord(buffer16, pos16)) < 0) {
+ /* TODO: close leak on parse error. same holds true for String */
+ qsyylval = QString(buffer16, pos16);
+ return QScriptGrammar::T_IDENTIFIER;
+ }
+ if (token == QScriptGrammar::T_CONTINUE || token == QScriptGrammar::T_BREAK
+ || token == QScriptGrammar::T_RETURN || token == QScriptGrammar::T_THROW) {
+ restrKeyword = true;
+ } else if (token == QScriptGrammar::T_IF || token == QScriptGrammar::T_FOR
+ || token == QScriptGrammar::T_WHILE || token == QScriptGrammar::T_WITH) {
+ parenthesesState = CountParentheses;
+ parenthesesCount = 0;
+ } else if (token == QScriptGrammar::T_DO) {
+ parenthesesState = BalancedParentheses;
+ }
+ return token;
+ case String:
+ qsyylval = QString(buffer16, pos16);
+ return QScriptGrammar::T_STRING_LITERAL;
+ case Number:
+ qsyylval = dval;
+ return QScriptGrammar::T_NUMERIC_LITERAL;
+ case Bad:
+ return -1;
+ default:
+ Q_ASSERT(!"unhandled numeration value in switch");
+ return -1;
+ }
+}
+
+bool QScript::Lexer::isWhiteSpace() const
+{
+ return (current == ' ' || current == '\t' ||
+ current == 0x0b || current == 0x0c);
+}
+
+bool QScript::Lexer::isLineTerminator() const
+{
+ return (current == '\n' || current == '\r');
+}
+
+bool QScript::Lexer::isIdentLetter(ushort c)
+{
+ /* TODO: allow other legitimate unicode chars */
+ return ((c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || c == '$'
+ || c == '_');
+}
+
+bool QScript::Lexer::isDecimalDigit(ushort c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+bool QScript::Lexer::isHexDigit(ushort c) const
+{
+ return ((c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'f')
+ || (c >= 'A' && c <= 'F'));
+}
+
+bool QScript::Lexer::isOctalDigit(ushort c) const
+{
+ return (c >= '0' && c <= '7');
+}
+
+int QScript::Lexer::matchPunctuator(ushort c1, ushort c2,
+ ushort c3, ushort c4)
+{
+ if (c1 == '>' && c2 == '>' && c3 == '>' && c4 == '=') {
+ shift(4);
+ return QScriptGrammar::T_GT_GT_GT_EQ;
+ } else if (c1 == '=' && c2 == '=' && c3 == '=') {
+ shift(3);
+ return QScriptGrammar::T_EQ_EQ_EQ;
+ } else if (c1 == '!' && c2 == '=' && c3 == '=') {
+ shift(3);
+ return QScriptGrammar::T_NOT_EQ_EQ;
+ } else if (c1 == '>' && c2 == '>' && c3 == '>') {
+ shift(3);
+ return QScriptGrammar::T_GT_GT_GT;
+ } else if (c1 == '<' && c2 == '<' && c3 == '=') {
+ shift(3);
+ return QScriptGrammar::T_LT_LT_EQ;
+ } else if (c1 == '>' && c2 == '>' && c3 == '=') {
+ shift(3);
+ return QScriptGrammar::T_GT_GT_EQ;
+ } else if (c1 == '<' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_LE;
+ } else if (c1 == '>' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_GE;
+ } else if (c1 == '!' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_NOT_EQ;
+ } else if (c1 == '+' && c2 == '+') {
+ shift(2);
+ return QScriptGrammar::T_PLUS_PLUS;
+ } else if (c1 == '-' && c2 == '-') {
+ shift(2);
+ return QScriptGrammar::T_MINUS_MINUS;
+ } else if (c1 == '=' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_EQ_EQ;
+ } else if (c1 == '+' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_PLUS_EQ;
+ } else if (c1 == '-' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_MINUS_EQ;
+ } else if (c1 == '*' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_STAR_EQ;
+ } else if (c1 == '/' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_DIVIDE_EQ;
+ } else if (c1 == '&' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_AND_EQ;
+ } else if (c1 == '^' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_XOR_EQ;
+ } else if (c1 == '%' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_REMAINDER_EQ;
+ } else if (c1 == '|' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_OR_EQ;
+ } else if (c1 == '<' && c2 == '<') {
+ shift(2);
+ return QScriptGrammar::T_LT_LT;
+ } else if (c1 == '>' && c2 == '>') {
+ shift(2);
+ return QScriptGrammar::T_GT_GT;
+ } else if (c1 == '&' && c2 == '&') {
+ shift(2);
+ return QScriptGrammar::T_AND_AND;
+ } else if (c1 == '|' && c2 == '|') {
+ shift(2);
+ return QScriptGrammar::T_OR_OR;
+ }
+
+ switch(c1) {
+ case '=': shift(1); return QScriptGrammar::T_EQ;
+ case '>': shift(1); return QScriptGrammar::T_GT;
+ case '<': shift(1); return QScriptGrammar::T_LT;
+ case ',': shift(1); return QScriptGrammar::T_COMMA;
+ case '!': shift(1); return QScriptGrammar::T_NOT;
+ case '~': shift(1); return QScriptGrammar::T_TILDE;
+ case '?': shift(1); return QScriptGrammar::T_QUESTION;
+ case ':': shift(1); return QScriptGrammar::T_COLON;
+ case '.': shift(1); return QScriptGrammar::T_DOT;
+ case '+': shift(1); return QScriptGrammar::T_PLUS;
+ case '-': shift(1); return QScriptGrammar::T_MINUS;
+ case '*': shift(1); return QScriptGrammar::T_STAR;
+ case '/': shift(1); return QScriptGrammar::T_DIVIDE_;
+ case '&': shift(1); return QScriptGrammar::T_AND;
+ case '|': shift(1); return QScriptGrammar::T_OR;
+ case '^': shift(1); return QScriptGrammar::T_XOR;
+ case '%': shift(1); return QScriptGrammar::T_REMAINDER;
+ case '(': shift(1); return QScriptGrammar::T_LPAREN;
+ case ')': shift(1); return QScriptGrammar::T_RPAREN;
+ case '{': shift(1); return QScriptGrammar::T_LBRACE;
+ case '}': shift(1); return QScriptGrammar::T_RBRACE;
+ case '[': shift(1); return QScriptGrammar::T_LBRACKET;
+ case ']': shift(1); return QScriptGrammar::T_RBRACKET;
+ case ';': shift(1); return QScriptGrammar::T_SEMICOLON;
+
+ default: return -1;
+ }
+}
+
+ushort QScript::Lexer::singleEscape(ushort c) const
+{
+ switch(c) {
+ case 'b':
+ return 0x08;
+ case 't':
+ return 0x09;
+ case 'n':
+ return 0x0A;
+ case 'v':
+ return 0x0B;
+ case 'f':
+ return 0x0C;
+ case 'r':
+ return 0x0D;
+ case '"':
+ return 0x22;
+ case '\'':
+ return 0x27;
+ case '\\':
+ return 0x5C;
+ default:
+ return c;
+ }
+}
+
+ushort QScript::Lexer::convertOctal(ushort c1, ushort c2,
+ ushort c3) const
+{
+ return ((c1 - '0') * 64 + (c2 - '0') * 8 + c3 - '0');
+}
+
+unsigned char QScript::Lexer::convertHex(ushort c)
+{
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ else if (c >= 'a' && c <= 'f')
+ return (c - 'a' + 10);
+ else
+ return (c - 'A' + 10);
+}
+
+unsigned char QScript::Lexer::convertHex(ushort c1, ushort c2)
+{
+ return ((convertHex(c1) << 4) + convertHex(c2));
+}
+
+QChar QScript::Lexer::convertUnicode(ushort c1, ushort c2,
+ ushort c3, ushort c4)
+{
+ return QChar((convertHex(c3) << 4) + convertHex(c4),
+ (convertHex(c1) << 4) + convertHex(c2));
+}
+
+void QScript::Lexer::record8(ushort c)
+{
+ Q_ASSERT(c <= 0xff);
+
+ // enlarge buffer if full
+ if (pos8 >= size8 - 1) {
+ char *tmp = new char[2 * size8];
+ memcpy(tmp, buffer8, size8 * sizeof(char));
+ delete [] buffer8;
+ buffer8 = tmp;
+ size8 *= 2;
+ }
+
+ buffer8[pos8++] = (char) c;
+}
+
+void QScript::Lexer::record16(QChar c)
+{
+ // enlarge buffer if full
+ if (pos16 >= size16 - 1) {
+ QChar *tmp = new QChar[2 * size16];
+ memcpy(tmp, buffer16, size16 * sizeof(QChar));
+ delete [] buffer16;
+ buffer16 = tmp;
+ size16 *= 2;
+ }
+
+ buffer16[pos16++] = c;
+}
+
+void QScript::Lexer::recordStartPos()
+{
+ startlineno = yylineno;
+ startcolumn = yycolumn;
+}
+
+bool QScript::Lexer::scanRegExp(RegExpBodyPrefix prefix)
+{
+ pos16 = 0;
+ bool lastWasEscape = false;
+
+ if (prefix == EqualPrefix)
+ record16(QLatin1Char('='));
+
+ while (1) {
+ if (isLineTerminator() || current == 0) {
+ errmsg = LU::tr("Unterminated regular expression literal");
+ return false;
+ }
+ else if (current != '/' || lastWasEscape == true)
+ {
+ record16(current);
+ lastWasEscape = !lastWasEscape && (current == '\\');
+ }
+ else {
+ pattern = QString(buffer16, pos16);
+ pos16 = 0;
+ shift(1);
+ break;
+ }
+ shift(1);
+ }
+
+ flags = 0;
+ while (isIdentLetter(current)) {
+ record16(current);
+ shift(1);
+ }
+
+ return true;
+}
+
+void QScript::Lexer::syncProhibitAutomaticSemicolon()
+{
+ if (parenthesesState == BalancedParentheses) {
+ // we have seen something like "if (foo)", which means we should
+ // never insert an automatic semicolon at this point, since it would
+ // then be expanded into an empty statement (ECMA-262 7.9.1)
+ prohibitAutomaticSemicolon = true;
+ parenthesesState = IgnoreParentheses;
+ } else {
+ prohibitAutomaticSemicolon = false;
+ }
+}
+
+void QScript::Lexer::processComment(const QChar *chars, int length)
+{
+ commentProcessor->processComment(chars, length);
+}
+
+
+class Translator;
+
+class QScriptParser: protected QScriptGrammar, public QScript::CommentProcessor
+{
+public:
+ QVariant val;
+
+ struct Location {
+ int startLine;
+ int startColumn;
+ int endLine;
+ int endColumn;
+ };
+
+public:
+ QScriptParser();
+ ~QScriptParser();
+
+ void setLexer(QScript::Lexer *);
+ void setTranslator(Translator *);
+
+ bool parse();
+
+ QString fileName() const
+ { return lexer->fileName(); }
+ inline QString errorMessage() const
+ { return error_message; }
+ inline int errorLineNumber() const
+ { return error_lineno; }
+ inline int errorColumnNumber() const
+ { return error_column; }
+
+protected:
+ inline void reallocateStack();
+
+ inline QVariant &sym(int index)
+ { return sym_stack [tos + index - 1]; }
+
+ inline Location &loc(int index)
+ { return location_stack [tos + index - 2]; }
+
+ std::ostream &yyMsg(int line = 0);
+
+ virtual void processComment(const QChar *, int);
+
+protected:
+ int tos;
+ int stack_size;
+ QVector<QVariant> sym_stack;
+ int *state_stack;
+ Location *location_stack;
+ QString error_message;
+ int error_lineno;
+ int error_column;
+
+private:
+ QScript::Lexer *lexer;
+ Translator *translator;
+ QString trcontext;
+ QString extracomment;
+ QString msgid;
+ QString sourcetext;
+ TranslatorMessage::ExtraData extra;
+};
+
+inline void QScriptParser::reallocateStack()
+{
+ if (! stack_size)
+ stack_size = 128;
+ else
+ stack_size <<= 1;
+
+ sym_stack.resize(stack_size);
+ state_stack = reinterpret_cast<int*> (qRealloc(state_stack, stack_size * sizeof(int)));
+ location_stack = reinterpret_cast<Location*> (qRealloc(location_stack, stack_size * sizeof(Location)));
+}
+
+inline static bool automatic(QScript::Lexer *lexer, int token)
+{
+ return (token == QScriptGrammar::T_RBRACE)
+ || (token == 0)
+ || lexer->prevTerminator();
+}
+
+QScriptParser::QScriptParser():
+ tos(0),
+ stack_size(0),
+ sym_stack(0),
+ state_stack(0),
+ location_stack(0),
+ lexer(0),
+ translator(0)
+{
+}
+
+QScriptParser::~QScriptParser()
+{
+ if (stack_size) {
+ qFree(state_stack);
+ qFree(location_stack);
+ }
+}
+
+static inline QScriptParser::Location location(QScript::Lexer *lexer)
+{
+ QScriptParser::Location loc;
+ loc.startLine = lexer->startLineNo();
+ loc.startColumn = lexer->startColumnNo();
+ loc.endLine = lexer->endLineNo();
+ loc.endColumn = lexer->endColumnNo();
+ return loc;
+}
+
+void QScriptParser::setLexer(QScript::Lexer *lex)
+{
+ lexer = lex;
+}
+
+void QScriptParser::setTranslator(Translator *tor)
+{
+ translator = tor;
+}
+
+bool QScriptParser::parse()
+{
+ Q_ASSERT(lexer != 0);
+ Q_ASSERT(translator != 0);
+ trcontext = QFileInfo(fileName()).baseName();
+
+ const int INITIAL_STATE = 0;
+
+ int yytoken = -1;
+ int saved_yytoken = -1;
+ int identLineNo = -1;
+
+ reallocateStack();
+
+ tos = 0;
+ state_stack[++tos] = INITIAL_STATE;
+
+ while (true)
+ {
+ const int state = state_stack [tos];
+ if (yytoken == -1 && - TERMINAL_COUNT != action_index [state])
+ {
+ if (saved_yytoken == -1)
+ {
+ yytoken = lexer->lex();
+ location_stack [tos] = location(lexer);
+ }
+ else
+ {
+ yytoken = saved_yytoken;
+ saved_yytoken = -1;
+ }
+ }
+
+ int act = t_action (state, yytoken);
+
+ if (act == ACCEPT_STATE)
+ return true;
+
+ else if (act > 0)
+ {
+ if (++tos == stack_size)
+ reallocateStack();
+
+ sym_stack [tos] = lexer->val ();
+ state_stack [tos] = act;
+ location_stack [tos] = location(lexer);
+ yytoken = -1;
+ }
+
+ else if (act < 0)
+ {
+ int r = - act - 1;
+
+ tos -= rhs [r];
+ act = state_stack [tos++];
+
+ switch (r) {
+
+case 1: {
+ sym(1) = sym(1).toByteArray();
+ identLineNo = lexer->startLineNo();
+} break;
+
+case 7: {
+ bool rx = lexer->scanRegExp(QScript::Lexer::NoPrefix);
+ if (!rx) {
+ error_message = lexer->errorMessage();
+ error_lineno = lexer->startLineNo();
+ error_column = lexer->startColumnNo();
+ return false;
+ }
+} break;
+
+case 8: {
+ bool rx = lexer->scanRegExp(QScript::Lexer::EqualPrefix);
+ if (!rx) {
+ error_message = lexer->errorMessage();
+ error_lineno = lexer->startLineNo();
+ error_column = lexer->startColumnNo();
+ return false;
+ }
+} break;
+
+case 66: {
+ QString name = sym(1).toString();
+ if ((name == QLatin1String("qsTranslate")) || (name == QLatin1String("QT_TRANSLATE_NOOP"))) {
+ if (!sourcetext.isEmpty())
+ yyMsg(identLineNo) << qPrintable(LU::tr("//% cannot be used with %1(). Ignoring\n").arg(name));
+ QVariantList args = sym(2).toList();
+ if (args.size() < 2) {
+ yyMsg(identLineNo) << qPrintable(LU::tr("%1() requires at least two arguments.\n").arg(name));
+ } else {
+ if ((args.at(0).type() != QVariant::String)
+ || (args.at(1).type() != QVariant::String)) {
+ yyMsg(identLineNo) << qPrintable(LU::tr("%1(): both arguments must be literal strings.\n").arg(name));
+ } else {
+ QString context = args.at(0).toString();
+ QString text = args.at(1).toString();
+ QString comment = args.value(2).toString();
+ bool plural = (args.size() > 4);
+ recordMessage(translator, context, text, comment, extracomment,
+ msgid, extra, plural, fileName(), identLineNo);
+ }
+ }
+ sourcetext.clear();
+ extracomment.clear();
+ msgid.clear();
+ extra.clear();
+ } else if ((name == QLatin1String("qsTr")) || (name == QLatin1String("QT_TR_NOOP"))) {
+ if (!sourcetext.isEmpty())
+ yyMsg(identLineNo) << qPrintable(LU::tr("//% cannot be used with %1(). Ignoring\n").arg(name));
+ QVariantList args = sym(2).toList();
+ if (args.size() < 1) {
+ yyMsg(identLineNo) << qPrintable(LU::tr("%1() requires at least one argument.\n").arg(name));
+ } else {
+ if (args.at(0).type() != QVariant::String) {
+ yyMsg(identLineNo) << qPrintable(LU::tr("%1(): text to translate must be a literal string.\n").arg(name));
+ } else {
+ QString text = args.at(0).toString();
+ QString comment = args.value(1).toString();
+ bool plural = (args.size() > 2);
+ recordMessage(translator, trcontext, text, comment, extracomment,
+ msgid, extra, plural, fileName(), identLineNo);
+ }
+ }
+ sourcetext.clear();
+ extracomment.clear();
+ msgid.clear();
+ extra.clear();
+ } else if ((name == QLatin1String("qsTrId")) || (name == QLatin1String("QT_TRID_NOOP"))) {
+ if (!msgid.isEmpty())
+ yyMsg(identLineNo) << qPrintable(LU::tr("//= cannot be used with %1(). Ignoring\n").arg(name));
+ QVariantList args = sym(2).toList();
+ if (args.size() < 1) {
+ yyMsg(identLineNo) << qPrintable(LU::tr("%1() requires at least one argument.\n").arg(name));
+ } else {
+ if (args.at(0).type() != QVariant::String) {
+ yyMsg(identLineNo) << qPrintable(LU::tr("%1(): identifier must be a literal string.\n").arg(name));
+ } else {
+ msgid = args.at(0).toString();
+ bool plural = (args.size() > 1);
+ recordMessage(translator, QString(), sourcetext, QString(), extracomment,
+ msgid, extra, plural, fileName(), identLineNo);
+ }
+ }
+ sourcetext.clear();
+ extracomment.clear();
+ msgid.clear();
+ extra.clear();
+ }
+} break;
+
+case 70: {
+ sym(1) = QVariantList();
+} break;
+
+case 71: {
+ sym(1) = sym(2);
+} break;
+
+case 72: {
+ sym(1) = QVariantList() << sym(1);
+} break;
+
+case 73: {
+ sym(1) = sym(1).toList() << sym(3);
+} break;
+
+case 94: {
+ if ((sym(1).type() == QVariant::String) || (sym(3).type() == QVariant::String))
+ sym(1) = sym(1).toString() + sym(3).toString();
+ else
+ sym(1) = QVariant();
+} break;
+
+ case 171:
+
+ case 172:
+
+ case 173:
+
+ case 174:
+
+ case 175:
+
+ case 176:
+
+ case 177:
+
+ case 178:
+
+ case 179:
+
+ case 180:
+
+ case 181:
+
+ case 182:
+
+ case 183:
+
+ case 184:
+
+ case 185:
+ if (!sourcetext.isEmpty() || !extracomment.isEmpty() || !msgid.isEmpty() || !extra.isEmpty()) {
+ yyMsg() << qPrintable(LU::tr("Discarding unconsumed meta data\n"));
+ sourcetext.clear();
+ extracomment.clear();
+ msgid.clear();
+ extra.clear();
+ }
+ break;
+
+ } // switch
+
+ state_stack [tos] = nt_action (act, lhs [r] - TERMINAL_COUNT);
+
+ if (rhs[r] > 1) {
+ location_stack[tos - 1].endLine = location_stack[tos + rhs[r] - 2].endLine;
+ location_stack[tos - 1].endColumn = location_stack[tos + rhs[r] - 2].endColumn;
+ location_stack[tos] = location_stack[tos + rhs[r] - 1];
+ }
+ }
+
+ else
+ {
+ if (saved_yytoken == -1 && automatic (lexer, yytoken) && t_action (state, T_AUTOMATIC_SEMICOLON) > 0)
+ {
+ saved_yytoken = yytoken;
+ yytoken = T_SEMICOLON;
+ continue;
+ }
+
+ else if ((state == INITIAL_STATE) && (yytoken == 0)) {
+ // accept empty input
+ yytoken = T_SEMICOLON;
+ continue;
+ }
+
+ int ers = state;
+ int shifts = 0;
+ int reduces = 0;
+ int expected_tokens [3];
+ for (int tk = 0; tk < TERMINAL_COUNT; ++tk)
+ {
+ int k = t_action (ers, tk);
+
+ if (! k)
+ continue;
+ else if (k < 0)
+ ++reduces;
+ else if (spell [tk])
+ {
+ if (shifts < 3)
+ expected_tokens [shifts] = tk;
+ ++shifts;
+ }
+ }
+
+ error_message.clear ();
+ if (shifts && shifts < 3)
+ {
+ bool first = true;
+
+ for (int s = 0; s < shifts; ++s)
+ {
+ if (first)
+ //: Beginning of the string that contains
+ //: comma-separated list of expected tokens
+ error_message += LU::tr("Expected ");
+ else
+ error_message += QLatin1String (", ");
+
+ first = false;
+ error_message += QLatin1String("`");
+ error_message += QLatin1String (spell [expected_tokens [s]]);
+ error_message += QLatin1String("'");
+ }
+ }
+
+ if (error_message.isEmpty())
+ error_message = lexer->errorMessage();
+
+ error_lineno = lexer->startLineNo();
+ error_column = lexer->startColumnNo();
+
+ return false;
+ }
+ }
+
+ return false;
+}
+
+std::ostream &QScriptParser::yyMsg(int line)
+{
+ return std::cerr << qPrintable(fileName()) << ':' << (line ? line : lexer->startLineNo()) << ": ";
+}
+
+void QScriptParser::processComment(const QChar *chars, int length)
+{
+ if (!length)
+ return;
+ // Try to match the logic of the C++ parser.
+ if (*chars == QLatin1Char(':') && chars[1].isSpace()) {
+ extracomment += QString(chars+2, length-2);
+ } else if (*chars == QLatin1Char('=') && chars[1].isSpace()) {
+ msgid = QString(chars+2, length-2).simplified();
+ } else if (*chars == QLatin1Char('~') && chars[1].isSpace()) {
+ QString text = QString(chars+2, length-2).trimmed();
+ int k = text.indexOf(QLatin1Char(' '));
+ if (k > -1)
+ extra.insert(text.left(k), text.mid(k + 1).trimmed());
+ } else if (*chars == QLatin1Char('%') && chars[1].isSpace()) {
+ sourcetext.reserve(sourcetext.length() + length-2);
+ ushort *ptr = (ushort *)sourcetext.data() + sourcetext.length();
+ int p = 2, c;
+ forever {
+ if (p >= length)
+ break;
+ c = chars[p++].unicode();
+ if (isspace(c))
+ continue;
+ if (c != '"') {
+ yyMsg() << qPrintable(LU::tr("Unexpected character in meta string\n"));
+ break;
+ }
+ forever {
+ if (p >= length) {
+ whoops:
+ yyMsg() << qPrintable(LU::tr("Unterminated meta string\n"));
+ break;
+ }
+ c = chars[p++].unicode();
+ if (c == '"')
+ break;
+ if (c == '\\') {
+ if (p >= length)
+ goto whoops;
+ c = chars[p++].unicode();
+ if (c == '\n')
+ goto whoops;
+ *ptr++ = '\\';
+ }
+ *ptr++ = c;
+ }
+ }
+ sourcetext.resize(ptr - (ushort *)sourcetext.data());
+ } else {
+ int idx = 0;
+ ushort c;
+ while ((c = chars[idx].unicode()) == ' ' || c == '\t' || c == '\n')
+ ++idx;
+ if (!memcmp(chars + idx, MagicComment.unicode(), MagicComment.length() * 2)) {
+ idx += MagicComment.length();
+ QString comment = QString(chars + idx, length - idx).simplified();
+ int k = comment.indexOf(QLatin1Char(' '));
+ if (k == -1) {
+ trcontext = comment;
+ } else {
+ trcontext = comment.left(k);
+ comment.remove(0, k + 1);
+ TranslatorMessage msg(
+ trcontext, QString(),
+ comment, QString(),
+ fileName(), lexer->startLineNo(), QStringList(),
+ TranslatorMessage::Finished, /*plural=*/false);
+ msg.setExtraComment(extracomment.simplified());
+ extracomment.clear();
+ translator->append(msg);
+ translator->setExtras(extra);
+ extra.clear();
+ }
+ }
+ }
+}
+
+
+bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd)
+{
+ QFile file(filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ cd.appendError(LU::tr("Cannot open %1: %2").arg(filename, file.errorString()));
+ return false;
+ }
+ QTextStream ts(&file);
+ QByteArray codecName;
+ if (!cd.m_codecForSource.isEmpty())
+ codecName = cd.m_codecForSource;
+ else
+ codecName = translator.codecName(); // Just because it should be latin1 already
+ ts.setCodec(QTextCodec::codecForName(codecName));
+ ts.setAutoDetectUnicode(true);
+
+ QString code = ts.readAll();
+ QScriptParser parser;
+ QScript::Lexer lexer(&parser);
+ lexer.setCode(code, filename, /*lineNumber=*/1);
+ parser.setLexer(&lexer);
+ parser.setTranslator(&translator);
+ if (!parser.parse()) {
+ std::cerr << qPrintable(filename) << ':' << parser.errorLineNumber() << ": "
+ << qPrintable(parser.errorMessage()) << std::endl;
+ return false;
+ }
+
+ // Java uses UTF-16 internally and Jambi makes UTF-8 for tr() purposes of it.
+ translator.setCodecName("UTF-8");
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/lupdate/qscript.g b/src/linguist/lupdate/qscript.g
new file mode 100644
index 000000000..b8b7b5f19
--- /dev/null
+++ b/src/linguist/lupdate/qscript.g
@@ -0,0 +1,2261 @@
+----------------------------------------------------------------------------
+--
+-- 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$
+--
+----------------------------------------------------------------------------
+
+--------------------------------------------------------------------------------
+-- Process with "qlalr --no-debug --no-lines qscript.g" to update qscript.cpp --
+--------------------------------------------------------------------------------
+
+%parser QScriptGrammar
+%merged_output qscript.cpp
+%expect 3
+%expect-rr 1
+
+%token T_AND "&" T_AND_AND "&&" T_AND_EQ "&="
+%token T_BREAK "break" T_CASE "case" T_CATCH "catch"
+%token T_COLON ":" T_COMMA ";" T_CONTINUE "continue"
+%token T_DEFAULT "default" T_DELETE "delete" T_DIVIDE_ "/"
+%token T_DIVIDE_EQ "/=" T_DO "do" T_DOT "."
+%token T_ELSE "else" T_EQ "=" T_EQ_EQ "=="
+%token T_EQ_EQ_EQ "===" T_FINALLY "finally" T_FOR "for"
+%token T_FUNCTION "function" T_GE ">=" T_GT ">"
+%token T_GT_GT ">>" T_GT_GT_EQ ">>=" T_GT_GT_GT ">>>"
+%token T_GT_GT_GT_EQ ">>>=" T_IDENTIFIER "identifier" T_IF "if"
+%token T_IN "in" T_INSTANCEOF "instanceof" T_LBRACE "{"
+%token T_LBRACKET "[" T_LE "<=" T_LPAREN "("
+%token T_LT "<" T_LT_LT "<<" T_LT_LT_EQ "<<="
+%token T_MINUS "-" T_MINUS_EQ "-=" T_MINUS_MINUS "--"
+%token T_NEW "new" T_NOT "!" T_NOT_EQ "!="
+%token T_NOT_EQ_EQ "!==" T_NUMERIC_LITERAL "numeric literal" T_OR "|"
+%token T_OR_EQ "|=" T_OR_OR "||" T_PLUS "+"
+%token T_PLUS_EQ "+=" T_PLUS_PLUS "++" T_QUESTION "?"
+%token T_RBRACE "}" T_RBRACKET "]" T_REMAINDER "%"
+%token T_REMAINDER_EQ "%=" T_RETURN "return" T_RPAREN ")"
+%token T_SEMICOLON ";" T_AUTOMATIC_SEMICOLON T_STAR "*"
+%token T_STAR_EQ "*=" T_STRING_LITERAL "string literal"
+%token T_SWITCH "switch" T_THIS "this" T_THROW "throw"
+%token T_TILDE "~" T_TRY "try" T_TYPEOF "typeof"
+%token T_VAR "var" T_VOID "void" T_WHILE "while"
+%token T_WITH "with" T_XOR "^" T_XOR_EQ "^="
+%token T_NULL "null" T_TRUE "true" T_FALSE "false"
+%token T_CONST "const"
+%token T_DEBUGGER "debugger"
+%token T_RESERVED_WORD "reserved word"
+
+%start Program
+
+/.
+#include <translator.h>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/qdebug.h>
+#include <QtCore/qnumeric.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qtextcodec.h>
+#include <QtCore/qvariant.h>
+
+#include <iostream>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+class LU {
+ Q_DECLARE_TR_FUNCTIONS(LUpdate)
+};
+
+static QString MagicComment(QLatin1String("TRANSLATOR"));
+
+static void recordMessage(
+ Translator *tor, const QString &context, const QString &text, const QString &comment,
+ const QString &extracomment, const QString &msgid, const TranslatorMessage::ExtraData &extra,
+ bool plural, const QString &fileName, int lineNo)
+{
+ TranslatorMessage msg(
+ context, text, comment, QString(),
+ fileName, lineNo, QStringList(),
+ TranslatorMessage::Unfinished, plural);
+ msg.setExtraComment(extracomment.simplified());
+ msg.setId(msgid);
+ msg.setExtras(extra);
+ tor->extend(msg);
+}
+
+
+namespace QScript
+{
+
+class CommentProcessor
+{
+public:
+ virtual ~CommentProcessor() {}
+ virtual void processComment(const QChar *chars, int length) = 0;
+};
+
+class Lexer
+{
+public:
+ Lexer(CommentProcessor *);
+ ~Lexer();
+
+ void setCode(const QString &c, const QString &fileName, int lineno);
+ int lex();
+
+ QString fileName() const { return yyfilename; }
+ int currentLineNo() const { return yylineno; }
+ int currentColumnNo() const { return yycolumn; }
+
+ int startLineNo() const { return startlineno; }
+ int startColumnNo() const { return startcolumn; }
+
+ int endLineNo() const { return currentLineNo(); }
+ int endColumnNo() const
+ { int col = currentColumnNo(); return (col > 0) ? col - 1 : col; }
+
+ bool prevTerminator() const { return terminator; }
+
+ enum State { Start,
+ Identifier,
+ InIdentifier,
+ InSingleLineComment,
+ InMultiLineComment,
+ InNum,
+ InNum0,
+ InHex,
+ InOctal,
+ InDecimal,
+ InExponentIndicator,
+ InExponent,
+ Hex,
+ Octal,
+ Number,
+ String,
+ Eof,
+ InString,
+ InEscapeSequence,
+ InHexEscape,
+ InUnicodeEscape,
+ Other,
+ Bad };
+
+ enum Error {
+ NoError,
+ IllegalCharacter,
+ UnclosedStringLiteral,
+ IllegalEscapeSequence,
+ IllegalUnicodeEscapeSequence,
+ UnclosedComment,
+ IllegalExponentIndicator,
+ IllegalIdentifier
+ };
+
+ enum ParenthesesState {
+ IgnoreParentheses,
+ CountParentheses,
+ BalancedParentheses
+ };
+
+ enum RegExpBodyPrefix {
+ NoPrefix,
+ EqualPrefix
+ };
+
+ bool scanRegExp(RegExpBodyPrefix prefix = NoPrefix);
+
+ QString pattern;
+ int flags;
+
+ State lexerState() const
+ { return state; }
+
+ QString errorMessage() const
+ { return errmsg; }
+ void setErrorMessage(const QString &err)
+ { errmsg = err; }
+ void setErrorMessage(const char *err)
+ { setErrorMessage(QString::fromLatin1(err)); }
+
+ Error error() const
+ { return err; }
+ void clearError()
+ { err = NoError; }
+
+private:
+ QString yyfilename;
+ int yylineno;
+ bool done;
+ char *buffer8;
+ QChar *buffer16;
+ uint size8, size16;
+ uint pos8, pos16;
+ bool terminator;
+ bool restrKeyword;
+ // encountered delimiter like "'" and "}" on last run
+ bool delimited;
+ int stackToken;
+
+ State state;
+ void setDone(State s);
+ uint pos;
+ void shift(uint p);
+ int lookupKeyword(const char *);
+
+ bool isWhiteSpace() const;
+ bool isLineTerminator() const;
+ bool isHexDigit(ushort c) const;
+ bool isOctalDigit(ushort c) const;
+
+ int matchPunctuator(ushort c1, ushort c2,
+ ushort c3, ushort c4);
+ ushort singleEscape(ushort c) const;
+ ushort convertOctal(ushort c1, ushort c2,
+ ushort c3) const;
+public:
+ static unsigned char convertHex(ushort c1);
+ static unsigned char convertHex(ushort c1, ushort c2);
+ static QChar convertUnicode(ushort c1, ushort c2,
+ ushort c3, ushort c4);
+ static bool isIdentLetter(ushort c);
+ static bool isDecimalDigit(ushort c);
+
+ inline int ival() const { return qsyylval.toInt(); }
+ inline double dval() const { return qsyylval.toDouble(); }
+ inline QString ustr() const { return qsyylval.toString(); }
+ inline QVariant val() const { return qsyylval; }
+
+ const QChar *characterBuffer() const { return buffer16; }
+ int characterCount() const { return pos16; }
+
+private:
+ void record8(ushort c);
+ void record16(QChar c);
+ void recordStartPos();
+
+ int findReservedWord(const QChar *buffer, int size) const;
+
+ void syncProhibitAutomaticSemicolon();
+
+ void processComment(const QChar *, int);
+
+ const QChar *code;
+ uint length;
+ int yycolumn;
+ int startlineno;
+ int startcolumn;
+ int bol; // begin of line
+
+ QVariant qsyylval;
+
+ // current and following unicode characters
+ ushort current, next1, next2, next3;
+
+ struct keyword {
+ const char *name;
+ int token;
+ };
+
+ QString errmsg;
+ Error err;
+
+ bool wantRx;
+ bool check_reserved;
+
+ ParenthesesState parenthesesState;
+ int parenthesesCount;
+ bool prohibitAutomaticSemicolon;
+
+ CommentProcessor *commentProcessor;
+};
+
+} // namespace QScript
+
+extern double qstrtod(const char *s00, char const **se, bool *ok);
+
+#define shiftWindowsLineBreak() if(current == '\r' && next1 == '\n') shift(1);
+
+namespace QScript {
+
+static int toDigit(char c)
+{
+ if ((c >= '0') && (c <= '9'))
+ return c - '0';
+ else if ((c >= 'a') && (c <= 'z'))
+ return 10 + c - 'a';
+ else if ((c >= 'A') && (c <= 'Z'))
+ return 10 + c - 'A';
+ return -1;
+}
+
+double integerFromString(const char *buf, int size, int radix)
+{
+ if (size == 0)
+ return qSNaN();
+
+ double sign = 1.0;
+ int i = 0;
+ if (buf[0] == '+') {
+ ++i;
+ } else if (buf[0] == '-') {
+ sign = -1.0;
+ ++i;
+ }
+
+ if (((size-i) >= 2) && (buf[i] == '0')) {
+ if (((buf[i+1] == 'x') || (buf[i+1] == 'X'))
+ && (radix < 34)) {
+ if ((radix != 0) && (radix != 16))
+ return 0;
+ radix = 16;
+ i += 2;
+ } else {
+ if (radix == 0) {
+ radix = 8;
+ ++i;
+ }
+ }
+ } else if (radix == 0) {
+ radix = 10;
+ }
+
+ int j = i;
+ for ( ; i < size; ++i) {
+ int d = toDigit(buf[i]);
+ if ((d == -1) || (d >= radix))
+ break;
+ }
+ double result;
+ if (j == i) {
+ if (!qstrcmp(buf, "Infinity"))
+ result = qInf();
+ else
+ result = qSNaN();
+ } else {
+ result = 0;
+ double multiplier = 1;
+ for (--i ; i >= j; --i, multiplier *= radix)
+ result += toDigit(buf[i]) * multiplier;
+ }
+ result *= sign;
+ return result;
+}
+
+} // namespace QScript
+
+QScript::Lexer::Lexer(QScript::CommentProcessor *proc)
+ :
+ yylineno(0),
+ size8(128), size16(128), restrKeyword(false),
+ stackToken(-1), pos(0),
+ code(0), length(0),
+ bol(true),
+ current(0), next1(0), next2(0), next3(0),
+ err(NoError),
+ check_reserved(true),
+ parenthesesState(IgnoreParentheses),
+ prohibitAutomaticSemicolon(false),
+ commentProcessor(proc)
+{
+ // allocate space for read buffers
+ buffer8 = new char[size8];
+ buffer16 = new QChar[size16];
+ flags = 0;
+
+}
+
+QScript::Lexer::~Lexer()
+{
+ delete [] buffer8;
+ delete [] buffer16;
+}
+
+void QScript::Lexer::setCode(const QString &c, const QString &fileName, int lineno)
+{
+ errmsg = QString();
+ yyfilename = fileName;
+ yylineno = lineno;
+ yycolumn = 1;
+ restrKeyword = false;
+ delimited = false;
+ stackToken = -1;
+ pos = 0;
+ code = c.unicode();
+ length = c.length();
+ bol = true;
+
+ // read first characters
+ current = (length > 0) ? code[0].unicode() : 0;
+ next1 = (length > 1) ? code[1].unicode() : 0;
+ next2 = (length > 2) ? code[2].unicode() : 0;
+ next3 = (length > 3) ? code[3].unicode() : 0;
+}
+
+void QScript::Lexer::shift(uint p)
+{
+ while (p--) {
+ ++pos;
+ ++yycolumn;
+ current = next1;
+ next1 = next2;
+ next2 = next3;
+ next3 = (pos + 3 < length) ? code[pos+3].unicode() : 0;
+ }
+}
+
+void QScript::Lexer::setDone(State s)
+{
+ state = s;
+ done = true;
+}
+
+int QScript::Lexer::findReservedWord(const QChar *c, int size) const
+{
+ switch (size) {
+ case 2: {
+ if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('o'))
+ return QScriptGrammar::T_DO;
+ else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('f'))
+ return QScriptGrammar::T_IF;
+ else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n'))
+ return QScriptGrammar::T_IN;
+ } break;
+
+ case 3: {
+ if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('o') && c[2] == QLatin1Char('r'))
+ return QScriptGrammar::T_FOR;
+ else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('e') && c[2] == QLatin1Char('w'))
+ return QScriptGrammar::T_NEW;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') && c[2] == QLatin1Char('y'))
+ return QScriptGrammar::T_TRY;
+ else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('a') && c[2] == QLatin1Char('r'))
+ return QScriptGrammar::T_VAR;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') && c[2] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 4: {
+ if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e'))
+ return QScriptGrammar::T_CASE;
+ else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('l')
+ && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e'))
+ return QScriptGrammar::T_ELSE;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('s'))
+ return QScriptGrammar::T_THIS;
+ else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('d'))
+ return QScriptGrammar::T_VOID;
+ else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('i')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('h'))
+ return QScriptGrammar::T_WITH;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('e'))
+ return QScriptGrammar::T_TRUE;
+ else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('u')
+ && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('l'))
+ return QScriptGrammar::T_NULL;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('n')
+ && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('m'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('y')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('l') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('g'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('r'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('g') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('o'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 5: {
+ if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('e') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('k'))
+ return QScriptGrammar::T_BREAK;
+ else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('c')
+ && c[4] == QLatin1Char('h'))
+ return QScriptGrammar::T_CATCH;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('r') && c[3] == QLatin1Char('o')
+ && c[4] == QLatin1Char('w'))
+ return QScriptGrammar::T_THROW;
+ else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('l')
+ && c[4] == QLatin1Char('e'))
+ return QScriptGrammar::T_WHILE;
+ else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('s')
+ && c[4] == QLatin1Char('t'))
+ return QScriptGrammar::T_CONST;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('s')
+ && c[4] == QLatin1Char('e'))
+ return QScriptGrammar::T_FALSE;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('r')
+ && c[4] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('u')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('r'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('i')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('l'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('l')
+ && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('s')
+ && c[4] == QLatin1Char('s'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('l')
+ && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 6: {
+ if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e')
+ && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('e'))
+ return QScriptGrammar::T_DELETE;
+ else if (c[0] == QLatin1Char('r') && c[1] == QLatin1Char('e')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('u')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('n'))
+ return QScriptGrammar::T_RETURN;
+ else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('w')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('c') && c[5] == QLatin1Char('h'))
+ return QScriptGrammar::T_SWITCH;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('y')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('o') && c[5] == QLatin1Char('f'))
+ return QScriptGrammar::T_TYPEOF;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('x')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('t')
+ && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('c'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('b')
+ && c[4] == QLatin1Char('l') && c[5] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('u')
+ && c[2] == QLatin1Char('b') && c[3] == QLatin1Char('l')
+ && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('c'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('i')
+ && c[4] == QLatin1Char('v') && c[5] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h')
+ && c[2] == QLatin1Char('r') && c[3] == QLatin1Char('o')
+ && c[4] == QLatin1Char('w') && c[5] == QLatin1Char('s'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 7: {
+ if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e')
+ && c[2] == QLatin1Char('f') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('u') && c[5] == QLatin1Char('l')
+ && c[6] == QLatin1Char('t'))
+ return QScriptGrammar::T_DEFAULT;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('i')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('l') && c[5] == QLatin1Char('l')
+ && c[6] == QLatin1Char('y'))
+ return QScriptGrammar::T_FINALLY;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('l')
+ && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('a')
+ && c[6] == QLatin1Char('n'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('x')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('n') && c[5] == QLatin1Char('d')
+ && c[6] == QLatin1Char('s'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('a')
+ && c[2] == QLatin1Char('c') && c[3] == QLatin1Char('k')
+ && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('g')
+ && c[6] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('v')
+ && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('t')
+ && c[6] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 8: {
+ if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('n')
+ && c[6] == QLatin1Char('u') && c[7] == QLatin1Char('e'))
+ return QScriptGrammar::T_CONTINUE;
+ else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('u')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('c')
+ && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('i')
+ && c[6] == QLatin1Char('o') && c[7] == QLatin1Char('n'))
+ return QScriptGrammar::T_FUNCTION;
+ else if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e')
+ && c[2] == QLatin1Char('b') && c[3] == QLatin1Char('u')
+ && c[4] == QLatin1Char('g') && c[5] == QLatin1Char('g')
+ && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('r'))
+ return QScriptGrammar::T_DEBUGGER;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('a') && c[1] == QLatin1Char('b')
+ && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('a')
+ && c[6] == QLatin1Char('c') && c[7] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o')
+ && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('a')
+ && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('i')
+ && c[6] == QLatin1Char('l') && c[7] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 9: {
+ if (check_reserved) {
+ if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n')
+ && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e')
+ && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('f')
+ && c[6] == QLatin1Char('a') && c[7] == QLatin1Char('c')
+ && c[8] == QLatin1Char('e'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('n')
+ && c[4] == QLatin1Char('s') && c[5] == QLatin1Char('i')
+ && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('n')
+ && c[8] == QLatin1Char('t'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r')
+ && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('c')
+ && c[6] == QLatin1Char('t') && c[7] == QLatin1Char('e')
+ && c[8] == QLatin1Char('d'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 10: {
+ if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n')
+ && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('t')
+ && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('n')
+ && c[6] == QLatin1Char('c') && c[7] == QLatin1Char('e')
+ && c[8] == QLatin1Char('o') && c[9] == QLatin1Char('f'))
+ return QScriptGrammar::T_INSTANCEOF;
+ else if (check_reserved) {
+ if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m')
+ && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('l')
+ && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('m')
+ && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('n')
+ && c[8] == QLatin1Char('t') && c[9] == QLatin1Char('s'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ case 12: {
+ if (check_reserved) {
+ if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('y')
+ && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('c')
+ && c[4] == QLatin1Char('h') && c[5] == QLatin1Char('r')
+ && c[6] == QLatin1Char('o') && c[7] == QLatin1Char('n')
+ && c[8] == QLatin1Char('i') && c[9] == QLatin1Char('z')
+ && c[10] == QLatin1Char('e') && c[11] == QLatin1Char('d'))
+ return QScriptGrammar::T_RESERVED_WORD;
+ }
+ } break;
+
+ } // switch
+
+ return -1;
+}
+
+int QScript::Lexer::lex()
+{
+ int token = 0;
+ state = Start;
+ ushort stringType = 0; // either single or double quotes
+ pos8 = pos16 = 0;
+ done = false;
+ terminator = false;
+
+ // did we push a token on the stack previously ?
+ // (after an automatic semicolon insertion)
+ if (stackToken >= 0) {
+ setDone(Other);
+ token = stackToken;
+ stackToken = -1;
+ }
+
+ while (!done) {
+ switch (state) {
+ case Start:
+ if (isWhiteSpace()) {
+ // do nothing
+ } else if (current == '/' && next1 == '/') {
+ recordStartPos();
+ shift(1);
+ Q_ASSERT(pos16 == 0);
+ state = InSingleLineComment;
+ } else if (current == '/' && next1 == '*') {
+ recordStartPos();
+ shift(1);
+ Q_ASSERT(pos16 == 0);
+ state = InMultiLineComment;
+ } else if (current == 0) {
+ syncProhibitAutomaticSemicolon();
+ if (!terminator && !delimited && !prohibitAutomaticSemicolon) {
+ // automatic semicolon insertion if program incomplete
+ token = QScriptGrammar::T_SEMICOLON;
+ stackToken = 0;
+ setDone(Other);
+ } else {
+ setDone(Eof);
+ }
+ } else if (isLineTerminator()) {
+ shiftWindowsLineBreak();
+ yylineno++;
+ yycolumn = 0;
+ bol = true;
+ terminator = true;
+ syncProhibitAutomaticSemicolon();
+ if (restrKeyword) {
+ token = QScriptGrammar::T_SEMICOLON;
+ setDone(Other);
+ }
+ } else if (current == '"' || current == '\'') {
+ recordStartPos();
+ state = InString;
+ stringType = current;
+ } else if (isIdentLetter(current)) {
+ recordStartPos();
+ record16(current);
+ state = InIdentifier;
+ } else if (current == '0') {
+ recordStartPos();
+ record8(current);
+ state = InNum0;
+ } else if (isDecimalDigit(current)) {
+ recordStartPos();
+ record8(current);
+ state = InNum;
+ } else if (current == '.' && isDecimalDigit(next1)) {
+ recordStartPos();
+ record8(current);
+ state = InDecimal;
+ } else {
+ recordStartPos();
+ token = matchPunctuator(current, next1, next2, next3);
+ if (token != -1) {
+ if (terminator && !delimited && !prohibitAutomaticSemicolon
+ && (token == QScriptGrammar::T_PLUS_PLUS
+ || token == QScriptGrammar::T_MINUS_MINUS)) {
+ // automatic semicolon insertion
+ stackToken = token;
+ token = QScriptGrammar::T_SEMICOLON;
+ }
+ setDone(Other);
+ }
+ else {
+ setDone(Bad);
+ err = IllegalCharacter;
+ errmsg = LU::tr("Illegal character");
+ }
+ }
+ break;
+ case InString:
+ if (current == stringType) {
+ shift(1);
+ setDone(String);
+ } else if (current == 0 || isLineTerminator()) {
+ setDone(Bad);
+ err = UnclosedStringLiteral;
+ errmsg = LU::tr("Unclosed string at end of line");
+ } else if (current == '\\') {
+ state = InEscapeSequence;
+ } else {
+ record16(current);
+ }
+ break;
+ // Escape Sequences inside of strings
+ case InEscapeSequence:
+ if (isOctalDigit(current)) {
+ if (current >= '0' && current <= '3' &&
+ isOctalDigit(next1) && isOctalDigit(next2)) {
+ record16(convertOctal(current, next1, next2));
+ shift(2);
+ state = InString;
+ } else if (isOctalDigit(current) &&
+ isOctalDigit(next1)) {
+ record16(convertOctal('0', current, next1));
+ shift(1);
+ state = InString;
+ } else if (isOctalDigit(current)) {
+ record16(convertOctal('0', '0', current));
+ state = InString;
+ } else {
+ setDone(Bad);
+ err = IllegalEscapeSequence;
+ errmsg = LU::tr("Illegal escape sequence");
+ }
+ } else if (current == 'x')
+ state = InHexEscape;
+ else if (current == 'u')
+ state = InUnicodeEscape;
+ else {
+ record16(singleEscape(current));
+ state = InString;
+ }
+ break;
+ case InHexEscape:
+ if (isHexDigit(current) && isHexDigit(next1)) {
+ state = InString;
+ record16(QLatin1Char(convertHex(current, next1)));
+ shift(1);
+ } else if (current == stringType) {
+ record16(QLatin1Char('x'));
+ shift(1);
+ setDone(String);
+ } else {
+ record16(QLatin1Char('x'));
+ record16(current);
+ state = InString;
+ }
+ break;
+ case InUnicodeEscape:
+ if (isHexDigit(current) && isHexDigit(next1) &&
+ isHexDigit(next2) && isHexDigit(next3)) {
+ record16(convertUnicode(current, next1, next2, next3));
+ shift(3);
+ state = InString;
+ } else if (current == stringType) {
+ record16(QLatin1Char('u'));
+ shift(1);
+ setDone(String);
+ } else {
+ setDone(Bad);
+ err = IllegalUnicodeEscapeSequence;
+ errmsg = LU::tr("Illegal unicode escape sequence");
+ }
+ break;
+ case InSingleLineComment:
+ if (isLineTerminator()) {
+ record16(current); // include newline
+ processComment(buffer16, pos16);
+ shiftWindowsLineBreak();
+ yylineno++;
+ yycolumn = 0;
+ pos16 = 0;
+ terminator = true;
+ bol = true;
+ if (restrKeyword) {
+ token = QScriptGrammar::T_SEMICOLON;
+ setDone(Other);
+ } else
+ state = Start;
+ } else if (current == 0) {
+ setDone(Eof);
+ } else {
+ record16(current);
+ }
+ break;
+ case InMultiLineComment:
+ if (current == 0) {
+ setDone(Bad);
+ err = UnclosedComment;
+ errmsg = LU::tr("Unclosed comment at end of file");
+ } else if (isLineTerminator()) {
+ shiftWindowsLineBreak();
+ yylineno++;
+ } else if (current == '*' && next1 == '/') {
+ processComment(buffer16, pos16);
+ pos16 = 0;
+ state = Start;
+ shift(1);
+ } else {
+ record16(current);
+ }
+ break;
+ case InIdentifier:
+ if (isIdentLetter(current) || isDecimalDigit(current)) {
+ record16(current);
+ break;
+ }
+ setDone(Identifier);
+ break;
+ case InNum0:
+ if (current == 'x' || current == 'X') {
+ record8(current);
+ state = InHex;
+ } else if (current == '.') {
+ record8(current);
+ state = InDecimal;
+ } else if (current == 'e' || current == 'E') {
+ record8(current);
+ state = InExponentIndicator;
+ } else if (isOctalDigit(current)) {
+ record8(current);
+ state = InOctal;
+ } else if (isDecimalDigit(current)) {
+ record8(current);
+ state = InDecimal;
+ } else {
+ setDone(Number);
+ }
+ break;
+ case InHex:
+ if (isHexDigit(current))
+ record8(current);
+ else
+ setDone(Hex);
+ break;
+ case InOctal:
+ if (isOctalDigit(current)) {
+ record8(current);
+ } else if (isDecimalDigit(current)) {
+ record8(current);
+ state = InDecimal;
+ } else {
+ setDone(Octal);
+ }
+ break;
+ case InNum:
+ if (isDecimalDigit(current)) {
+ record8(current);
+ } else if (current == '.') {
+ record8(current);
+ state = InDecimal;
+ } else if (current == 'e' || current == 'E') {
+ record8(current);
+ state = InExponentIndicator;
+ } else {
+ setDone(Number);
+ }
+ break;
+ case InDecimal:
+ if (isDecimalDigit(current)) {
+ record8(current);
+ } else if (current == 'e' || current == 'E') {
+ record8(current);
+ state = InExponentIndicator;
+ } else {
+ setDone(Number);
+ }
+ break;
+ case InExponentIndicator:
+ if (current == '+' || current == '-') {
+ record8(current);
+ } else if (isDecimalDigit(current)) {
+ record8(current);
+ state = InExponent;
+ } else {
+ setDone(Bad);
+ err = IllegalExponentIndicator;
+ errmsg = LU::tr("Illegal syntax for exponential number");
+ }
+ break;
+ case InExponent:
+ if (isDecimalDigit(current)) {
+ record8(current);
+ } else {
+ setDone(Number);
+ }
+ break;
+ default:
+ Q_ASSERT_X(0, "Lexer::lex", "Unhandled state in switch statement");
+ }
+
+ // move on to the next character
+ if (!done)
+ shift(1);
+ if (state != Start && state != InSingleLineComment)
+ bol = false;
+ }
+
+ // no identifiers allowed directly after numeric literal, e.g. "3in" is bad
+ if ((state == Number || state == Octal || state == Hex)
+ && isIdentLetter(current)) {
+ state = Bad;
+ err = IllegalIdentifier;
+ errmsg = LU::tr("Identifier cannot start with numeric literal");
+ }
+
+ // terminate string
+ buffer8[pos8] = '\0';
+
+ double dval = 0;
+ if (state == Number) {
+ dval = qstrtod(buffer8, 0, 0);
+ } else if (state == Hex) { // scan hex numbers
+ dval = QScript::integerFromString(buffer8, pos8, 16);
+ state = Number;
+ } else if (state == Octal) { // scan octal number
+ dval = QScript::integerFromString(buffer8, pos8, 8);
+ state = Number;
+ }
+
+ restrKeyword = false;
+ delimited = false;
+
+ switch (parenthesesState) {
+ case IgnoreParentheses:
+ break;
+ case CountParentheses:
+ if (token == QScriptGrammar::T_RPAREN) {
+ --parenthesesCount;
+ if (parenthesesCount == 0)
+ parenthesesState = BalancedParentheses;
+ } else if (token == QScriptGrammar::T_LPAREN) {
+ ++parenthesesCount;
+ }
+ break;
+ case BalancedParentheses:
+ parenthesesState = IgnoreParentheses;
+ break;
+ }
+
+ switch (state) {
+ case Eof:
+ return 0;
+ case Other:
+ if(token == QScriptGrammar::T_RBRACE || token == QScriptGrammar::T_SEMICOLON)
+ delimited = true;
+ return token;
+ case Identifier:
+ if ((token = findReservedWord(buffer16, pos16)) < 0) {
+ /* TODO: close leak on parse error. same holds true for String */
+ qsyylval = QString(buffer16, pos16);
+ return QScriptGrammar::T_IDENTIFIER;
+ }
+ if (token == QScriptGrammar::T_CONTINUE || token == QScriptGrammar::T_BREAK
+ || token == QScriptGrammar::T_RETURN || token == QScriptGrammar::T_THROW) {
+ restrKeyword = true;
+ } else if (token == QScriptGrammar::T_IF || token == QScriptGrammar::T_FOR
+ || token == QScriptGrammar::T_WHILE || token == QScriptGrammar::T_WITH) {
+ parenthesesState = CountParentheses;
+ parenthesesCount = 0;
+ } else if (token == QScriptGrammar::T_DO) {
+ parenthesesState = BalancedParentheses;
+ }
+ return token;
+ case String:
+ qsyylval = QString(buffer16, pos16);
+ return QScriptGrammar::T_STRING_LITERAL;
+ case Number:
+ qsyylval = dval;
+ return QScriptGrammar::T_NUMERIC_LITERAL;
+ case Bad:
+ return -1;
+ default:
+ Q_ASSERT(!"unhandled numeration value in switch");
+ return -1;
+ }
+}
+
+bool QScript::Lexer::isWhiteSpace() const
+{
+ return (current == ' ' || current == '\t' ||
+ current == 0x0b || current == 0x0c);
+}
+
+bool QScript::Lexer::isLineTerminator() const
+{
+ return (current == '\n' || current == '\r');
+}
+
+bool QScript::Lexer::isIdentLetter(ushort c)
+{
+ /* TODO: allow other legitimate unicode chars */
+ return ((c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || c == '$'
+ || c == '_');
+}
+
+bool QScript::Lexer::isDecimalDigit(ushort c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+bool QScript::Lexer::isHexDigit(ushort c) const
+{
+ return ((c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'f')
+ || (c >= 'A' && c <= 'F'));
+}
+
+bool QScript::Lexer::isOctalDigit(ushort c) const
+{
+ return (c >= '0' && c <= '7');
+}
+
+int QScript::Lexer::matchPunctuator(ushort c1, ushort c2,
+ ushort c3, ushort c4)
+{
+ if (c1 == '>' && c2 == '>' && c3 == '>' && c4 == '=') {
+ shift(4);
+ return QScriptGrammar::T_GT_GT_GT_EQ;
+ } else if (c1 == '=' && c2 == '=' && c3 == '=') {
+ shift(3);
+ return QScriptGrammar::T_EQ_EQ_EQ;
+ } else if (c1 == '!' && c2 == '=' && c3 == '=') {
+ shift(3);
+ return QScriptGrammar::T_NOT_EQ_EQ;
+ } else if (c1 == '>' && c2 == '>' && c3 == '>') {
+ shift(3);
+ return QScriptGrammar::T_GT_GT_GT;
+ } else if (c1 == '<' && c2 == '<' && c3 == '=') {
+ shift(3);
+ return QScriptGrammar::T_LT_LT_EQ;
+ } else if (c1 == '>' && c2 == '>' && c3 == '=') {
+ shift(3);
+ return QScriptGrammar::T_GT_GT_EQ;
+ } else if (c1 == '<' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_LE;
+ } else if (c1 == '>' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_GE;
+ } else if (c1 == '!' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_NOT_EQ;
+ } else if (c1 == '+' && c2 == '+') {
+ shift(2);
+ return QScriptGrammar::T_PLUS_PLUS;
+ } else if (c1 == '-' && c2 == '-') {
+ shift(2);
+ return QScriptGrammar::T_MINUS_MINUS;
+ } else if (c1 == '=' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_EQ_EQ;
+ } else if (c1 == '+' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_PLUS_EQ;
+ } else if (c1 == '-' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_MINUS_EQ;
+ } else if (c1 == '*' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_STAR_EQ;
+ } else if (c1 == '/' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_DIVIDE_EQ;
+ } else if (c1 == '&' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_AND_EQ;
+ } else if (c1 == '^' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_XOR_EQ;
+ } else if (c1 == '%' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_REMAINDER_EQ;
+ } else if (c1 == '|' && c2 == '=') {
+ shift(2);
+ return QScriptGrammar::T_OR_EQ;
+ } else if (c1 == '<' && c2 == '<') {
+ shift(2);
+ return QScriptGrammar::T_LT_LT;
+ } else if (c1 == '>' && c2 == '>') {
+ shift(2);
+ return QScriptGrammar::T_GT_GT;
+ } else if (c1 == '&' && c2 == '&') {
+ shift(2);
+ return QScriptGrammar::T_AND_AND;
+ } else if (c1 == '|' && c2 == '|') {
+ shift(2);
+ return QScriptGrammar::T_OR_OR;
+ }
+
+ switch(c1) {
+ case '=': shift(1); return QScriptGrammar::T_EQ;
+ case '>': shift(1); return QScriptGrammar::T_GT;
+ case '<': shift(1); return QScriptGrammar::T_LT;
+ case ',': shift(1); return QScriptGrammar::T_COMMA;
+ case '!': shift(1); return QScriptGrammar::T_NOT;
+ case '~': shift(1); return QScriptGrammar::T_TILDE;
+ case '?': shift(1); return QScriptGrammar::T_QUESTION;
+ case ':': shift(1); return QScriptGrammar::T_COLON;
+ case '.': shift(1); return QScriptGrammar::T_DOT;
+ case '+': shift(1); return QScriptGrammar::T_PLUS;
+ case '-': shift(1); return QScriptGrammar::T_MINUS;
+ case '*': shift(1); return QScriptGrammar::T_STAR;
+ case '/': shift(1); return QScriptGrammar::T_DIVIDE_;
+ case '&': shift(1); return QScriptGrammar::T_AND;
+ case '|': shift(1); return QScriptGrammar::T_OR;
+ case '^': shift(1); return QScriptGrammar::T_XOR;
+ case '%': shift(1); return QScriptGrammar::T_REMAINDER;
+ case '(': shift(1); return QScriptGrammar::T_LPAREN;
+ case ')': shift(1); return QScriptGrammar::T_RPAREN;
+ case '{': shift(1); return QScriptGrammar::T_LBRACE;
+ case '}': shift(1); return QScriptGrammar::T_RBRACE;
+ case '[': shift(1); return QScriptGrammar::T_LBRACKET;
+ case ']': shift(1); return QScriptGrammar::T_RBRACKET;
+ case ';': shift(1); return QScriptGrammar::T_SEMICOLON;
+
+ default: return -1;
+ }
+}
+
+ushort QScript::Lexer::singleEscape(ushort c) const
+{
+ switch(c) {
+ case 'b':
+ return 0x08;
+ case 't':
+ return 0x09;
+ case 'n':
+ return 0x0A;
+ case 'v':
+ return 0x0B;
+ case 'f':
+ return 0x0C;
+ case 'r':
+ return 0x0D;
+ case '"':
+ return 0x22;
+ case '\'':
+ return 0x27;
+ case '\\':
+ return 0x5C;
+ default:
+ return c;
+ }
+}
+
+ushort QScript::Lexer::convertOctal(ushort c1, ushort c2,
+ ushort c3) const
+{
+ return ((c1 - '0') * 64 + (c2 - '0') * 8 + c3 - '0');
+}
+
+unsigned char QScript::Lexer::convertHex(ushort c)
+{
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ else if (c >= 'a' && c <= 'f')
+ return (c - 'a' + 10);
+ else
+ return (c - 'A' + 10);
+}
+
+unsigned char QScript::Lexer::convertHex(ushort c1, ushort c2)
+{
+ return ((convertHex(c1) << 4) + convertHex(c2));
+}
+
+QChar QScript::Lexer::convertUnicode(ushort c1, ushort c2,
+ ushort c3, ushort c4)
+{
+ return QChar((convertHex(c3) << 4) + convertHex(c4),
+ (convertHex(c1) << 4) + convertHex(c2));
+}
+
+void QScript::Lexer::record8(ushort c)
+{
+ Q_ASSERT(c <= 0xff);
+
+ // enlarge buffer if full
+ if (pos8 >= size8 - 1) {
+ char *tmp = new char[2 * size8];
+ memcpy(tmp, buffer8, size8 * sizeof(char));
+ delete [] buffer8;
+ buffer8 = tmp;
+ size8 *= 2;
+ }
+
+ buffer8[pos8++] = (char) c;
+}
+
+void QScript::Lexer::record16(QChar c)
+{
+ // enlarge buffer if full
+ if (pos16 >= size16 - 1) {
+ QChar *tmp = new QChar[2 * size16];
+ memcpy(tmp, buffer16, size16 * sizeof(QChar));
+ delete [] buffer16;
+ buffer16 = tmp;
+ size16 *= 2;
+ }
+
+ buffer16[pos16++] = c;
+}
+
+void QScript::Lexer::recordStartPos()
+{
+ startlineno = yylineno;
+ startcolumn = yycolumn;
+}
+
+bool QScript::Lexer::scanRegExp(RegExpBodyPrefix prefix)
+{
+ pos16 = 0;
+ bool lastWasEscape = false;
+
+ if (prefix == EqualPrefix)
+ record16(QLatin1Char('='));
+
+ while (1) {
+ if (isLineTerminator() || current == 0) {
+ errmsg = LU::tr("Unterminated regular expression literal");
+ return false;
+ }
+ else if (current != '/' || lastWasEscape == true)
+ {
+ record16(current);
+ lastWasEscape = !lastWasEscape && (current == '\\');
+ }
+ else {
+ pattern = QString(buffer16, pos16);
+ pos16 = 0;
+ shift(1);
+ break;
+ }
+ shift(1);
+ }
+
+ flags = 0;
+ while (isIdentLetter(current)) {
+ record16(current);
+ shift(1);
+ }
+
+ return true;
+}
+
+void QScript::Lexer::syncProhibitAutomaticSemicolon()
+{
+ if (parenthesesState == BalancedParentheses) {
+ // we have seen something like "if (foo)", which means we should
+ // never insert an automatic semicolon at this point, since it would
+ // then be expanded into an empty statement (ECMA-262 7.9.1)
+ prohibitAutomaticSemicolon = true;
+ parenthesesState = IgnoreParentheses;
+ } else {
+ prohibitAutomaticSemicolon = false;
+ }
+}
+
+void QScript::Lexer::processComment(const QChar *chars, int length)
+{
+ commentProcessor->processComment(chars, length);
+}
+
+
+class Translator;
+
+class QScriptParser: protected $table, public QScript::CommentProcessor
+{
+public:
+ QVariant val;
+
+ struct Location {
+ int startLine;
+ int startColumn;
+ int endLine;
+ int endColumn;
+ };
+
+public:
+ QScriptParser();
+ ~QScriptParser();
+
+ void setLexer(QScript::Lexer *);
+ void setTranslator(Translator *);
+
+ bool parse();
+
+ QString fileName() const
+ { return lexer->fileName(); }
+ inline QString errorMessage() const
+ { return error_message; }
+ inline int errorLineNumber() const
+ { return error_lineno; }
+ inline int errorColumnNumber() const
+ { return error_column; }
+
+protected:
+ inline void reallocateStack();
+
+ inline QVariant &sym(int index)
+ { return sym_stack [tos + index - 1]; }
+
+ inline Location &loc(int index)
+ { return location_stack [tos + index - 2]; }
+
+ std::ostream &yyMsg(int line = 0);
+
+ virtual void processComment(const QChar *, int);
+
+protected:
+ int tos;
+ int stack_size;
+ QVector<QVariant> sym_stack;
+ int *state_stack;
+ Location *location_stack;
+ QString error_message;
+ int error_lineno;
+ int error_column;
+
+private:
+ QScript::Lexer *lexer;
+ Translator *translator;
+ QString trcontext;
+ QString extracomment;
+ QString msgid;
+ QString sourcetext;
+ TranslatorMessage::ExtraData extra;
+};
+
+inline void QScriptParser::reallocateStack()
+{
+ if (! stack_size)
+ stack_size = 128;
+ else
+ stack_size <<= 1;
+
+ sym_stack.resize(stack_size);
+ state_stack = reinterpret_cast<int*> (qRealloc(state_stack, stack_size * sizeof(int)));
+ location_stack = reinterpret_cast<Location*> (qRealloc(location_stack, stack_size * sizeof(Location)));
+}
+
+inline static bool automatic(QScript::Lexer *lexer, int token)
+{
+ return (token == $table::T_RBRACE)
+ || (token == 0)
+ || lexer->prevTerminator();
+}
+
+QScriptParser::QScriptParser():
+ tos(0),
+ stack_size(0),
+ sym_stack(0),
+ state_stack(0),
+ location_stack(0),
+ lexer(0),
+ translator(0)
+{
+}
+
+QScriptParser::~QScriptParser()
+{
+ if (stack_size) {
+ qFree(state_stack);
+ qFree(location_stack);
+ }
+}
+
+static inline QScriptParser::Location location(QScript::Lexer *lexer)
+{
+ QScriptParser::Location loc;
+ loc.startLine = lexer->startLineNo();
+ loc.startColumn = lexer->startColumnNo();
+ loc.endLine = lexer->endLineNo();
+ loc.endColumn = lexer->endColumnNo();
+ return loc;
+}
+
+void QScriptParser::setLexer(QScript::Lexer *lex)
+{
+ lexer = lex;
+}
+
+void QScriptParser::setTranslator(Translator *tor)
+{
+ translator = tor;
+}
+
+bool QScriptParser::parse()
+{
+ Q_ASSERT(lexer != 0);
+ Q_ASSERT(translator != 0);
+ trcontext = QFileInfo(fileName()).baseName();
+
+ const int INITIAL_STATE = 0;
+
+ int yytoken = -1;
+ int saved_yytoken = -1;
+ int identLineNo = -1;
+
+ reallocateStack();
+
+ tos = 0;
+ state_stack[++tos] = INITIAL_STATE;
+
+ while (true)
+ {
+ const int state = state_stack [tos];
+ if (yytoken == -1 && - TERMINAL_COUNT != action_index [state])
+ {
+ if (saved_yytoken == -1)
+ {
+ yytoken = lexer->lex();
+ location_stack [tos] = location(lexer);
+ }
+ else
+ {
+ yytoken = saved_yytoken;
+ saved_yytoken = -1;
+ }
+ }
+
+ int act = t_action (state, yytoken);
+
+ if (act == ACCEPT_STATE)
+ return true;
+
+ else if (act > 0)
+ {
+ if (++tos == stack_size)
+ reallocateStack();
+
+ sym_stack [tos] = lexer->val ();
+ state_stack [tos] = act;
+ location_stack [tos] = location(lexer);
+ yytoken = -1;
+ }
+
+ else if (act < 0)
+ {
+ int r = - act - 1;
+
+ tos -= rhs [r];
+ act = state_stack [tos++];
+
+ switch (r) {
+./
+
+PrimaryExpression: T_THIS ;
+
+PrimaryExpression: T_IDENTIFIER ;
+/.
+case $rule_number: {
+ sym(1) = sym(1).toByteArray();
+ identLineNo = lexer->startLineNo();
+} break;
+./
+
+PrimaryExpression: T_NULL ;
+PrimaryExpression: T_TRUE ;
+PrimaryExpression: T_FALSE ;
+PrimaryExpression: T_NUMERIC_LITERAL ;
+PrimaryExpression: T_STRING_LITERAL ;
+
+PrimaryExpression: T_DIVIDE_ ;
+/:
+#define Q_SCRIPT_REGEXPLITERAL_RULE1 $rule_number
+:/
+/.
+case $rule_number: {
+ bool rx = lexer->scanRegExp(QScript::Lexer::NoPrefix);
+ if (!rx) {
+ error_message = lexer->errorMessage();
+ error_lineno = lexer->startLineNo();
+ error_column = lexer->startColumnNo();
+ return false;
+ }
+} break;
+./
+
+PrimaryExpression: T_DIVIDE_EQ ;
+/:
+#define Q_SCRIPT_REGEXPLITERAL_RULE2 $rule_number
+:/
+/.
+case $rule_number: {
+ bool rx = lexer->scanRegExp(QScript::Lexer::EqualPrefix);
+ if (!rx) {
+ error_message = lexer->errorMessage();
+ error_lineno = lexer->startLineNo();
+ error_column = lexer->startColumnNo();
+ return false;
+ }
+} break;
+./
+
+PrimaryExpression: T_LBRACKET ElisionOpt T_RBRACKET ;
+PrimaryExpression: T_LBRACKET ElementList T_RBRACKET ;
+PrimaryExpression: T_LBRACKET ElementList T_COMMA ElisionOpt T_RBRACKET ;
+PrimaryExpression: T_LBRACE PropertyNameAndValueListOpt T_RBRACE ;
+PrimaryExpression: T_LPAREN Expression T_RPAREN ;
+ElementList: ElisionOpt AssignmentExpression ;
+ElementList: ElementList T_COMMA ElisionOpt AssignmentExpression ;
+Elision: T_COMMA ;
+Elision: Elision T_COMMA ;
+ElisionOpt: ;
+ElisionOpt: Elision ;
+PropertyNameAndValueList: PropertyName T_COLON AssignmentExpression ;
+PropertyNameAndValueList: PropertyNameAndValueList T_COMMA PropertyName T_COLON AssignmentExpression ;
+PropertyName: T_IDENTIFIER ;
+PropertyName: T_STRING_LITERAL ;
+PropertyName: T_NUMERIC_LITERAL ;
+PropertyName: ReservedIdentifier ;
+ReservedIdentifier: T_BREAK ;
+ReservedIdentifier: T_CASE ;
+ReservedIdentifier: T_CATCH ;
+ReservedIdentifier: T_CONST ;
+ReservedIdentifier: T_CONTINUE ;
+ReservedIdentifier: T_DEBUGGER ;
+ReservedIdentifier: T_DEFAULT ;
+ReservedIdentifier: T_DELETE ;
+ReservedIdentifier: T_DO ;
+ReservedIdentifier: T_ELSE ;
+ReservedIdentifier: T_FALSE ;
+ReservedIdentifier: T_FINALLY ;
+ReservedIdentifier: T_FOR ;
+ReservedIdentifier: T_FUNCTION ;
+ReservedIdentifier: T_IF ;
+ReservedIdentifier: T_IN ;
+ReservedIdentifier: T_INSTANCEOF ;
+ReservedIdentifier: T_NEW ;
+ReservedIdentifier: T_NULL ;
+ReservedIdentifier: T_RESERVED_WORD ;
+ReservedIdentifier: T_RETURN ;
+ReservedIdentifier: T_SWITCH ;
+ReservedIdentifier: T_THIS ;
+ReservedIdentifier: T_THROW ;
+ReservedIdentifier: T_TRUE ;
+ReservedIdentifier: T_TRY ;
+ReservedIdentifier: T_TYPEOF ;
+ReservedIdentifier: T_VAR ;
+ReservedIdentifier: T_VOID ;
+ReservedIdentifier: T_WHILE ;
+ReservedIdentifier: T_WITH ;
+PropertyIdentifier: T_IDENTIFIER ;
+PropertyIdentifier: ReservedIdentifier ;
+
+MemberExpression: PrimaryExpression ;
+MemberExpression: FunctionExpression ;
+MemberExpression: MemberExpression T_LBRACKET Expression T_RBRACKET ;
+MemberExpression: MemberExpression T_DOT PropertyIdentifier ;
+MemberExpression: T_NEW MemberExpression Arguments ;
+NewExpression: MemberExpression ;
+NewExpression: T_NEW NewExpression ;
+
+CallExpression: MemberExpression Arguments ;
+/.
+case $rule_number: {
+ QString name = sym(1).toString();
+ if ((name == QLatin1String("qsTranslate")) || (name == QLatin1String("QT_TRANSLATE_NOOP"))) {
+ if (!sourcetext.isEmpty())
+ yyMsg(identLineNo) << qPrintable(LU::tr("//% cannot be used with %1(). Ignoring\n").arg(name));
+ QVariantList args = sym(2).toList();
+ if (args.size() < 2) {
+ yyMsg(identLineNo) << qPrintable(LU::tr("%1() requires at least two arguments.\n").arg(name));
+ } else {
+ if ((args.at(0).type() != QVariant::String)
+ || (args.at(1).type() != QVariant::String)) {
+ yyMsg(identLineNo) << qPrintable(LU::tr("%1(): both arguments must be literal strings.\n").arg(name));
+ } else {
+ QString context = args.at(0).toString();
+ QString text = args.at(1).toString();
+ QString comment = args.value(2).toString();
+ bool plural = (args.size() > 4);
+ recordMessage(translator, context, text, comment, extracomment,
+ msgid, extra, plural, fileName(), identLineNo);
+ }
+ }
+ sourcetext.clear();
+ extracomment.clear();
+ msgid.clear();
+ extra.clear();
+ } else if ((name == QLatin1String("qsTr")) || (name == QLatin1String("QT_TR_NOOP"))) {
+ if (!sourcetext.isEmpty())
+ yyMsg(identLineNo) << qPrintable(LU::tr("//% cannot be used with %1(). Ignoring\n").arg(name));
+ QVariantList args = sym(2).toList();
+ if (args.size() < 1) {
+ yyMsg(identLineNo) << qPrintable(LU::tr("%1() requires at least one argument.\n").arg(name));
+ } else {
+ if (args.at(0).type() != QVariant::String) {
+ yyMsg(identLineNo) << qPrintable(LU::tr("%1(): text to translate must be a literal string.\n").arg(name));
+ } else {
+ QString text = args.at(0).toString();
+ QString comment = args.value(1).toString();
+ bool plural = (args.size() > 2);
+ recordMessage(translator, trcontext, text, comment, extracomment,
+ msgid, extra, plural, fileName(), identLineNo);
+ }
+ }
+ sourcetext.clear();
+ extracomment.clear();
+ msgid.clear();
+ extra.clear();
+ } else if ((name == QLatin1String("qsTrId")) || (name == QLatin1String("QT_TRID_NOOP"))) {
+ if (!msgid.isEmpty())
+ yyMsg(identLineNo) << qPrintable(LU::tr("//= cannot be used with %1(). Ignoring\n").arg(name));
+ QVariantList args = sym(2).toList();
+ if (args.size() < 1) {
+ yyMsg(identLineNo) << qPrintable(LU::tr("%1() requires at least one argument.\n").arg(name));
+ } else {
+ if (args.at(0).type() != QVariant::String) {
+ yyMsg(identLineNo) << qPrintable(LU::tr("%1(): identifier must be a literal string.\n").arg(name));
+ } else {
+ msgid = args.at(0).toString();
+ bool plural = (args.size() > 1);
+ recordMessage(translator, QString(), sourcetext, QString(), extracomment,
+ msgid, extra, plural, fileName(), identLineNo);
+ }
+ }
+ sourcetext.clear();
+ extracomment.clear();
+ msgid.clear();
+ extra.clear();
+ }
+} break;
+./
+
+CallExpression: CallExpression Arguments ;
+CallExpression: CallExpression T_LBRACKET Expression T_RBRACKET ;
+CallExpression: CallExpression T_DOT PropertyIdentifier ;
+
+Arguments: T_LPAREN T_RPAREN ;
+/.
+case $rule_number: {
+ sym(1) = QVariantList();
+} break;
+./
+
+Arguments: T_LPAREN ArgumentList T_RPAREN ;
+/.
+case $rule_number: {
+ sym(1) = sym(2);
+} break;
+./
+
+ArgumentList: AssignmentExpression ;
+/.
+case $rule_number: {
+ sym(1) = QVariantList() << sym(1);
+} break;
+./
+
+ArgumentList: ArgumentList T_COMMA AssignmentExpression ;
+/.
+case $rule_number: {
+ sym(1) = sym(1).toList() << sym(3);
+} break;
+./
+
+LeftHandSideExpression: NewExpression ;
+LeftHandSideExpression: CallExpression ;
+PostfixExpression: LeftHandSideExpression ;
+PostfixExpression: LeftHandSideExpression T_PLUS_PLUS ;
+PostfixExpression: LeftHandSideExpression T_MINUS_MINUS ;
+UnaryExpression: PostfixExpression ;
+UnaryExpression: T_DELETE UnaryExpression ;
+UnaryExpression: T_VOID UnaryExpression ;
+UnaryExpression: T_TYPEOF UnaryExpression ;
+UnaryExpression: T_PLUS_PLUS UnaryExpression ;
+UnaryExpression: T_MINUS_MINUS UnaryExpression ;
+UnaryExpression: T_PLUS UnaryExpression ;
+UnaryExpression: T_MINUS UnaryExpression ;
+UnaryExpression: T_TILDE UnaryExpression ;
+UnaryExpression: T_NOT UnaryExpression ;
+MultiplicativeExpression: UnaryExpression ;
+MultiplicativeExpression: MultiplicativeExpression T_STAR UnaryExpression ;
+MultiplicativeExpression: MultiplicativeExpression T_DIVIDE_ UnaryExpression ;
+MultiplicativeExpression: MultiplicativeExpression T_REMAINDER UnaryExpression ;
+AdditiveExpression: MultiplicativeExpression ;
+
+AdditiveExpression: AdditiveExpression T_PLUS MultiplicativeExpression ;
+/.
+case $rule_number: {
+ if ((sym(1).type() == QVariant::String) || (sym(3).type() == QVariant::String))
+ sym(1) = sym(1).toString() + sym(3).toString();
+ else
+ sym(1) = QVariant();
+} break;
+./
+
+AdditiveExpression: AdditiveExpression T_MINUS MultiplicativeExpression ;
+ShiftExpression: AdditiveExpression ;
+ShiftExpression: ShiftExpression T_LT_LT AdditiveExpression ;
+ShiftExpression: ShiftExpression T_GT_GT AdditiveExpression ;
+ShiftExpression: ShiftExpression T_GT_GT_GT AdditiveExpression ;
+RelationalExpression: ShiftExpression ;
+RelationalExpression: RelationalExpression T_LT ShiftExpression ;
+RelationalExpression: RelationalExpression T_GT ShiftExpression ;
+RelationalExpression: RelationalExpression T_LE ShiftExpression ;
+RelationalExpression: RelationalExpression T_GE ShiftExpression ;
+RelationalExpression: RelationalExpression T_INSTANCEOF ShiftExpression ;
+RelationalExpression: RelationalExpression T_IN ShiftExpression ;
+RelationalExpressionNotIn: ShiftExpression ;
+RelationalExpressionNotIn: RelationalExpressionNotIn T_LT ShiftExpression ;
+RelationalExpressionNotIn: RelationalExpressionNotIn T_GT ShiftExpression ;
+RelationalExpressionNotIn: RelationalExpressionNotIn T_LE ShiftExpression ;
+RelationalExpressionNotIn: RelationalExpressionNotIn T_GE ShiftExpression ;
+RelationalExpressionNotIn: RelationalExpressionNotIn T_INSTANCEOF ShiftExpression ;
+EqualityExpression: RelationalExpression ;
+EqualityExpression: EqualityExpression T_EQ_EQ RelationalExpression ;
+EqualityExpression: EqualityExpression T_NOT_EQ RelationalExpression ;
+EqualityExpression: EqualityExpression T_EQ_EQ_EQ RelationalExpression ;
+EqualityExpression: EqualityExpression T_NOT_EQ_EQ RelationalExpression ;
+EqualityExpressionNotIn: RelationalExpressionNotIn ;
+EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ RelationalExpressionNotIn ;
+EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ RelationalExpressionNotIn;
+EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ_EQ RelationalExpressionNotIn ;
+EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ_EQ RelationalExpressionNotIn ;
+BitwiseANDExpression: EqualityExpression ;
+BitwiseANDExpression: BitwiseANDExpression T_AND EqualityExpression ;
+BitwiseANDExpressionNotIn: EqualityExpressionNotIn ;
+BitwiseANDExpressionNotIn: BitwiseANDExpressionNotIn T_AND EqualityExpressionNotIn ;
+BitwiseXORExpression: BitwiseANDExpression ;
+BitwiseXORExpression: BitwiseXORExpression T_XOR BitwiseANDExpression ;
+BitwiseXORExpressionNotIn: BitwiseANDExpressionNotIn ;
+BitwiseXORExpressionNotIn: BitwiseXORExpressionNotIn T_XOR BitwiseANDExpressionNotIn ;
+BitwiseORExpression: BitwiseXORExpression ;
+BitwiseORExpression: BitwiseORExpression T_OR BitwiseXORExpression ;
+BitwiseORExpressionNotIn: BitwiseXORExpressionNotIn ;
+BitwiseORExpressionNotIn: BitwiseORExpressionNotIn T_OR BitwiseXORExpressionNotIn ;
+LogicalANDExpression: BitwiseORExpression ;
+LogicalANDExpression: LogicalANDExpression T_AND_AND BitwiseORExpression ;
+LogicalANDExpressionNotIn: BitwiseORExpressionNotIn ;
+LogicalANDExpressionNotIn: LogicalANDExpressionNotIn T_AND_AND BitwiseORExpressionNotIn ;
+LogicalORExpression: LogicalANDExpression ;
+LogicalORExpression: LogicalORExpression T_OR_OR LogicalANDExpression ;
+LogicalORExpressionNotIn: LogicalANDExpressionNotIn ;
+LogicalORExpressionNotIn: LogicalORExpressionNotIn T_OR_OR LogicalANDExpressionNotIn ;
+ConditionalExpression: LogicalORExpression ;
+ConditionalExpression: LogicalORExpression T_QUESTION AssignmentExpression T_COLON AssignmentExpression ;
+ConditionalExpressionNotIn: LogicalORExpressionNotIn ;
+ConditionalExpressionNotIn: LogicalORExpressionNotIn T_QUESTION AssignmentExpressionNotIn T_COLON AssignmentExpressionNotIn ;
+AssignmentExpression: ConditionalExpression ;
+AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression ;
+AssignmentExpressionNotIn: ConditionalExpressionNotIn ;
+AssignmentExpressionNotIn: LeftHandSideExpression AssignmentOperator AssignmentExpressionNotIn ;
+AssignmentOperator: T_EQ ;
+AssignmentOperator: T_STAR_EQ ;
+AssignmentOperator: T_DIVIDE_EQ ;
+AssignmentOperator: T_REMAINDER_EQ ;
+AssignmentOperator: T_PLUS_EQ ;
+AssignmentOperator: T_MINUS_EQ ;
+AssignmentOperator: T_LT_LT_EQ ;
+AssignmentOperator: T_GT_GT_EQ ;
+AssignmentOperator: T_GT_GT_GT_EQ ;
+AssignmentOperator: T_AND_EQ ;
+AssignmentOperator: T_XOR_EQ ;
+AssignmentOperator: T_OR_EQ ;
+Expression: AssignmentExpression ;
+Expression: Expression T_COMMA AssignmentExpression ;
+ExpressionOpt: ;
+ExpressionOpt: Expression ;
+ExpressionNotIn: AssignmentExpressionNotIn ;
+ExpressionNotIn: ExpressionNotIn T_COMMA AssignmentExpressionNotIn ;
+ExpressionNotInOpt: ;
+ExpressionNotInOpt: ExpressionNotIn ;
+
+Statement: Block ;
+/.
+ case $rule_number:
+./
+Statement: VariableStatement ;
+/.
+ case $rule_number:
+./
+Statement: EmptyStatement ;
+/.
+ case $rule_number:
+./
+Statement: ExpressionStatement ;
+/.
+ case $rule_number:
+./
+Statement: IfStatement ;
+/.
+ case $rule_number:
+./
+Statement: IterationStatement ;
+/.
+ case $rule_number:
+./
+Statement: ContinueStatement ;
+/.
+ case $rule_number:
+./
+Statement: BreakStatement ;
+/.
+ case $rule_number:
+./
+Statement: ReturnStatement ;
+/.
+ case $rule_number:
+./
+Statement: WithStatement ;
+/.
+ case $rule_number:
+./
+Statement: LabelledStatement ;
+/.
+ case $rule_number:
+./
+Statement: SwitchStatement ;
+/.
+ case $rule_number:
+./
+Statement: ThrowStatement ;
+/.
+ case $rule_number:
+./
+Statement: TryStatement ;
+/.
+ case $rule_number:
+./
+Statement: DebuggerStatement ;
+/.
+ case $rule_number:
+ if (!sourcetext.isEmpty() || !extracomment.isEmpty() || !msgid.isEmpty() || !extra.isEmpty()) {
+ yyMsg() << qPrintable(LU::tr("Discarding unconsumed meta data\n"));
+ sourcetext.clear();
+ extracomment.clear();
+ msgid.clear();
+ extra.clear();
+ }
+ break;
+./
+
+Block: T_LBRACE StatementListOpt T_RBRACE ;
+StatementList: Statement ;
+StatementList: StatementList Statement ;
+StatementListOpt: ;
+StatementListOpt: StatementList ;
+VariableStatement: VariableDeclarationKind VariableDeclarationList T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+VariableStatement: VariableDeclarationKind VariableDeclarationList T_SEMICOLON ;
+VariableDeclarationKind: T_CONST ;
+VariableDeclarationKind: T_VAR ;
+VariableDeclarationList: VariableDeclaration ;
+VariableDeclarationList: VariableDeclarationList T_COMMA VariableDeclaration ;
+VariableDeclarationListNotIn: VariableDeclarationNotIn ;
+VariableDeclarationListNotIn: VariableDeclarationListNotIn T_COMMA VariableDeclarationNotIn ;
+VariableDeclaration: T_IDENTIFIER InitialiserOpt ;
+VariableDeclarationNotIn: T_IDENTIFIER InitialiserNotInOpt ;
+Initialiser: T_EQ AssignmentExpression ;
+InitialiserOpt: ;
+InitialiserOpt: Initialiser ;
+InitialiserNotIn: T_EQ AssignmentExpressionNotIn ;
+InitialiserNotInOpt: ;
+InitialiserNotInOpt: InitialiserNotIn ;
+EmptyStatement: T_SEMICOLON ;
+ExpressionStatement: Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+ExpressionStatement: Expression T_SEMICOLON ;
+IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement T_ELSE Statement ;
+IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement ;
+IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_SEMICOLON ;
+IterationStatement: T_WHILE T_LPAREN Expression T_RPAREN Statement ;
+IterationStatement: T_FOR T_LPAREN ExpressionNotInOpt T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ;
+IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationListNotIn T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ;
+IterationStatement: T_FOR T_LPAREN LeftHandSideExpression T_IN Expression T_RPAREN Statement ;
+IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationNotIn T_IN Expression T_RPAREN Statement ;
+ContinueStatement: T_CONTINUE T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+ContinueStatement: T_CONTINUE T_SEMICOLON ;
+ContinueStatement: T_CONTINUE T_IDENTIFIER T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+ContinueStatement: T_CONTINUE T_IDENTIFIER T_SEMICOLON ;
+BreakStatement: T_BREAK T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+BreakStatement: T_BREAK T_SEMICOLON ;
+BreakStatement: T_BREAK T_IDENTIFIER T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+BreakStatement: T_BREAK T_IDENTIFIER T_SEMICOLON ;
+ReturnStatement: T_RETURN ExpressionOpt T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+ReturnStatement: T_RETURN ExpressionOpt T_SEMICOLON ;
+WithStatement: T_WITH T_LPAREN Expression T_RPAREN Statement ;
+SwitchStatement: T_SWITCH T_LPAREN Expression T_RPAREN CaseBlock ;
+CaseBlock: T_LBRACE CaseClausesOpt T_RBRACE ;
+CaseBlock: T_LBRACE CaseClausesOpt DefaultClause CaseClausesOpt T_RBRACE ;
+CaseClauses: CaseClause ;
+CaseClauses: CaseClauses CaseClause ;
+CaseClausesOpt: ;
+CaseClausesOpt: CaseClauses ;
+CaseClause: T_CASE Expression T_COLON StatementListOpt ;
+DefaultClause: T_DEFAULT T_COLON StatementListOpt ;
+LabelledStatement: T_IDENTIFIER T_COLON Statement ;
+ThrowStatement: T_THROW Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+ThrowStatement: T_THROW Expression T_SEMICOLON ;
+TryStatement: T_TRY Block Catch ;
+TryStatement: T_TRY Block Finally ;
+TryStatement: T_TRY Block Catch Finally ;
+Catch: T_CATCH T_LPAREN T_IDENTIFIER T_RPAREN Block ;
+Finally: T_FINALLY Block ;
+DebuggerStatement: T_DEBUGGER ;
+FunctionDeclaration: T_FUNCTION T_IDENTIFIER T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ;
+FunctionExpression: T_FUNCTION IdentifierOpt T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ;
+FormalParameterList: T_IDENTIFIER ;
+FormalParameterList: FormalParameterList T_COMMA T_IDENTIFIER ;
+FormalParameterListOpt: ;
+FormalParameterListOpt: FormalParameterList ;
+FunctionBodyOpt: ;
+FunctionBodyOpt: FunctionBody ;
+FunctionBody: SourceElements ;
+Program: SourceElements ;
+SourceElements: SourceElement ;
+SourceElements: SourceElements SourceElement ;
+SourceElement: Statement ;
+SourceElement: FunctionDeclaration ;
+IdentifierOpt: ;
+IdentifierOpt: T_IDENTIFIER ;
+PropertyNameAndValueListOpt: ;
+PropertyNameAndValueListOpt: PropertyNameAndValueList ;
+
+/.
+ } // switch
+
+ state_stack [tos] = nt_action (act, lhs [r] - TERMINAL_COUNT);
+
+ if (rhs[r] > 1) {
+ location_stack[tos - 1].endLine = location_stack[tos + rhs[r] - 2].endLine;
+ location_stack[tos - 1].endColumn = location_stack[tos + rhs[r] - 2].endColumn;
+ location_stack[tos] = location_stack[tos + rhs[r] - 1];
+ }
+ }
+
+ else
+ {
+ if (saved_yytoken == -1 && automatic (lexer, yytoken) && t_action (state, T_AUTOMATIC_SEMICOLON) > 0)
+ {
+ saved_yytoken = yytoken;
+ yytoken = T_SEMICOLON;
+ continue;
+ }
+
+ else if ((state == INITIAL_STATE) && (yytoken == 0)) {
+ // accept empty input
+ yytoken = T_SEMICOLON;
+ continue;
+ }
+
+ int ers = state;
+ int shifts = 0;
+ int reduces = 0;
+ int expected_tokens [3];
+ for (int tk = 0; tk < TERMINAL_COUNT; ++tk)
+ {
+ int k = t_action (ers, tk);
+
+ if (! k)
+ continue;
+ else if (k < 0)
+ ++reduces;
+ else if (spell [tk])
+ {
+ if (shifts < 3)
+ expected_tokens [shifts] = tk;
+ ++shifts;
+ }
+ }
+
+ error_message.clear ();
+ if (shifts && shifts < 3)
+ {
+ bool first = true;
+
+ for (int s = 0; s < shifts; ++s)
+ {
+ if (first)
+ error_message += QLatin1String ("Expected ");
+ //: Beginning of the string that contains
+ //: comma-separated list of expected tokens
+ error_message += LU::tr("Expected ");
+ else
+ error_message += QLatin1String (", ");
+
+ first = false;
+ error_message += QLatin1String("`");
+ error_message += QLatin1String (spell [expected_tokens [s]]);
+ error_message += QLatin1String("'");
+ }
+ }
+
+ if (error_message.isEmpty())
+ error_message = lexer->errorMessage();
+
+ error_lineno = lexer->startLineNo();
+ error_column = lexer->startColumnNo();
+
+ return false;
+ }
+ }
+
+ return false;
+}
+
+std::ostream &QScriptParser::yyMsg(int line)
+{
+ return std::cerr << qPrintable(fileName()) << ':' << (line ? line : lexer->startLineNo()) << ": ";
+}
+
+void QScriptParser::processComment(const QChar *chars, int length)
+{
+ if (!length)
+ return;
+ // Try to match the logic of the C++ parser.
+ if (*chars == QLatin1Char(':') && chars[1].isSpace()) {
+ extracomment += QString(chars+2, length-2);
+ } else if (*chars == QLatin1Char('=') && chars[1].isSpace()) {
+ msgid = QString(chars+2, length-2).simplified();
+ } else if (*chars == QLatin1Char('~') && chars[1].isSpace()) {
+ QString text = QString(chars+2, length-2).trimmed();
+ int k = text.indexOf(QLatin1Char(' '));
+ if (k > -1)
+ extra.insert(text.left(k), text.mid(k + 1).trimmed());
+ } else if (*chars == QLatin1Char('%') && chars[1].isSpace()) {
+ sourcetext.reserve(sourcetext.length() + length-2);
+ ushort *ptr = (ushort *)sourcetext.data() + sourcetext.length();
+ int p = 2, c;
+ forever {
+ if (p >= length)
+ break;
+ c = chars[p++].unicode();
+ if (isspace(c))
+ continue;
+ if (c != '"') {
+ yyMsg() << qPrintable(LU::tr("Unexpected character in meta string\n"));
+ break;
+ }
+ forever {
+ if (p >= length) {
+ whoops:
+ yyMsg() << qPrintable(LU::tr("Unterminated meta string\n"));
+ break;
+ }
+ c = chars[p++].unicode();
+ if (c == '"')
+ break;
+ if (c == '\\') {
+ if (p >= length)
+ goto whoops;
+ c = chars[p++].unicode();
+ if (c == '\n')
+ goto whoops;
+ *ptr++ = '\\';
+ }
+ *ptr++ = c;
+ }
+ }
+ sourcetext.resize(ptr - (ushort *)sourcetext.data());
+ } else {
+ int idx = 0;
+ ushort c;
+ while ((c = chars[idx].unicode()) == ' ' || c == '\t' || c == '\n')
+ ++idx;
+ if (!memcmp(chars + idx, MagicComment.unicode(), MagicComment.length() * 2)) {
+ idx += MagicComment.length();
+ QString comment = QString(chars + idx, length - idx).simplified();
+ int k = comment.indexOf(QLatin1Char(' '));
+ if (k == -1) {
+ trcontext = comment;
+ } else {
+ trcontext = comment.left(k);
+ comment.remove(0, k + 1);
+ TranslatorMessage msg(
+ trcontext, QString(),
+ comment, QString(),
+ fileName(), lexer->startLineNo(), QStringList(),
+ TranslatorMessage::Finished, /*plural=*/false);
+ msg.setExtraComment(extracomment.simplified());
+ extracomment.clear();
+ translator->append(msg);
+ translator->setExtras(extra);
+ extra.clear();
+ }
+ }
+ }
+}
+
+
+bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd)
+{
+ QFile file(filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ cd.appendError(LU::tr("Cannot open %1: %2").arg(filename, file.errorString()));
+ return false;
+ }
+ QTextStream ts(&file);
+ QByteArray codecName;
+ if (!cd.m_codecForSource.isEmpty())
+ codecName = cd.m_codecForSource;
+ else
+ codecName = translator.codecName(); // Just because it should be latin1 already
+ ts.setCodec(QTextCodec::codecForName(codecName));
+ ts.setAutoDetectUnicode(true);
+
+ QString code = ts.readAll();
+ QScriptParser parser;
+ QScript::Lexer lexer(&parser);
+ lexer.setCode(code, filename, /*lineNumber=*/1);
+ parser.setLexer(&lexer);
+ parser.setTranslator(&translator);
+ if (!parser.parse()) {
+ std::cerr << qPrintable(filename) << ':' << parser.errorLineNumber() << ": "
+ << qPrintable(parser.errorMessage()) << std::endl;
+ return false;
+ }
+
+ // Java uses UTF-16 internally and Jambi makes UTF-8 for tr() purposes of it.
+ translator.setCodecName("UTF-8");
+ return true;
+}
+
+QT_END_NAMESPACE
+./
diff --git a/src/linguist/lupdate/ui.cpp b/src/linguist/lupdate/ui.cpp
new file mode 100644
index 000000000..6bbd189ed
--- /dev/null
+++ b/src/linguist/lupdate/ui.cpp
@@ -0,0 +1,208 @@
+/****************************************************************************
+**
+** 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 "lupdate.h"
+
+#include <translator.h>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QFile>
+#include <QtCore/QString>
+
+#include <QtXml/QXmlAttributes>
+#include <QtXml/QXmlDefaultHandler>
+#include <QtXml/QXmlLocator>
+#include <QtXml/QXmlParseException>
+
+
+QT_BEGIN_NAMESPACE
+
+class LU {
+ Q_DECLARE_TR_FUNCTIONS(LUpdate)
+};
+
+class UiReader : public QXmlDefaultHandler
+{
+public:
+ UiReader(Translator &translator, ConversionData &cd)
+ : m_translator(translator), m_cd(cd), m_lineNumber(-1), m_isTrString(false),
+ m_needUtf8(translator.codecName() != "UTF-8")
+ {}
+
+ bool startElement(const QString &namespaceURI, const QString &localName,
+ const QString &qName, const QXmlAttributes &atts);
+ bool endElement(const QString &namespaceURI, const QString &localName,
+ const QString &qName);
+ bool characters(const QString &ch);
+ bool fatalError(const QXmlParseException &exception);
+
+ void setDocumentLocator(QXmlLocator *locator) { m_locator = locator; }
+
+private:
+ void flush();
+
+ Translator &m_translator;
+ ConversionData &m_cd;
+ QString m_context;
+ QString m_source;
+ QString m_comment;
+ QString m_extracomment;
+ QXmlLocator *m_locator;
+
+ QString m_accum;
+ int m_lineNumber;
+ bool m_isTrString;
+ bool m_needUtf8;
+};
+
+bool UiReader::startElement(const QString &namespaceURI,
+ const QString &localName, const QString &qName, const QXmlAttributes &atts)
+{
+ Q_UNUSED(namespaceURI);
+ Q_UNUSED(localName);
+
+ if (qName == QLatin1String("item")) { // UI3 menu entries
+ flush();
+ if (!atts.value(QLatin1String("text")).isEmpty()) {
+ m_source = atts.value(QLatin1String("text"));
+ m_isTrString = true;
+ if (!m_cd.m_noUiLines)
+ m_lineNumber = m_locator->lineNumber();
+ }
+ } else if (qName == QLatin1String("string")) {
+ flush();
+ if (atts.value(QLatin1String("notr")).isEmpty() ||
+ atts.value(QLatin1String("notr")) != QLatin1String("true")) {
+ m_isTrString = true;
+ m_comment = atts.value(QLatin1String("comment"));
+ m_extracomment = atts.value(QLatin1String("extracomment"));
+ if (!m_cd.m_noUiLines)
+ m_lineNumber = m_locator->lineNumber();
+ } else {
+ m_isTrString = false;
+ }
+ }
+ m_accum.clear();
+ return true;
+}
+
+bool UiReader::endElement(const QString &namespaceURI,
+ const QString &localName, const QString &qName)
+{
+ Q_UNUSED(namespaceURI);
+ Q_UNUSED(localName);
+
+ m_accum.replace(QLatin1String("\r\n"), QLatin1String("\n"));
+
+ if (qName == QLatin1String("class")) { // UI "header"
+ if (m_context.isEmpty())
+ m_context = m_accum;
+ } else if (qName == QLatin1String("string") && m_isTrString) {
+ m_source = m_accum;
+ } else if (qName == QLatin1String("comment")) { // FIXME: what's that?
+ m_comment = m_accum;
+ flush();
+ } else if (qName == QLatin1String("function")) { // UI3 embedded code
+ fetchtrInlinedCpp(m_accum, m_translator, m_context);
+ } else {
+ flush();
+ }
+ return true;
+}
+
+bool UiReader::characters(const QString &ch)
+{
+ m_accum += ch;
+ return true;
+}
+
+bool UiReader::fatalError(const QXmlParseException &exception)
+{
+ QString msg = LU::tr("XML error: Parse error at line %1, column %2 (%3).")
+ .arg(exception.lineNumber()).arg(exception.columnNumber())
+ .arg(exception.message());
+ m_cd.appendError(msg);
+ return false;
+}
+
+void UiReader::flush()
+{
+ if (!m_context.isEmpty() && !m_source.isEmpty()) {
+ TranslatorMessage msg(m_context, m_source,
+ m_comment, QString(), m_cd.m_sourceFileName,
+ m_lineNumber, QStringList());
+ msg.setExtraComment(m_extracomment);
+ if (m_needUtf8 && msg.needs8Bit())
+ msg.setUtf8(true);
+ m_translator.extend(msg);
+ }
+ m_source.clear();
+ m_comment.clear();
+ m_extracomment.clear();
+}
+
+bool loadUI(Translator &translator, const QString &filename, ConversionData &cd)
+{
+ cd.m_sourceFileName = filename;
+ QFile file(filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ cd.appendError(LU::tr("Cannot open %1: %2").arg(filename, file.errorString()));
+ return false;
+ }
+ QXmlInputSource in(&file);
+ QXmlSimpleReader reader;
+ 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);
+ UiReader handler(translator, cd);
+ reader.setContentHandler(&handler);
+ reader.setErrorHandler(&handler);
+ bool result = reader.parse(in);
+ if (!result)
+ cd.appendError(LU::tr("Parse error in UI file"));
+ reader.setContentHandler(0);
+ reader.setErrorHandler(0);
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/lupdate/winmanifest.rc b/src/linguist/lupdate/winmanifest.rc
new file mode 100644
index 000000000..45c493576
--- /dev/null
+++ b/src/linguist/lupdate/winmanifest.rc
@@ -0,0 +1,4 @@
+#define RT_MANIFEST 24
+#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
+
+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "lupdate.exe.manifest"
diff --git a/src/linguist/phrasebooks/danish.qph b/src/linguist/phrasebooks/danish.qph
new file mode 100644
index 000000000..fb06e6f3c
--- /dev/null
+++ b/src/linguist/phrasebooks/danish.qph
@@ -0,0 +1,1018 @@
+<!DOCTYPE QPH><QPH language="da">
+<phrase>
+ <source>About</source>
+ <target>Om</target>
+</phrase>
+<phrase>
+ <source>access key</source>
+ <target>hurtigtast</target>
+</phrase>
+<phrase>
+ <source>accessibility</source>
+ <target>brugervenlighed</target>
+</phrase>
+<phrase>
+ <source>action handle</source>
+ <target>genvejshåndtag</target>
+</phrase>
+<phrase>
+ <source>active</source>
+ <target>aktiv</target>
+</phrase>
+<phrase>
+ <source>active end</source>
+ <target>markeringsafslutning</target>
+</phrase>
+<phrase>
+ <source>active object</source>
+ <target>aktivt objekt</target>
+</phrase>
+<phrase>
+ <source>active window</source>
+ <target>aktivt vindue</target>
+</phrase>
+<phrase>
+ <source>adornment</source>
+ <target>værktøjselement</target>
+</phrase>
+<phrase>
+ <source>Always on Top</source>
+ <target>Altid øverst</target>
+</phrase>
+<phrase>
+ <source>anchor point</source>
+ <target>forankringspunkt</target>
+</phrase>
+<phrase>
+ <source>Apply</source>
+ <target>Anvend</target>
+</phrase>
+<phrase>
+ <source>auto-exit</source>
+ <target>tekstboks med automatisk udgang</target>
+</phrase>
+<phrase>
+ <source>auto-repeat</source>
+ <target>automatisk gentagelse</target>
+</phrase>
+<phrase>
+ <source>automatic link</source>
+ <target>automatisk kæde</target>
+</phrase>
+<phrase>
+ <source>automatic scrolling</source>
+ <target>automatisk rulning</target>
+</phrase>
+<phrase>
+ <source>autoscroll</source>
+ <target>automatisk rulning</target>
+</phrase>
+<phrase>
+ <source>Back</source>
+ <target>Tilbage</target>
+</phrase>
+<phrase>
+ <source>Browse</source>
+ <target>Gennemse</target>
+</phrase>
+<phrase>
+ <source>Cancel</source>
+ <target>Annuller</target>
+</phrase>
+<phrase>
+ <source>cascading menu</source>
+ <target>undermenu</target>
+</phrase>
+<phrase>
+ <source>check box</source>
+ <target>afkrydsningsfelt</target>
+</phrase>
+<phrase>
+ <source>check mark</source>
+ <target>afkrydsning</target>
+</phrase>
+<phrase>
+ <source>child window</source>
+ <target>underordnet vindue</target>
+</phrase>
+<phrase>
+ <source>choose</source>
+ <target>vælge</target>
+</phrase>
+<phrase>
+ <source>click</source>
+ <target>klikke</target>
+</phrase>
+<phrase>
+ <source>Clipboard</source>
+ <target>Udklipsholder</target>
+</phrase>
+<phrase>
+ <source>Close</source>
+ <target>Luk</target>
+</phrase>
+<phrase>
+ <source>Close button</source>
+ <target>lukknap</target>
+</phrase>
+<phrase>
+ <source>collapse</source>
+ <target>skjule</target>
+ <definition>outline/disposition</definition>
+</phrase>
+<phrase>
+ <source>column heading</source>
+ <target>kolonneoverskrift</target>
+ <definition>control/kontrolelement</definition>
+</phrase>
+<phrase>
+ <source>combo box</source>
+ <target>kombinationsboks</target>
+</phrase>
+<phrase>
+ <source>command button</source>
+ <target>kommandoknap</target>
+</phrase>
+<phrase>
+ <source>container</source>
+ <target>objektbeholder</target>
+</phrase>
+<phrase>
+ <source>context-sensitive Help</source>
+ <target>kontekstafhængig hjælp</target>
+</phrase>
+<phrase>
+ <source>contextual</source>
+ <target>kontekstafhængig</target>
+</phrase>
+<phrase>
+ <source>control</source>
+ <target>kontrolelement</target>
+</phrase>
+<phrase>
+ <source>Copy</source>
+ <target>Kopier</target>
+</phrase>
+<phrase>
+ <source>Copy here</source>
+ <target>Kopier hertil</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut</source>
+ <target>Opret genvej</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut Here</source>
+ <target>Opret genvej her</target>
+</phrase>
+<phrase>
+ <source>Cut</source>
+ <target>Klip</target>
+</phrase>
+<phrase>
+ <source>default</source>
+ <target>standard</target>
+</phrase>
+<phrase>
+ <source>default button</source>
+ <target>standardknap</target>
+</phrase>
+<phrase>
+ <source>Delete</source>
+ <target>Slet</target>
+</phrase>
+<phrase>
+ <source>desktop</source>
+ <target>skrivebord</target>
+</phrase>
+<phrase>
+ <source>destination</source>
+ <target>destination</target>
+</phrase>
+<phrase>
+ <source>dialog box</source>
+ <target>dialogboks</target>
+</phrase>
+<phrase>
+ <source>disability</source>
+ <target>handicap</target>
+</phrase>
+<phrase>
+ <source>disjoint selection</source>
+ <target>usammenhængende markering</target>
+</phrase>
+<phrase>
+ <source>dock</source>
+ <target>forankre</target>
+</phrase>
+<phrase>
+ <source>document</source>
+ <target>dokument</target>
+</phrase>
+<phrase>
+ <source>double-click</source>
+ <target>dobbeltklikke</target>
+</phrase>
+<phrase>
+ <source>drag</source>
+ <target>trække</target>
+</phrase>
+<phrase>
+ <source>drag-and-drop</source>
+ <target>trække og slippe</target>
+</phrase>
+<phrase>
+ <source>drop-down combo box</source>
+ <target>kombinationsboks med rullepil</target>
+</phrase>
+<phrase>
+ <source>drop-down list box</source>
+ <target>rulleliste</target>
+</phrase>
+<phrase>
+ <source>drop-down menu</source>
+ <target>rullemenu</target>
+</phrase>
+<phrase>
+ <source>Edit</source>
+ <target>Rediger</target>
+</phrase>
+<phrase>
+ <source>Edit menu</source>
+ <target>Menuen Rediger</target>
+</phrase>
+<phrase>
+ <source>ellipsis</source>
+ <target>ellipse</target>
+</phrase>
+<phrase>
+ <source>embedded object</source>
+ <target>integreret objekt</target>
+</phrase>
+<phrase>
+ <source>Exit</source>
+ <target>Afslut</target>
+</phrase>
+<phrase>
+ <source>expand</source>
+ <target>udvide</target>
+ <definition>an outline/en disposition</definition>
+</phrase>
+<phrase>
+ <source>Explore</source>
+ <target>Stifinder</target>
+</phrase>
+<phrase>
+ <source>extended selection</source>
+ <target>udvidet markering</target>
+</phrase>
+<phrase>
+ <source>extended selection list box</source>
+ <target>liste</target>
+ <definition>der tillader udvidet markering</definition>
+</phrase>
+<phrase>
+ <source>file</source>
+ <target>fil</target>
+</phrase>
+<phrase>
+ <source>File menu</source>
+ <target>Menuen Filer</target>
+</phrase>
+<phrase>
+ <source>Find</source>
+ <target>Søg</target>
+</phrase>
+<phrase>
+ <source>Find Next</source>
+ <target>Find næste</target>
+</phrase>
+<phrase>
+ <source>Find What</source>
+ <target>Søg efter</target>
+</phrase>
+<phrase>
+ <source>folder</source>
+ <target>mappe</target>
+</phrase>
+<phrase>
+ <source>font</source>
+ <target>skrifttype</target>
+</phrase>
+<phrase>
+ <source>font size</source>
+ <target>skriftstørrelse</target>
+</phrase>
+<phrase>
+ <source>font style</source>
+ <target>typografi</target>
+</phrase>
+<phrase>
+ <source>function key</source>
+ <target>funktionstast</target>
+</phrase>
+<phrase>
+ <source>group box</source>
+ <target>gruppeboks</target>
+</phrase>
+<phrase>
+ <source>handle</source>
+ <target>håndtag</target>
+</phrase>
+<phrase>
+ <source>Help</source>
+ <target>Hjælp</target>
+</phrase>
+<phrase>
+ <source>Help menu</source>
+ <target>Menuen Hjælp</target>
+</phrase>
+<phrase>
+ <source>Hide</source>
+ <target>Skjul</target>
+</phrase>
+<phrase>
+ <source>hierarchical selection</source>
+ <target>hierarkisk markering</target>
+</phrase>
+<phrase>
+ <source>hold</source>
+ <target>holde nede</target>
+</phrase>
+<phrase>
+ <source>hot spot</source>
+ <target>aktivt punkt</target>
+</phrase>
+<phrase>
+ <source>hot zone</source>
+ <target>aktiv zone</target>
+</phrase>
+<phrase>
+ <source>icon</source>
+ <target>ikon</target>
+</phrase>
+<phrase>
+ <source>inactive</source>
+ <target>ikke-aktiv</target>
+</phrase>
+<phrase>
+ <source>inactive window</source>
+ <target>ikke-aktivt vindue</target>
+</phrase>
+<phrase>
+ <source>input focus</source>
+ <target>inputfokus</target>
+</phrase>
+<phrase>
+ <source>Insert</source>
+ <target>Menuen Indsæt</target>
+</phrase>
+<phrase>
+ <source>Insert Object</source>
+ <target>Indsæt objekt</target>
+</phrase>
+<phrase>
+ <source>insertion point</source>
+ <target>indsætningspunkt</target>
+</phrase>
+<phrase>
+ <source>italic</source>
+ <target>kursiv</target>
+</phrase>
+<phrase>
+ <source>label</source>
+ <target>etiket</target>
+</phrase>
+<phrase>
+ <source>landscape</source>
+ <target>liggende</target>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>sammenkæde</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>kæde</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>Link Here</source>
+ <target>Indsæt kæde her</target>
+</phrase>
+<phrase>
+ <source>list box</source>
+ <target>liste</target>
+</phrase>
+<phrase>
+ <source>list view</source>
+ <target>listevisningsboks</target>
+ <definition>control/kontrolelement</definition>
+</phrase>
+<phrase>
+ <source>manual link</source>
+ <target>manuel kæde</target>
+</phrase>
+<phrase>
+ <source>Maximize</source>
+ <target>Maksimer</target>
+</phrase>
+<phrase>
+ <source>maximize button</source>
+ <target>maksimeringsknap</target>
+</phrase>
+<phrase>
+ <source>MDI</source>
+ <target>MDI</target>
+</phrase>
+<phrase>
+ <source>menu</source>
+ <target>menu</target>
+</phrase>
+<phrase>
+ <source>menu bar</source>
+ <target>menulinje</target>
+</phrase>
+<phrase>
+ <source>menu button</source>
+ <target>menuknap</target>
+</phrase>
+<phrase>
+ <source>menu item</source>
+ <target>menupunkt</target>
+</phrase>
+<phrase>
+ <source>menu title</source>
+ <target>menutitel</target>
+</phrase>
+<phrase>
+ <source>message box</source>
+ <target>meddelelsesboks</target>
+</phrase>
+<phrase>
+ <source>Minimize</source>
+ <target>Minimer</target>
+</phrase>
+<phrase>
+ <source>minimize button</source>
+ <target>minimeringsknap</target>
+</phrase>
+<phrase>
+ <source>mixed-value</source>
+ <target>blandet værdi</target>
+</phrase>
+<phrase>
+ <source>modal</source>
+ <target>modal</target>
+</phrase>
+<phrase>
+ <source>mode</source>
+ <target>tilstand</target>
+</phrase>
+<phrase>
+ <source>modeless</source>
+ <target>ikke-modal</target>
+</phrase>
+<phrase>
+ <source>modifier key</source>
+ <target>ændringstast</target>
+</phrase>
+<phrase>
+ <source>mouse</source>
+ <target>mus</target>
+</phrase>
+<phrase>
+ <source>Move</source>
+ <target>Flyt</target>
+</phrase>
+<phrase>
+ <source>Move Here</source>
+ <target>Flyt hertil</target>
+</phrase>
+<phrase>
+ <source>Multiple Document Interface</source>
+ <target>multiple document interface</target>
+ <definition>MDI</definition>
+</phrase>
+<phrase>
+ <source>multiple selection list box</source>
+ <target>liste</target>
+ <definition>der tillader flere markeringer</definition>
+</phrase>
+<phrase>
+ <source>My Computer</source>
+ <target>Denne computer</target>
+ <definition>icon/ikon</definition>
+</phrase>
+<phrase>
+ <source>Network Neighborhood</source>
+ <target>Andre computere</target>
+ <definition>icon/ikon</definition>
+</phrase>
+<phrase>
+ <source>New</source>
+ <target>Ny</target>
+</phrase>
+<phrase>
+ <source>Next</source>
+ <target>Næste</target>
+</phrase>
+<phrase>
+ <source>object</source>
+ <target>objekt</target>
+</phrase>
+<phrase>
+ <source>OK</source>
+ <target>OK</target>
+</phrase>
+<phrase>
+ <source>OLE</source>
+ <target>OLE</target>
+</phrase>
+<phrase>
+ <source>OLE drag and drop</source>
+ <target>OLE-træk og slip</target>
+</phrase>
+<phrase>
+ <source>OLE embedded object</source>
+ <target>integreret OLE-objekt</target>
+</phrase>
+<phrase>
+ <source>OLE linked object</source>
+ <target>sammenkædet OLE-objekt</target>
+</phrase>
+<phrase>
+ <source>OLE nondefault drag and drop</source>
+ <target>interaktiv OLE-træk og slip</target>
+</phrase>
+<phrase>
+ <source>Open</source>
+ <target>Åbn</target>
+</phrase>
+<phrase>
+ <source>Open With</source>
+ <target>Åbn med</target>
+</phrase>
+<phrase>
+ <source>option button</source>
+ <target>alternativknap</target>
+</phrase>
+<phrase>
+ <source>option-set</source>
+ <target>aktiveret indstilling</target>
+</phrase>
+<phrase>
+ <source>package</source>
+ <target>objektpakke</target>
+</phrase>
+<phrase>
+ <source>Page Setup</source>
+ <target>Sideopsætning</target>
+</phrase>
+<phrase>
+ <source>palette window</source>
+ <target>paletvindue</target>
+</phrase>
+<phrase>
+ <source>pane</source>
+ <target>rude</target>
+</phrase>
+<phrase>
+ <source>parent window</source>
+ <target>overordnet vindue</target>
+</phrase>
+<phrase>
+ <source>password</source>
+ <target>adgangskode</target>
+</phrase>
+<phrase>
+ <source>Paste</source>
+ <target>Sæt ind</target>
+</phrase>
+<phrase>
+ <source>Paste Link</source>
+ <target>Indsæt kæde</target>
+</phrase>
+<phrase>
+ <source>Paste Shortcut</source>
+ <target>Indsæt genvej</target>
+</phrase>
+<phrase>
+ <source>Paste Special</source>
+ <target>Indsæt speciel</target>
+</phrase>
+<phrase>
+ <source>path</source>
+ <target>sti</target>
+</phrase>
+<phrase>
+ <source>Pause</source>
+ <target>Pause</target>
+</phrase>
+<phrase>
+ <source>Play</source>
+ <target>Afspil</target>
+</phrase>
+<phrase>
+ <source>Plug and Play</source>
+ <target>Plug and Play</target>
+</phrase>
+<phrase>
+ <source>point</source>
+ <target>punkt</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>point</source>
+ <target>pege</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>pointer</source>
+ <target>markør</target>
+</phrase>
+<phrase>
+ <source>pop-up menu</source>
+ <target>pop-up-menu</target>
+</phrase>
+<phrase>
+ <source>pop-up window</source>
+ <target>pop-up-vindue</target>
+</phrase>
+<phrase>
+ <source>portrait</source>
+ <target>stående</target>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>trykke på</target>
+ <definition>and hold a mouse button/og holde en museknap nede</definition>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>trykke på</target>
+ <definition>a key/en tast</definition>
+</phrase>
+<phrase>
+ <source>primary container</source>
+ <target>primært destinationsobjekt</target>
+</phrase>
+<phrase>
+ <source>primary window</source>
+ <target>primært vindue</target>
+</phrase>
+<phrase>
+ <source>Print</source>
+ <target>Udskriv</target>
+</phrase>
+<phrase>
+ <source>printer</source>
+ <target>printer</target>
+</phrase>
+<phrase>
+ <source>progress indicator</source>
+ <target>statusindikator</target>
+ <definition>control/kontrolelement</definition>
+</phrase>
+<phrase>
+ <source>project</source>
+ <target>projekt</target>
+</phrase>
+<phrase>
+ <source>Properties</source>
+ <target>Egenskaber</target>
+</phrase>
+<phrase>
+ <source>property inspector</source>
+ <target>egenskabsfremviser</target>
+</phrase>
+<phrase>
+ <source>property page</source>
+ <target>egenskabsside</target>
+</phrase>
+<phrase>
+ <source>property sheet</source>
+ <target>egenskabsark</target>
+</phrase>
+<phrase>
+ <source>property sheet control</source>
+ <target>kontrolelement på egenskabsark</target>
+</phrase>
+<phrase>
+ <source>Quick View</source>
+ <target>Hurtig visning</target>
+</phrase>
+<phrase>
+ <source>read-only</source>
+ <target>skrivebeskyttet</target>
+</phrase>
+<phrase>
+ <source>Recycle Bin</source>
+ <target>Papirkurv</target>
+ <definition>Icon/ikon</definition>
+</phrase>
+<phrase>
+ <source>Redo</source>
+ <target>Annuller Fortryd</target>
+</phrase>
+<phrase>
+ <source>region selection</source>
+ <target>områdemarkering</target>
+</phrase>
+<phrase>
+ <source>registry</source>
+ <target>registreringsdatabase</target>
+</phrase>
+<phrase>
+ <source>Repeat</source>
+ <target>Gentag</target>
+</phrase>
+<phrase>
+ <source>Replace</source>
+ <target>Erstat</target>
+</phrase>
+<phrase>
+ <source>Restore</source>
+ <target>Gendan</target>
+</phrase>
+<phrase>
+ <source>Restore button</source>
+ <target>gendannelsesknap</target>
+</phrase>
+<phrase>
+ <source>Resume</source>
+ <target>Fortsæt</target>
+</phrase>
+<phrase>
+ <source>Retry</source>
+ <target>Forsøg igen</target>
+</phrase>
+<phrase>
+ <source>rich-text box</source>
+ <target>tekstboks til formateret tekst</target>
+</phrase>
+<phrase>
+ <source>Run</source>
+ <target>Kør</target>
+</phrase>
+<phrase>
+ <source>Save</source>
+ <target>Gem</target>
+</phrase>
+<phrase>
+ <source>Save as</source>
+ <target>Gem som</target>
+</phrase>
+<phrase>
+ <source>scroll</source>
+ <target>rulle</target>
+</phrase>
+<phrase>
+ <source>scroll arrow</source>
+ <target>rullepil</target>
+</phrase>
+<phrase>
+ <source>scroll bar</source>
+ <target>rullepanel</target>
+</phrase>
+<phrase>
+ <source>scroll box</source>
+ <target>rulleboks</target>
+</phrase>
+<phrase>
+ <source>secondary window</source>
+ <target>sekundært vindue</target>
+</phrase>
+<phrase>
+ <source>select</source>
+ <target>markere</target>
+</phrase>
+<phrase>
+ <source>select</source>
+ <target>vælge</target>
+</phrase>
+<phrase>
+ <source>Select All</source>
+ <target>Marker alt</target>
+</phrase>
+<phrase>
+ <source>selection</source>
+ <target>det markerede</target>
+</phrase>
+<phrase>
+ <source>selection handle</source>
+ <target>markeringshåndtag</target>
+</phrase>
+<phrase>
+ <source>Send To</source>
+ <target>Overfør til</target>
+</phrase>
+<phrase>
+ <source>separator</source>
+ <target>separator</target>
+</phrase>
+<phrase>
+ <source>Settings</source>
+ <target>Indstillinger</target>
+</phrase>
+<phrase>
+ <source>Setup</source>
+ <target>Indstillinger</target>
+</phrase>
+<phrase>
+ <source>Setup</source>
+ <target>Installation</target>
+</phrase>
+<phrase>
+ <source>shortcut</source>
+ <target>genvej</target>
+</phrase>
+<phrase>
+ <source>shortcut button</source>
+ <target>genvejsknap</target>
+</phrase>
+<phrase>
+ <source>shortcut icon</source>
+ <target>genvejsikon</target>
+</phrase>
+<phrase>
+ <source>shortcut key</source>
+ <target>genvejstast</target>
+</phrase>
+<phrase>
+ <source>shortcut key control</source>
+ <target>kontrolelement til genvejstast</target>
+</phrase>
+<phrase>
+ <source>Show</source>
+ <target>Vis</target>
+</phrase>
+<phrase>
+ <source>Shutdown</source>
+ <target>Luk computeren</target>
+</phrase>
+<phrase>
+ <source>single selection list box</source>
+ <target>liste</target>
+ <definition>der kun tillader en enkelt markering</definition>
+</phrase>
+<phrase>
+ <source>Size</source>
+ <target>Tilpas størrelse</target>
+</phrase>
+<phrase>
+ <source>size grip</source>
+ <target>størrelseshåndtag</target>
+</phrase>
+<phrase>
+ <source>slider</source>
+ <target>skala</target>
+</phrase>
+<phrase>
+ <source>spin box</source>
+ <target>rotationsboks</target>
+</phrase>
+<phrase>
+ <source>Split</source>
+ <target>Opdel</target>
+</phrase>
+<phrase>
+ <source>split bar</source>
+ <target>delelinje</target>
+</phrase>
+<phrase>
+ <source>split box</source>
+ <target>deleboks</target>
+</phrase>
+<phrase>
+ <source>Start button</source>
+ <target>startknap</target>
+</phrase>
+<phrase>
+ <source>StartUp folder</source>
+ <target>Startmappe</target>
+</phrase>
+<phrase>
+ <source>status bar</source>
+ <target>statuslinje</target>
+</phrase>
+<phrase>
+ <source>Stop</source>
+ <target>Stop</target>
+</phrase>
+<phrase>
+ <source>tab control</source>
+ <target>fane</target>
+</phrase>
+<phrase>
+ <source>task bar</source>
+ <target>proceslinje</target>
+</phrase>
+<phrase>
+ <source>task-oriented Help</source>
+ <target>opgaveafhængig Hjælp</target>
+</phrase>
+<phrase>
+ <source>template</source>
+ <target>skabelon</target>
+</phrase>
+<phrase>
+ <source>text box</source>
+ <target>tekstboks</target>
+</phrase>
+<phrase>
+ <source>title bar</source>
+ <target>titellinje</target>
+</phrase>
+<phrase>
+ <source>title text</source>
+ <target>titeltekst</target>
+</phrase>
+<phrase>
+ <source>toggle key</source>
+ <target>til/fra-tast</target>
+</phrase>
+<phrase>
+ <source>toolbar</source>
+ <target>verktøjslinje</target>
+</phrase>
+<phrase>
+ <source>tooltip</source>
+ <target>verktøjstip</target>
+</phrase>
+<phrase>
+ <source>tree view control</source>
+ <target>træstruktur</target>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>skrive</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>type</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>unavailable</source>
+ <target>ikke tilgængelig</target>
+</phrase>
+<phrase>
+ <source>Undo</source>
+ <target>Fortryd</target>
+</phrase>
+<phrase>
+ <source>Uninstall</source>
+ <target>Fjern installationen</target>
+</phrase>
+<phrase>
+ <source>View</source>
+ <target>Menuen Vis</target>
+</phrase>
+<phrase>
+ <source>visual editing</source>
+ <target>lokal redigering</target>
+</phrase>
+<phrase>
+ <source>well control</source>
+ <target>grafikpalet</target>
+</phrase>
+<phrase>
+ <source>What&apos;s This?</source>
+ <target>Hvad er det?</target>
+</phrase>
+<phrase>
+ <source>Window</source>
+ <target>Menuen Vindue</target>
+</phrase>
+<phrase>
+ <source>window</source>
+ <target>vindue</target>
+</phrase>
+<phrase>
+ <source>Windows Explorer</source>
+ <target>Windows Stifinder</target>
+</phrase>
+<phrase>
+ <source>wizard</source>
+ <target>guide</target>
+</phrase>
+<phrase>
+ <source>workbook</source>
+ <target>projektmappe</target>
+</phrase>
+<phrase>
+ <source>workgroup</source>
+ <target>arbejdsgruppe</target>
+</phrase>
+<phrase>
+ <source>workspace</source>
+ <target>arbejdsområde</target>
+</phrase>
+<phrase>
+ <source>Yes</source>
+ <target>Ja</target>
+</phrase>
+</QPH>
diff --git a/src/linguist/phrasebooks/dutch.qph b/src/linguist/phrasebooks/dutch.qph
new file mode 100644
index 000000000..f2db4e05f
--- /dev/null
+++ b/src/linguist/phrasebooks/dutch.qph
@@ -0,0 +1,1044 @@
+<!DOCTYPE QPH><QPH language="nl">
+<phrase>
+ <source>About</source>
+ <target>Info</target>
+</phrase>
+<phrase>
+ <source>access key</source>
+ <target>toegangstoets</target>
+</phrase>
+<phrase>
+ <source>accessibility</source>
+ <target>toegankelijkheid</target>
+</phrase>
+<phrase>
+ <source>action handle</source>
+ <target>bewerkingsgreep</target>
+</phrase>
+<phrase>
+ <source>active</source>
+ <target>actief</target>
+</phrase>
+<phrase>
+ <source>active end</source>
+ <target>selecie-einde</target>
+</phrase>
+<phrase>
+ <source>active object</source>
+ <target>actief object</target>
+</phrase>
+<phrase>
+ <source>active window</source>
+ <target>actief venster</target>
+</phrase>
+<phrase>
+ <source>adornment</source>
+ <target>grafisch hulpmiddel</target>
+</phrase>
+<phrase>
+ <source>Always on Top</source>
+ <target>Altijd op voorgrond</target>
+</phrase>
+<phrase>
+ <source>anchor point</source>
+ <target>fixeerpunt</target>
+</phrase>
+<phrase>
+ <source>Apply</source>
+ <target>Toepassen</target>
+</phrase>
+<phrase>
+ <source>auto-exit</source>
+ <target>automatisch verlaten</target>
+</phrase>
+<phrase>
+ <source>auto-repeat</source>
+ <target>zich automatisch herhalen</target>
+</phrase>
+<phrase>
+ <source>auto-repeat</source>
+ <target>automatisch herhalen</target>
+</phrase>
+<phrase>
+ <source>automatic link</source>
+ <target>automatische koppeling</target>
+</phrase>
+<phrase>
+ <source>automatic scrolling</source>
+ <target>automatisch schuiven</target>
+</phrase>
+<phrase>
+ <source>autoscroll</source>
+ <target>automatisch schuiven</target>
+</phrase>
+<phrase>
+ <source>Back</source>
+ <target>Vorige</target>
+ <definition>wanneer het een logisch paar vormt met Volgende</definition>
+</phrase>
+<phrase>
+ <source>Browse</source>
+ <target>Bladeren</target>
+</phrase>
+<phrase>
+ <source>Cancel</source>
+ <target>Annuleren</target>
+</phrase>
+<phrase>
+ <source>cascading menu</source>
+ <target>vervolgmenu</target>
+</phrase>
+<phrase>
+ <source>check box</source>
+ <target>selectievakje</target>
+</phrase>
+<phrase>
+ <source>check mark</source>
+ <target>vinkje</target>
+</phrase>
+<phrase>
+ <source>child window</source>
+ <target>subvenster</target>
+</phrase>
+<phrase>
+ <source>choose</source>
+ <target>kiezen</target>
+</phrase>
+<phrase>
+ <source>click</source>
+ <target>klikken</target>
+</phrase>
+<phrase>
+ <source>Clipboard</source>
+ <target>Klembord</target>
+</phrase>
+<phrase>
+ <source>Close</source>
+ <target>Sluiten</target>
+</phrase>
+<phrase>
+ <source>Close button</source>
+ <target>knop Sluiten</target>
+</phrase>
+<phrase>
+ <source>collapse</source>
+ <target>samenvouwen</target>
+ <definition>outline</definition>
+</phrase>
+<phrase>
+ <source>column heading</source>
+ <target>kolomnaam</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>combo box</source>
+ <target>keuzelijst met invoervak</target>
+</phrase>
+<phrase>
+ <source>command button</source>
+ <target>opdrachtknop</target>
+</phrase>
+<phrase>
+ <source>container</source>
+ <target>hoofdobject</target>
+</phrase>
+<phrase>
+ <source>context-sensitive Help</source>
+ <target>contextafhankelijke Help</target>
+</phrase>
+<phrase>
+ <source>contextual</source>
+ <target>contextafhankelijk</target>
+</phrase>
+<phrase>
+ <source>control</source>
+ <target>besturingselement</target>
+</phrase>
+<phrase>
+ <source>Copy</source>
+ <target>Kopiëren</target>
+</phrase>
+<phrase>
+ <source>Copy here</source>
+ <target>Hierheen kopiëren</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut</source>
+ <target>Snelkoppeling maken</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut Here</source>
+ <target>Hier snelkoppeling maken</target>
+</phrase>
+<phrase>
+ <source>Cut</source>
+ <target>Knippen</target>
+</phrase>
+<phrase>
+ <source>default</source>
+ <target>standaard</target>
+</phrase>
+<phrase>
+ <source>default button</source>
+ <target>standaardknop</target>
+</phrase>
+<phrase>
+ <source>Delete</source>
+ <target>Verwijderen</target>
+</phrase>
+<phrase>
+ <source>desktop</source>
+ <target>bureaublad</target>
+</phrase>
+<phrase>
+ <source>destination</source>
+ <target>doel</target>
+</phrase>
+<phrase>
+ <source>dialog box</source>
+ <target>dialoogvenster</target>
+</phrase>
+<phrase>
+ <source>disability</source>
+ <target>handicap</target>
+ <definition>voorzichtig in context, maar handicap niet verbloemen:VB: persoon met een handicap (beter dan gehandicapte)</definition>
+</phrase>
+<phrase>
+ <source>disjoint selection</source>
+ <target>niet-aaneengesloten selectie</target>
+</phrase>
+<phrase>
+ <source>dock</source>
+ <target>in werkbalkdok plaatsen</target>
+</phrase>
+<phrase>
+ <source>document</source>
+ <target>document</target>
+</phrase>
+<phrase>
+ <source>double-click</source>
+ <target>dubbelklikken</target>
+</phrase>
+<phrase>
+ <source>drag</source>
+ <target>slepen</target>
+</phrase>
+<phrase>
+ <source>drag-and-drop</source>
+ <target>slepen en neerzetten</target>
+</phrase>
+<phrase>
+ <source>drop-down combo box</source>
+ <target>vervolgkeuzelijst met invoervak</target>
+</phrase>
+<phrase>
+ <source>drop-down list box</source>
+ <target>vervolgkeuzelijst</target>
+</phrase>
+<phrase>
+ <source>drop-down menu</source>
+ <target>menu</target>
+</phrase>
+<phrase>
+ <source>Edit</source>
+ <target>Bewerken</target>
+</phrase>
+<phrase>
+ <source>Edit menu</source>
+ <target>menu Bewerken</target>
+</phrase>
+<phrase>
+ <source>ellipsis</source>
+ <target>puntjes</target>
+ <definition>(...)</definition>
+</phrase>
+<phrase>
+ <source>embedded object</source>
+ <target>ingesloten object</target>
+</phrase>
+<phrase>
+ <source>Exit</source>
+ <target>Afsluiten</target>
+</phrase>
+<phrase>
+ <source>expand</source>
+ <target>uitvouwen</target>
+ <definition>an outline</definition>
+</phrase>
+<phrase>
+ <source>Explore</source>
+ <target>Verkennen</target>
+</phrase>
+<phrase>
+ <source>extended selection</source>
+ <target>uitgebreide selectie</target>
+</phrase>
+<phrase>
+ <source>extended selection list box</source>
+ <target>keuzelijst met uitgebreide selectie</target>
+</phrase>
+<phrase>
+ <source>file</source>
+ <target>bestand</target>
+</phrase>
+<phrase>
+ <source>File menu</source>
+ <target>menu Bestand</target>
+</phrase>
+<phrase>
+ <source>Find</source>
+ <target>Zoeken</target>
+</phrase>
+<phrase>
+ <source>Find Next</source>
+ <target>Volgende zoeken</target>
+</phrase>
+<phrase>
+ <source>Find What</source>
+ <target>Zoeken naar</target>
+</phrase>
+<phrase>
+ <source>folder</source>
+ <target>map</target>
+</phrase>
+<phrase>
+ <source>font</source>
+ <target>lettertype</target>
+</phrase>
+<phrase>
+ <source>font size</source>
+ <target>tekengrootte</target>
+</phrase>
+<phrase>
+ <source>font style</source>
+ <target>tekenstijl</target>
+</phrase>
+<phrase>
+ <source>function key</source>
+ <target>functietoets</target>
+</phrase>
+<phrase>
+ <source>group box</source>
+ <target>groepsvak</target>
+</phrase>
+<phrase>
+ <source>handle</source>
+ <target>greep</target>
+</phrase>
+<phrase>
+ <source>Help</source>
+ <target>Help</target>
+</phrase>
+<phrase>
+ <source>Help menu</source>
+ <target>menu Help</target>
+</phrase>
+<phrase>
+ <source>Hide</source>
+ <target>Verbergen</target>
+</phrase>
+<phrase>
+ <source>hierarchical selection</source>
+ <target>hiërarchische selectie</target>
+</phrase>
+<phrase>
+ <source>hold</source>
+ <target>ingedrukt houden</target>
+</phrase>
+<phrase>
+ <source>hot spot</source>
+ <target>selectiepunt</target>
+</phrase>
+<phrase>
+ <source>hot zone</source>
+ <target>selectiegebied</target>
+</phrase>
+<phrase>
+ <source>icon</source>
+ <target>pictogram</target>
+</phrase>
+<phrase>
+ <source>inactive</source>
+ <target>niet-actief</target>
+</phrase>
+<phrase>
+ <source>inactive window</source>
+ <target>niet-actief venster</target>
+</phrase>
+<phrase>
+ <source>input focus</source>
+ <target>invoerfocus</target>
+</phrase>
+<phrase>
+ <source>Insert</source>
+ <target>menu Invoegen</target>
+</phrase>
+<phrase>
+ <source>Insert Object</source>
+ <target>Object Invoegen</target>
+</phrase>
+<phrase>
+ <source>insertion point</source>
+ <target>invoegpositie</target>
+</phrase>
+<phrase>
+ <source>italic</source>
+ <target>cursief</target>
+</phrase>
+<phrase>
+ <source>label</source>
+ <target>label</target>
+ <definition>gender, Masc.: de</definition>
+</phrase>
+<phrase>
+ <source>landscape</source>
+ <target>liggend</target>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>koppeling</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>koppelen</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>Link Here</source>
+ <target>Hier koppeling maken</target>
+</phrase>
+<phrase>
+ <source>list box</source>
+ <target>keuzelijst</target>
+</phrase>
+<phrase>
+ <source>list view</source>
+ <target>weergaveknoppen</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>manual link</source>
+ <target>handmatige koppeling</target>
+</phrase>
+<phrase>
+ <source>Maximize</source>
+ <target>Maximaliseren</target>
+</phrase>
+<phrase>
+ <source>maximize button</source>
+ <target>knop Maximaliseren</target>
+</phrase>
+<phrase>
+ <source>MDI</source>
+ <target>MDI</target>
+</phrase>
+<phrase>
+ <source>menu</source>
+ <target>menu</target>
+</phrase>
+<phrase>
+ <source>menu bar</source>
+ <target>menubalk</target>
+</phrase>
+<phrase>
+ <source>menu button</source>
+ <target>menuknop</target>
+</phrase>
+<phrase>
+ <source>menu item</source>
+ <target>opdracht</target>
+</phrase>
+<phrase>
+ <source>menu title</source>
+ <target>menunaam</target>
+</phrase>
+<phrase>
+ <source>message box</source>
+ <target>berichtvak</target>
+</phrase>
+<phrase>
+ <source>Minimize</source>
+ <target>Minimaliseren</target>
+</phrase>
+<phrase>
+ <source>minimize button</source>
+ <target>knop Minimaliseren</target>
+</phrase>
+<phrase>
+ <source>mixed-value</source>
+ <target>met gemengde waarden</target>
+</phrase>
+<phrase>
+ <source>modal</source>
+ <target>modusgebonden</target>
+</phrase>
+<phrase>
+ <source>mode</source>
+ <target>modi</target>
+ <definition>plural</definition>
+</phrase>
+<phrase>
+ <source>mode</source>
+ <target>modus</target>
+ <definition>singular</definition>
+</phrase>
+<phrase>
+ <source>modeless</source>
+ <target>niet-modusgebonden</target>
+</phrase>
+<phrase>
+ <source>modifier key</source>
+ <target>modificatietoets</target>
+</phrase>
+<phrase>
+ <source>mouse</source>
+ <target>muis</target>
+</phrase>
+<phrase>
+ <source>Move</source>
+ <target>Verplaatsen</target>
+</phrase>
+<phrase>
+ <source>Move Here</source>
+ <target>Hierheen verplaatsen</target>
+</phrase>
+<phrase>
+ <source>Multiple Document Interface</source>
+ <target>interface voor meerdere documenten</target>
+</phrase>
+<phrase>
+ <source>multiple selection list box</source>
+ <target>keuzelijst met meervoudige selectie</target>
+</phrase>
+<phrase>
+ <source>My Computer</source>
+ <target>Deze computer</target>
+ <definition>icon/pictogram</definition>
+</phrase>
+<phrase>
+ <source>Network Neighborhood</source>
+ <target>Netwerkomgeving</target>
+ <definition>icon/pictogram</definition>
+</phrase>
+<phrase>
+ <source>New</source>
+ <target>Nieuw</target>
+</phrase>
+<phrase>
+ <source>Next</source>
+ <target>Volgende</target>
+</phrase>
+<phrase>
+ <source>object</source>
+ <target>object</target>
+</phrase>
+<phrase>
+ <source>OK</source>
+ <target>OK</target>
+</phrase>
+<phrase>
+ <source>OLE</source>
+ <target>OLE</target>
+ <definition>OLE (objecten koppelen en insluiten): voluit alleen in doc en Help</definition>
+</phrase>
+<phrase>
+ <source>OLE drag and drop</source>
+ <target>slepen en neerzetten</target>
+</phrase>
+<phrase>
+ <source>OLE drag and drop</source>
+ <target>slepen en neerzetten via OLE</target>
+</phrase>
+<phrase>
+ <source>OLE embedded object</source>
+ <target>ingesloten OLE-object</target>
+</phrase>
+<phrase>
+ <source>OLE linked object</source>
+ <target>gekoppeld OLE-object</target>
+</phrase>
+<phrase>
+ <source>OLE nondefault drag and drop</source>
+ <target>Aangepast slepen en neerzetten</target>
+</phrase>
+<phrase>
+ <source>OLE nondefault drag and drop</source>
+ <target>Aangepast slepen en neerzetten via OLE</target>
+</phrase>
+<phrase>
+ <source>Open</source>
+ <target>Openen</target>
+</phrase>
+<phrase>
+ <source>Open With</source>
+ <target>Openen met</target>
+</phrase>
+<phrase>
+ <source>option button</source>
+ <target>keuzerondje</target>
+</phrase>
+<phrase>
+ <source>option-set</source>
+ <target>opties</target>
+</phrase>
+<phrase>
+ <source>package</source>
+ <target>OLE-pakket</target>
+</phrase>
+<phrase>
+ <source>Page Setup</source>
+ <target>Pagina-instelling</target>
+</phrase>
+<phrase>
+ <source>palette window</source>
+ <target>paletvenster</target>
+</phrase>
+<phrase>
+ <source>pane</source>
+ <target>deelvenster</target>
+</phrase>
+<phrase>
+ <source>parent window</source>
+ <target>hoofdvenster</target>
+</phrase>
+<phrase>
+ <source>password</source>
+ <target>wachtwoord</target>
+</phrase>
+<phrase>
+ <source>Paste</source>
+ <target>Plakken</target>
+</phrase>
+<phrase>
+ <source>Paste Link</source>
+ <target>Koppeling plakken</target>
+</phrase>
+<phrase>
+ <source>Paste Shortcut</source>
+ <target>Snelkoppeling plakken</target>
+</phrase>
+<phrase>
+ <source>Paste Special</source>
+ <target>Plakken speciaal</target>
+</phrase>
+<phrase>
+ <source>path</source>
+ <target>pad</target>
+</phrase>
+<phrase>
+ <source>Pause</source>
+ <target>Pauze</target>
+</phrase>
+<phrase>
+ <source>Play</source>
+ <target>Afspelen</target>
+</phrase>
+<phrase>
+ <source>Plug and Play</source>
+ <target>Plug en Play</target>
+</phrase>
+<phrase>
+ <source>point</source>
+ <target>aanwijzen</target>
+</phrase>
+<phrase>
+ <source>pointer</source>
+ <target>aanwijzer</target>
+</phrase>
+<phrase>
+ <source>pop-up menu</source>
+ <target>pop-up-menu</target>
+</phrase>
+<phrase>
+ <source>pop-up window</source>
+ <target>pop-up-venster</target>
+</phrase>
+<phrase>
+ <source>portrait</source>
+ <target>staand</target>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>drukken op</target>
+ <definition>a key/een toets</definition>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>ingedrukt houden</target>
+ <definition>and hold a mouse button/een muisknop</definition>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>een muisknop ingedrukt houden</target>
+ <definition>and hold a mouse button</definition>
+</phrase>
+<phrase>
+ <source>press a mouse botton</source>
+ <target>een muisknop indrukken </target>
+</phrase>
+<phrase>
+ <source>primary container</source>
+ <target>primair hoofdobject</target>
+</phrase>
+<phrase>
+ <source>primary window</source>
+ <target>primair venster</target>
+</phrase>
+<phrase>
+ <source>Print</source>
+ <target>Afdrukken</target>
+</phrase>
+<phrase>
+ <source>printer</source>
+ <target>printer</target>
+</phrase>
+<phrase>
+ <source>progress indicator</source>
+ <target>voortgangsindicator</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>project</source>
+ <target>project</target>
+</phrase>
+<phrase>
+ <source>Properties</source>
+ <target>Eigenschappen</target>
+</phrase>
+<phrase>
+ <source>property inspector</source>
+ <target>eigenschappenweergave</target>
+</phrase>
+<phrase>
+ <source>property page</source>
+ <target>eigenschappenpagina</target>
+</phrase>
+<phrase>
+ <source>property sheet</source>
+ <target>eigenschappenblad</target>
+</phrase>
+<phrase>
+ <source>property sheet control</source>
+ <target>besturingselement op een eigenschappenblad</target>
+</phrase>
+<phrase>
+ <source>Quick View</source>
+ <target>Snel weergeven</target>
+</phrase>
+<phrase>
+ <source>read-only</source>
+ <target>alleen-lezen</target>
+</phrase>
+<phrase>
+ <source>Recycle Bin</source>
+ <target>Prullenbak</target>
+ <definition>Icon/pictogram</definition>
+</phrase>
+<phrase>
+ <source>Redo</source>
+ <target>Opnieuw</target>
+</phrase>
+<phrase>
+ <source>region selection</source>
+ <target>gebiedsselectie</target>
+</phrase>
+<phrase>
+ <source>registry</source>
+ <target>het Register</target>
+</phrase>
+<phrase>
+ <source>Repeat</source>
+ <target>Herhalen</target>
+</phrase>
+<phrase>
+ <source>Replace</source>
+ <target>Vervangen</target>
+</phrase>
+<phrase>
+ <source>Restore</source>
+ <target>Vorig formaat</target>
+</phrase>
+<phrase>
+ <source>Restore button</source>
+ <target>knop Vorig formaat</target>
+</phrase>
+<phrase>
+ <source>Resume</source>
+ <target>Doorgaan</target>
+</phrase>
+<phrase>
+ <source>Retry</source>
+ <target>Nogmaals</target>
+</phrase>
+<phrase>
+ <source>rich-text box</source>
+ <target>RTF-vak</target>
+</phrase>
+<phrase>
+ <source>Run</source>
+ <target>Uitvoeren</target>
+ <definition>Maar: macro starten</definition>
+</phrase>
+<phrase>
+ <source>Save</source>
+ <target>Opslaan</target>
+</phrase>
+<phrase>
+ <source>Save as</source>
+ <target>Opslaan als</target>
+</phrase>
+<phrase>
+ <source>scroll</source>
+ <target>schuiven door</target>
+</phrase>
+<phrase>
+ <source>scroll arrow</source>
+ <target>schuifpijl</target>
+</phrase>
+<phrase>
+ <source>scroll bar</source>
+ <target>schuifbalk</target>
+</phrase>
+<phrase>
+ <source>scroll box</source>
+ <target>schuifblok</target>
+</phrase>
+<phrase>
+ <source>secondary window</source>
+ <target>secundair venster</target>
+</phrase>
+<phrase>
+ <source>select</source>
+ <target>selecteren</target>
+</phrase>
+<phrase>
+ <source>Select All</source>
+ <target>Alles selecteren</target>
+</phrase>
+<phrase>
+ <source>selection</source>
+ <target>selectie</target>
+</phrase>
+<phrase>
+ <source>selection handle</source>
+ <target>selectiegreep</target>
+</phrase>
+<phrase>
+ <source>Send To</source>
+ <target>Kopiëren naar</target>
+</phrase>
+<phrase>
+ <source>separator</source>
+ <target>scheidingsteken</target>
+</phrase>
+<phrase>
+ <source>Settings</source>
+ <target>Instellingen</target>
+</phrase>
+<phrase>
+ <source>Setup</source>
+ <target>Instellen</target>
+</phrase>
+<phrase>
+ <source>Setup</source>
+ <target>Setup</target>
+ <definition>als het programma Setup wordt bedoeld</definition>
+</phrase>
+<phrase>
+ <source>shortcut</source>
+ <target>snelkoppeling</target>
+</phrase>
+<phrase>
+ <source>shortcut button</source>
+ <target>snelkoppelingsknop</target>
+</phrase>
+<phrase>
+ <source>shortcut icon</source>
+ <target>snelkoppelingspictogram</target>
+</phrase>
+<phrase>
+ <source>shortcut key</source>
+ <target>sneltoets</target>
+</phrase>
+<phrase>
+ <source>shortcut key control</source>
+ <target>sneltoetsvak</target>
+</phrase>
+<phrase>
+ <source>Show</source>
+ <target>Weergeven</target>
+</phrase>
+<phrase>
+ <source>Shutdown</source>
+ <target>Afsluiten</target>
+</phrase>
+<phrase>
+ <source>single selection list box</source>
+ <target>keuzelijst met enkelvoudige selectie</target>
+</phrase>
+<phrase>
+ <source>Size</source>
+ <target>Formaat wijzigen</target>
+</phrase>
+<phrase>
+ <source>size grip</source>
+ <target>formaatgreep</target>
+</phrase>
+<phrase>
+ <source>slider</source>
+ <target>schuifregelaar</target>
+</phrase>
+<phrase>
+ <source>spin box</source>
+ <target>kringveld</target>
+</phrase>
+<phrase>
+ <source>Split</source>
+ <target>Splitsen</target>
+</phrase>
+<phrase>
+ <source>split bar</source>
+ <target>splitsbalk</target>
+</phrase>
+<phrase>
+ <source>split box</source>
+ <target>splitsblokje</target>
+</phrase>
+<phrase>
+ <source>Start button</source>
+ <target>knop Start</target>
+</phrase>
+<phrase>
+ <source>StartUp folder</source>
+ <target>map Opstarten</target>
+</phrase>
+<phrase>
+ <source>status bar</source>
+ <target>statusbalk</target>
+</phrase>
+<phrase>
+ <source>Stop</source>
+ <target>Stoppen</target>
+</phrase>
+<phrase>
+ <source>tab control</source>
+ <target>tab</target>
+</phrase>
+<phrase>
+ <source>task bar</source>
+ <target>taakbalk</target>
+</phrase>
+<phrase>
+ <source>task-oriented Help</source>
+ <target>taakgeoriënteerde Help</target>
+</phrase>
+<phrase>
+ <source>template</source>
+ <target>sjabloon</target>
+ <definition>de</definition>
+</phrase>
+<phrase>
+ <source>text box</source>
+ <target>tekstvak</target>
+</phrase>
+<phrase>
+ <source>title bar</source>
+ <target>titelbalk</target>
+</phrase>
+<phrase>
+ <source>title text</source>
+ <target>venstertitel</target>
+</phrase>
+<phrase>
+ <source>toggle key</source>
+ <target>wisseltoets</target>
+</phrase>
+<phrase>
+ <source>toolbar</source>
+ <target>werkbalk</target>
+</phrase>
+<phrase>
+ <source>tooltip</source>
+ <target>knopinfo</target>
+</phrase>
+<phrase>
+ <source>tree view control</source>
+ <target>besturingselement voor structuurweergave</target>
+</phrase>
+<phrase>
+ <source>tree view control</source>
+ <target>structuurweergave</target>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>typen</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>type</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>unavailable</source>
+ <target>niet beschikbaar</target>
+</phrase>
+<phrase>
+ <source>Undo</source>
+ <target>Ongedaan maken</target>
+</phrase>
+<phrase>
+ <source>Uninstall</source>
+ <target>Installatie ongedaan maken</target>
+</phrase>
+<phrase>
+ <source>View</source>
+ <target>menu Beeld</target>
+</phrase>
+<phrase>
+ <source>visual editing</source>
+ <target>direct bewerken</target>
+</phrase>
+<phrase>
+ <source>well control</source>
+ <target>keuzelijst met grafische opties</target>
+</phrase>
+<phrase>
+ <source>What&apos;s This?</source>
+ <target>Wat is dit?</target>
+</phrase>
+<phrase>
+ <source>Window</source>
+ <target>menu Venster</target>
+</phrase>
+<phrase>
+ <source>window</source>
+ <target>venster</target>
+</phrase>
+<phrase>
+ <source>Windows Explorer</source>
+ <target>Windows Verkenner</target>
+</phrase>
+<phrase>
+ <source>wizard</source>
+ <target>wizard</target>
+ <definition>geen hoofdletter meer in lopende tekst</definition>
+</phrase>
+<phrase>
+ <source>workbook</source>
+ <target>werkmap</target>
+</phrase>
+<phrase>
+ <source>workgroup</source>
+ <target>werkgroep</target>
+</phrase>
+<phrase>
+ <source>workspace</source>
+ <target>werkruimte</target>
+</phrase>
+<phrase>
+ <source>Yes</source>
+ <target>Ja</target>
+</phrase>
+</QPH>
diff --git a/src/linguist/phrasebooks/finnish.qph b/src/linguist/phrasebooks/finnish.qph
new file mode 100644
index 000000000..caef1972f
--- /dev/null
+++ b/src/linguist/phrasebooks/finnish.qph
@@ -0,0 +1,1033 @@
+<!DOCTYPE QPH><QPH language="fi">
+<phrase>
+ <source>About</source>
+ <target>Tietoja</target>
+</phrase>
+<phrase>
+ <source>access key</source>
+ <target>valintanäppäin</target>
+</phrase>
+<phrase>
+ <source>accessibility</source>
+ <target>helppokäyttötoiminto</target>
+</phrase>
+<phrase>
+ <source>action handle</source>
+ <target>toimintokahva</target>
+</phrase>
+<phrase>
+ <source>active</source>
+ <target>aktiivinen</target>
+</phrase>
+<phrase>
+ <source>active end</source>
+ <target>valinnan aktiivinen päätöskohta</target>
+</phrase>
+<phrase>
+ <source>active object</source>
+ <target>aktiivinen objekti</target>
+</phrase>
+<phrase>
+ <source>active window</source>
+ <target>aktiivinen ikkuna</target>
+</phrase>
+<phrase>
+ <source>adornment</source>
+ <target>graafinen lisäke</target>
+</phrase>
+<phrase>
+ <source>Always on Top</source>
+ <target>Aina päällimmäisenä</target>
+</phrase>
+<phrase>
+ <source>anchor point</source>
+ <target>ankkurikohta</target>
+</phrase>
+<phrase>
+ <source>Apply</source>
+ <target>Käytä</target>
+</phrase>
+<phrase>
+ <source>auto-exit</source>
+ <target>automaattinen siirtyminen</target>
+</phrase>
+<phrase>
+ <source>auto-repeat</source>
+ <target>automaattinen toisto</target>
+</phrase>
+<phrase>
+ <source>automatic link</source>
+ <target>automaattinen linkki</target>
+</phrase>
+<phrase>
+ <source>automatic scrolling</source>
+ <target>automaattinen vieritys</target>
+</phrase>
+<phrase>
+ <source>autoscroll</source>
+ <target>automaattinen vieritys</target>
+</phrase>
+<phrase>
+ <source>Back</source>
+ <target>Takaisin</target>
+</phrase>
+<phrase>
+ <source>Browse</source>
+ <target>Selaa</target>
+</phrase>
+<phrase>
+ <source>Cancel</source>
+ <target>Peruuta</target>
+</phrase>
+<phrase>
+ <source>cascading menu</source>
+ <target>alivalikko</target>
+</phrase>
+<phrase>
+ <source>check box</source>
+ <target>valintaruutu</target>
+</phrase>
+<phrase>
+ <source>check mark</source>
+ <target>valintamerkki</target>
+</phrase>
+<phrase>
+ <source>child window</source>
+ <target>ali-ikkuna</target>
+</phrase>
+<phrase>
+ <source>choose</source>
+ <target>valita</target>
+</phrase>
+<phrase>
+ <source>click</source>
+ <target>napsautta</target>
+</phrase>
+<phrase>
+ <source>Clipboard</source>
+ <target>Leikepöytä</target>
+</phrase>
+<phrase>
+ <source>Close</source>
+ <target>Sulje</target>
+</phrase>
+<phrase>
+ <source>Close button</source>
+ <target>sulkemispainike</target>
+</phrase>
+<phrase>
+ <source>collapse</source>
+ <target>tiivistää</target>
+ <definition>outline/jäsennys</definition>
+</phrase>
+<phrase>
+ <source>collapse</source>
+ <target>kutistaa</target>
+ <definition>outline/jäsennys</definition>
+</phrase>
+<phrase>
+ <source>column heading</source>
+ <target>saraketunnus</target>
+ <definition>control/ohjausobjekti</definition>
+</phrase>
+<phrase>
+ <source>column heading</source>
+ <target>sarakeotsikko</target>
+ <definition>control/ohjausobjekti</definition>
+</phrase>
+<phrase>
+ <source>combo box</source>
+ <target>yhdistelmäruutu</target>
+</phrase>
+<phrase>
+ <source>command button</source>
+ <target>painike</target>
+</phrase>
+<phrase>
+ <source>command button</source>
+ <target>komento painike</target>
+</phrase>
+<phrase>
+ <source>container</source>
+ <target>säilö</target>
+</phrase>
+<phrase>
+ <source>context-sensitive Help</source>
+ <target>tilannekohtainen ohje</target>
+</phrase>
+<phrase>
+ <source>contextual</source>
+ <target>tilannekohtainen</target>
+</phrase>
+<phrase>
+ <source>control</source>
+ <target>ohjausobjekti</target>
+</phrase>
+<phrase>
+ <source>Copy</source>
+ <target>Kopioi</target>
+</phrase>
+<phrase>
+ <source>Copy here</source>
+ <target>Kopioi tähän</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut</source>
+ <target>Luo pikakuvake</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut Here</source>
+ <target>Luo pikakuvake tähän</target>
+</phrase>
+<phrase>
+ <source>Cut</source>
+ <target>Leikkaa</target>
+</phrase>
+<phrase>
+ <source>default</source>
+ <target>oletus</target>
+</phrase>
+<phrase>
+ <source>default button</source>
+ <target>oletuspainike</target>
+</phrase>
+<phrase>
+ <source>Delete</source>
+ <target>Poista</target>
+</phrase>
+<phrase>
+ <source>desktop</source>
+ <target>työpöytä</target>
+</phrase>
+<phrase>
+ <source>destination</source>
+ <target>kohde</target>
+</phrase>
+<phrase>
+ <source>dialog box</source>
+ <target>valintaikkuna</target>
+</phrase>
+<phrase>
+ <source>disability</source>
+ <target>invaliditeetti</target>
+</phrase>
+<phrase>
+ <source>disjoint selection</source>
+ <target>hajavalinta</target>
+</phrase>
+<phrase>
+ <source>dock</source>
+ <target>telakoida</target>
+</phrase>
+<phrase>
+ <source>document</source>
+ <target>tiedosto</target>
+</phrase>
+<phrase>
+ <source>document</source>
+ <target>asiakirja</target>
+</phrase>
+<phrase>
+ <source>double-click</source>
+ <target>kaksoisnapsauttaa</target>
+</phrase>
+<phrase>
+ <source>drag</source>
+ <target>vetää</target>
+</phrase>
+<phrase>
+ <source>drag-and-drop</source>
+ <target>vetää ja pudottaa</target>
+</phrase>
+<phrase>
+ <source>drop-down combo box</source>
+ <target>avattava yhdistelmäruutu</target>
+</phrase>
+<phrase>
+ <source>drop-down list box</source>
+ <target>avattava luetteloruutu</target>
+</phrase>
+<phrase>
+ <source>drop-down menu</source>
+ <target>avattava valikko</target>
+</phrase>
+<phrase>
+ <source>Edit</source>
+ <target>Muokkaa</target>
+</phrase>
+<phrase>
+ <source>Edit menu</source>
+ <target>Muokkaa-valikko</target>
+</phrase>
+<phrase>
+ <source>ellipsis</source>
+ <target>kolme pistettä</target>
+</phrase>
+<phrase>
+ <source>embedded object</source>
+ <target>upotettu objekti</target>
+</phrase>
+<phrase>
+ <source>Exit</source>
+ <target>Lopeta</target>
+</phrase>
+<phrase>
+ <source>expand</source>
+ <target>laajentaa</target>
+ <definition>an outline/jäsennys</definition>
+</phrase>
+<phrase>
+ <source>Explore</source>
+ <target>Selaa</target>
+</phrase>
+<phrase>
+ <source>extended selection</source>
+ <target>laajennettu valinta</target>
+</phrase>
+<phrase>
+ <source>extended selection list box</source>
+ <target>laajennettu valinta-luetteloruutu</target>
+</phrase>
+<phrase>
+ <source>file</source>
+ <target>tiedosto</target>
+</phrase>
+<phrase>
+ <source>File menu</source>
+ <target>Tiedosto-valikko</target>
+</phrase>
+<phrase>
+ <source>Find</source>
+ <target>Etsi</target>
+</phrase>
+<phrase>
+ <source>Find Next</source>
+ <target>Etsi seuraava</target>
+</phrase>
+<phrase>
+ <source>Find What</source>
+ <target>Etsittävä</target>
+</phrase>
+<phrase>
+ <source>folder</source>
+ <target>kansio</target>
+</phrase>
+<phrase>
+ <source>font</source>
+ <target>fontti</target>
+</phrase>
+<phrase>
+ <source>font size</source>
+ <target>fonttikoko</target>
+</phrase>
+<phrase>
+ <source>font style</source>
+ <target>fonttityyli</target>
+</phrase>
+<phrase>
+ <source>function key</source>
+ <target>funktionäppäin</target>
+</phrase>
+<phrase>
+ <source>group box</source>
+ <target>ryhmän kehys</target>
+</phrase>
+<phrase>
+ <source>handle</source>
+ <target>kahva</target>
+</phrase>
+<phrase>
+ <source>Help</source>
+ <target>Ohje</target>
+</phrase>
+<phrase>
+ <source>Help menu</source>
+ <target>Ohje-valikko</target>
+</phrase>
+<phrase>
+ <source>Hide</source>
+ <target>Piilota</target>
+</phrase>
+<phrase>
+ <source>hierarchical selection</source>
+ <target>hierarkkinen valinta</target>
+</phrase>
+<phrase>
+ <source>hold</source>
+ <target>pitää painettuna</target>
+</phrase>
+<phrase>
+ <source>hot spot</source>
+ <target>kohdepiste</target>
+</phrase>
+<phrase>
+ <source>hot zone</source>
+ <target>kohdealue</target>
+</phrase>
+<phrase>
+ <source>icon</source>
+ <target>kuvake</target>
+</phrase>
+<phrase>
+ <source>inactive</source>
+ <target>passiivinen</target>
+</phrase>
+<phrase>
+ <source>inactive window</source>
+ <target>passiivinen ikkuna</target>
+</phrase>
+<phrase>
+ <source>input focus</source>
+ <target>syöttöalue</target>
+</phrase>
+<phrase>
+ <source>Insert</source>
+ <target>Lisää-valikko</target>
+</phrase>
+<phrase>
+ <source>Insert Object</source>
+ <target>Lisää objekti</target>
+</phrase>
+<phrase>
+ <source>insertion point</source>
+ <target>lisäyskohta</target>
+</phrase>
+<phrase>
+ <source>italic</source>
+ <target>kursivoitu</target>
+</phrase>
+<phrase>
+ <source>label</source>
+ <target>otsikko</target>
+</phrase>
+<phrase>
+ <source>label</source>
+ <target>nimi</target>
+</phrase>
+<phrase>
+ <source>landscape</source>
+ <target>vaaka</target>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>linkki</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>linkittää</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>Link Here</source>
+ <target>Linkitä tähän</target>
+</phrase>
+<phrase>
+ <source>list box</source>
+ <target>luetteloruutu</target>
+</phrase>
+<phrase>
+ <source>list view</source>
+ <target>luettelonäyttö</target>
+ <definition>control/ohjausobjekti</definition>
+</phrase>
+<phrase>
+ <source>manual link</source>
+ <target>manuaalinen linkki</target>
+</phrase>
+<phrase>
+ <source>Maximize</source>
+ <target>Suurenna</target>
+</phrase>
+<phrase>
+ <source>maximize button</source>
+ <target>suurennuspainike</target>
+</phrase>
+<phrase>
+ <source>MDI</source>
+ <target>MDI</target>
+</phrase>
+<phrase>
+ <source>menu</source>
+ <target>valikko</target>
+</phrase>
+<phrase>
+ <source>menu bar</source>
+ <target>valikkorivi</target>
+</phrase>
+<phrase>
+ <source>menu button</source>
+ <target>valikkopainike</target>
+</phrase>
+<phrase>
+ <source>menu item</source>
+ <target>valikon vaihtoehto</target>
+</phrase>
+<phrase>
+ <source>menu title</source>
+ <target>valikon otsikko</target>
+</phrase>
+<phrase>
+ <source>message box</source>
+ <target>sanomaruutu</target>
+</phrase>
+<phrase>
+ <source>Minimize</source>
+ <target>Pienennä</target>
+</phrase>
+<phrase>
+ <source>minimize button</source>
+ <target>pienennyspainike</target>
+</phrase>
+<phrase>
+ <source>mixed-value</source>
+ <target>monitila</target>
+</phrase>
+<phrase>
+ <source>modal</source>
+ <target>modaalinen</target>
+</phrase>
+<phrase>
+ <source>mode</source>
+ <target>tila</target>
+</phrase>
+<phrase>
+ <source>modeless</source>
+ <target>ei-modaalinen</target>
+</phrase>
+<phrase>
+ <source>modifier key</source>
+ <target>yhdistelmänäppäin</target>
+</phrase>
+<phrase>
+ <source>mouse</source>
+ <target>hiiri</target>
+</phrase>
+<phrase>
+ <source>Move</source>
+ <target>Siirrä</target>
+</phrase>
+<phrase>
+ <source>Move Here</source>
+ <target>Siirrä tähän</target>
+</phrase>
+<phrase>
+ <source>Multiple Document Interface</source>
+ <target>MDI-liittymä</target>
+</phrase>
+<phrase>
+ <source>multiple selection list box</source>
+ <target>monivalintainen luetteloruutu</target>
+</phrase>
+<phrase>
+ <source>My Computer</source>
+ <target>Oma tietokone</target>
+ <definition>icon/kuvake</definition>
+</phrase>
+<phrase>
+ <source>Network Neighborhood</source>
+ <target>Verkkoympäristö</target>
+ <definition>icon/kuvake</definition>
+</phrase>
+<phrase>
+ <source>New</source>
+ <target>Uusi</target>
+</phrase>
+<phrase>
+ <source>Next</source>
+ <target>Seuraava</target>
+</phrase>
+<phrase>
+ <source>object</source>
+ <target>objekti</target>
+</phrase>
+<phrase>
+ <source>OK</source>
+ <target>OK</target>
+</phrase>
+<phrase>
+ <source>OLE</source>
+ <target>OLE</target>
+</phrase>
+<phrase>
+ <source>OLE drag and drop</source>
+ <target>vedä ja pudota-OLE-toiminto</target>
+</phrase>
+<phrase>
+ <source>OLE embedded object</source>
+ <target>upotettu OLE-objekti</target>
+</phrase>
+<phrase>
+ <source>OLE linked object</source>
+ <target>linkitetty OLE-objekti</target>
+</phrase>
+<phrase>
+ <source>OLE nondefault drag and drop</source>
+ <target>käyttäjän määrittämä vedä ja pudota-OLE-toiminto</target>
+</phrase>
+<phrase>
+ <source>Open</source>
+ <target>Avaa</target>
+</phrase>
+<phrase>
+ <source>Open With</source>
+ <target>Avaa sovelluksessa</target>
+</phrase>
+<phrase>
+ <source>option button</source>
+ <target>valintanappi</target>
+</phrase>
+<phrase>
+ <source>option-set</source>
+ <target>valitsimen tila</target>
+</phrase>
+<phrase>
+ <source>package</source>
+ <target>pakkaus</target>
+</phrase>
+<phrase>
+ <source>Page Setup</source>
+ <target>Sivun asetukset</target>
+</phrase>
+<phrase>
+ <source>palette window</source>
+ <target>valikoimaikkuna</target>
+</phrase>
+<phrase>
+ <source>pane</source>
+ <target>ruutu</target>
+</phrase>
+<phrase>
+ <source>parent window</source>
+ <target>ylemmän tason ikkuna</target>
+</phrase>
+<phrase>
+ <source>password</source>
+ <target>salasana</target>
+</phrase>
+<phrase>
+ <source>Paste</source>
+ <target>Liitä</target>
+</phrase>
+<phrase>
+ <source>Paste Link</source>
+ <target>Liitä linkki</target>
+</phrase>
+<phrase>
+ <source>Paste Shortcut</source>
+ <target>Liitä pikakuvake</target>
+</phrase>
+<phrase>
+ <source>Paste Special</source>
+ <target>Liitä määräten</target>
+</phrase>
+<phrase>
+ <source>path</source>
+ <target>polku</target>
+</phrase>
+<phrase>
+ <source>Pause</source>
+ <target>Tauko</target>
+</phrase>
+<phrase>
+ <source>Play</source>
+ <target>Soita</target>
+</phrase>
+<phrase>
+ <source>Plug and Play</source>
+ <target>Plug and Play</target>
+</phrase>
+<phrase>
+ <source>point</source>
+ <target>piste</target>
+</phrase>
+<phrase>
+ <source>point</source>
+ <target>osoittaa</target>
+</phrase>
+<phrase>
+ <source>pointer</source>
+ <target>osoitin</target>
+</phrase>
+<phrase>
+ <source>pop-up menu</source>
+ <target>pikavalikko</target>
+</phrase>
+<phrase>
+ <source>pop-up window</source>
+ <target>ponnahdusikkuna</target>
+</phrase>
+<phrase>
+ <source>portrait</source>
+ <target>pysty</target>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>painaa</target>
+ <definition>and hold a mouse button/ja pitää painettuna hiiripainiketta</definition>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>painaa</target>
+ <definition>a key/näppäintä</definition>
+</phrase>
+<phrase>
+ <source>primary container</source>
+ <target>ensisijainen säilö</target>
+</phrase>
+<phrase>
+ <source>primary window</source>
+ <target>ensisijainen ikkuna</target>
+</phrase>
+<phrase>
+ <source>Print</source>
+ <target>Tulosta</target>
+</phrase>
+<phrase>
+ <source>printer</source>
+ <target>kirjoitin</target>
+</phrase>
+<phrase>
+ <source>progress indicator</source>
+ <target>tilanneilmaisin</target>
+ <definition>control/ohjausobjekti</definition>
+</phrase>
+<phrase>
+ <source>project</source>
+ <target>projekti</target>
+</phrase>
+<phrase>
+ <source>Properties</source>
+ <target>Ominaisuudet</target>
+</phrase>
+<phrase>
+ <source>property inspector</source>
+ <target>ominaisuuksien tarkastelu</target>
+</phrase>
+<phrase>
+ <source>property page</source>
+ <target>ominaisuusryhmä</target>
+</phrase>
+<phrase>
+ <source>property sheet</source>
+ <target>ominaisuusikkuna</target>
+</phrase>
+<phrase>
+ <source>property sheet control</source>
+ <target>ominaisuusikkuna-ohjausobjekti</target>
+</phrase>
+<phrase>
+ <source>Quick View</source>
+ <target>Pikanäyttö</target>
+</phrase>
+<phrase>
+ <source>read-only</source>
+ <target>vain luku</target>
+</phrase>
+<phrase>
+ <source>Recycle Bin</source>
+ <target>Roskakori</target>
+ <definition>Icon/kuvake</definition>
+</phrase>
+<phrase>
+ <source>Redo</source>
+ <target>Tee uudelleen</target>
+</phrase>
+<phrase>
+ <source>region selection</source>
+ <target>aluevalinta</target>
+</phrase>
+<phrase>
+ <source>registry</source>
+ <target>rekisteri</target>
+</phrase>
+<phrase>
+ <source>Repeat</source>
+ <target>Toista</target>
+</phrase>
+<phrase>
+ <source>Replace</source>
+ <target>Korvaa</target>
+</phrase>
+<phrase>
+ <source>Restore</source>
+ <target>Palauta</target>
+</phrase>
+<phrase>
+ <source>Restore button</source>
+ <target>palautuspainike</target>
+</phrase>
+<phrase>
+ <source>Resume</source>
+ <target>Jatka</target>
+</phrase>
+<phrase>
+ <source>Retry</source>
+ <target>Yritä uudelleen</target>
+</phrase>
+<phrase>
+ <source>rich-text box</source>
+ <target>monimuotoruutu</target>
+</phrase>
+<phrase>
+ <source>Run</source>
+ <target>Suorita</target>
+</phrase>
+<phrase>
+ <source>Save</source>
+ <target>Tallenna</target>
+</phrase>
+<phrase>
+ <source>Save as</source>
+ <target>Tallenna nimellä</target>
+</phrase>
+<phrase>
+ <source>scroll</source>
+ <target>vierittää</target>
+</phrase>
+<phrase>
+ <source>scroll arrow</source>
+ <target>vieritysnuoli</target>
+</phrase>
+<phrase>
+ <source>scroll bar</source>
+ <target>vierityspalkki</target>
+</phrase>
+<phrase>
+ <source>scroll box</source>
+ <target>vieritysruutu</target>
+</phrase>
+<phrase>
+ <source>secondary window</source>
+ <target>toissijainen ikkuna</target>
+</phrase>
+<phrase>
+ <source>select</source>
+ <target>valita</target>
+</phrase>
+<phrase>
+ <source>Select All</source>
+ <target>Valitse kaikki</target>
+</phrase>
+<phrase>
+ <source>selection</source>
+ <target>valinta</target>
+</phrase>
+<phrase>
+ <source>selection handle</source>
+ <target>valintakahva</target>
+</phrase>
+<phrase>
+ <source>Send To</source>
+ <target>Lähetä tiedosto</target>
+</phrase>
+<phrase>
+ <source>separator</source>
+ <target>erotin</target>
+</phrase>
+<phrase>
+ <source>Settings</source>
+ <target>Asetukset</target>
+</phrase>
+<phrase>
+ <source>Setup</source>
+ <target>Asennus</target>
+</phrase>
+<phrase>
+ <source>shortcut</source>
+ <target>pika-</target>
+</phrase>
+<phrase>
+ <source>shortcut button</source>
+ <target>pikapainike</target>
+</phrase>
+<phrase>
+ <source>shortcut icon</source>
+ <target>pikakuvake</target>
+</phrase>
+<phrase>
+ <source>shortcut key</source>
+ <target>pikanäppäin</target>
+</phrase>
+<phrase>
+ <source>shortcut key control</source>
+ <target>pikanäppäin-ohjausobjekti</target>
+</phrase>
+<phrase>
+ <source>Show</source>
+ <target>Näytä</target>
+</phrase>
+<phrase>
+ <source>Shutdown</source>
+ <target>Sammuta</target>
+</phrase>
+<phrase>
+ <source>single selection list box</source>
+ <target>yksivalintainen luetteloruutu</target>
+</phrase>
+<phrase>
+ <source>Size</source>
+ <target>Muuta kokoa</target>
+</phrase>
+<phrase>
+ <source>size grip</source>
+ <target>koonmuuttokahva</target>
+</phrase>
+<phrase>
+ <source>slider</source>
+ <target>liukusäädin</target>
+</phrase>
+<phrase>
+ <source>spin box</source>
+ <target>askellusruutu</target>
+</phrase>
+<phrase>
+ <source>Split</source>
+ <target>Jaa</target>
+</phrase>
+<phrase>
+ <source>split bar</source>
+ <target>jakopalkki</target>
+</phrase>
+<phrase>
+ <source>split box</source>
+ <target>jakoruutu</target>
+</phrase>
+<phrase>
+ <source>Start button</source>
+ <target>Käynnistä-painike</target>
+</phrase>
+<phrase>
+ <source>StartUp folder</source>
+ <target>Käynnistys-kansio</target>
+</phrase>
+<phrase>
+ <source>status bar</source>
+ <target>tilarivi</target>
+</phrase>
+<phrase>
+ <source>Stop</source>
+ <target>Pysäytä</target>
+</phrase>
+<phrase>
+ <source>tab control</source>
+ <target>välilehti</target>
+ <definition>ohjausobjekti</definition>
+</phrase>
+<phrase>
+ <source>task bar</source>
+ <target>tehtäväpalkki</target>
+</phrase>
+<phrase>
+ <source>task-oriented Help</source>
+ <target>tehtäväohje</target>
+</phrase>
+<phrase>
+ <source>template</source>
+ <target>malli</target>
+</phrase>
+<phrase>
+ <source>text box</source>
+ <target>muokkausruutu</target>
+</phrase>
+<phrase>
+ <source>title bar</source>
+ <target>otsikkorivi</target>
+</phrase>
+<phrase>
+ <source>title text</source>
+ <target>otsikkoteksti</target>
+</phrase>
+<phrase>
+ <source>toggle key</source>
+ <target>tilanvaihtonäppäin</target>
+</phrase>
+<phrase>
+ <source>toolbar</source>
+ <target>työkalurivi</target>
+</phrase>
+<phrase>
+ <source>tooltip</source>
+ <target>työkaluvihje</target>
+</phrase>
+<phrase>
+ <source>tree view control</source>
+ <target>puunäyttö</target>
+ <definition>ohjausobjekti</definition>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>tyyppi</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>laji</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>kirjoittaa</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>unavailable</source>
+ <target>ei käytettävissä</target>
+</phrase>
+<phrase>
+ <source>Undo</source>
+ <target>Kumoa</target>
+</phrase>
+<phrase>
+ <source>Uninstall</source>
+ <target>Pura asennus</target>
+</phrase>
+<phrase>
+ <source>View</source>
+ <target>Näytä-valikko</target>
+</phrase>
+<phrase>
+ <source>visual editing</source>
+ <target>visuaalinen muokkaus</target>
+</phrase>
+<phrase>
+ <source>well control</source>
+ <target>graafisen valinnan ohjausobjekti</target>
+</phrase>
+<phrase>
+ <source>What&apos;s This?</source>
+ <target>Lisätietoja</target>
+</phrase>
+<phrase>
+ <source>Window</source>
+ <target>Ikkuna-valikko</target>
+</phrase>
+<phrase>
+ <source>window</source>
+ <target>ikkuna</target>
+</phrase>
+<phrase>
+ <source>Windows Explorer</source>
+ <target>Resurssienhallinta</target>
+</phrase>
+<phrase>
+ <source>wizard</source>
+ <target>ohjattu toiminto</target>
+</phrase>
+<phrase>
+ <source>workbook</source>
+ <target>työkirja</target>
+</phrase>
+<phrase>
+ <source>workgroup</source>
+ <target>työryhmä</target>
+</phrase>
+<phrase>
+ <source>workspace</source>
+ <target>työtila</target>
+</phrase>
+<phrase>
+ <source>Yes</source>
+ <target>Kyllä</target>
+</phrase>
+</QPH>
diff --git a/src/linguist/phrasebooks/french.qph b/src/linguist/phrasebooks/french.qph
new file mode 100644
index 000000000..1884ed3d1
--- /dev/null
+++ b/src/linguist/phrasebooks/french.qph
@@ -0,0 +1,1493 @@
+<!DOCTYPE QPH>
+<QPH language="fr">
+<phrase>
+ <source>About</source>
+ <target>A propos</target>
+</phrase>
+<phrase>
+ <source>access key</source>
+ <target>touche d&apos;accès rapide</target>
+</phrase>
+<phrase>
+ <source>accessibility</source>
+ <target>accessibilité</target>
+</phrase>
+<phrase>
+ <source>action handle</source>
+ <target>handle d&apos;action</target>
+</phrase>
+<phrase>
+ <source>active</source>
+ <target>actif</target>
+</phrase>
+<phrase>
+ <source>active</source>
+ <target>active</target>
+</phrase>
+<phrase>
+ <source>active end</source>
+ <target>point de fin de sélecion</target>
+</phrase>
+<phrase>
+ <source>active object</source>
+ <target>objet actif</target>
+</phrase>
+<phrase>
+ <source>active window</source>
+ <target>fenêtre activ</target>
+</phrase>
+<phrase>
+ <source>adornment</source>
+ <target>barre</target>
+</phrase>
+<phrase>
+ <source>Always on Top</source>
+ <target>Toujours visible</target>
+</phrase>
+<phrase>
+ <source>anchor point</source>
+ <target>point de début de sélection</target>
+</phrase>
+<phrase>
+ <source>Apply</source>
+ <target>Appliquer</target>
+</phrase>
+<phrase>
+ <source>auto-exit</source>
+ <target>sortie automatique</target>
+</phrase>
+<phrase>
+ <source>auto-repeat</source>
+ <target>répétition automatique</target>
+</phrase>
+<phrase>
+ <source>automatic link</source>
+ <target>Liaison automatique</target>
+</phrase>
+<phrase>
+ <source>automatic scrolling</source>
+ <target>défilement automatique</target>
+</phrase>
+<phrase>
+ <source>autoscroll</source>
+ <target>défilement automatique</target>
+</phrase>
+<phrase>
+ <source>Back</source>
+ <target>Précédent</target>
+</phrase>
+<phrase>
+ <source>barrel button</source>
+ <target>Bouton du stylet</target>
+</phrase>
+<phrase>
+ <source>barrel-tap</source>
+ <target>toucher-maintenir enfoncé</target>
+</phrase>
+<phrase>
+ <source>boxed edit</source>
+ <target>édition contrôlée</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>Browse</source>
+ <target>Parcourir</target>
+</phrase>
+<phrase>
+ <source>Cancel</source>
+ <target>Annuler</target>
+</phrase>
+<phrase>
+ <source>cascading menu</source>
+ <target>menu en cascade</target>
+</phrase>
+<phrase>
+ <source>check box</source>
+ <target>case à cocher</target>
+</phrase>
+<phrase>
+ <source>check mark</source>
+ <target>coche</target>
+</phrase>
+<phrase>
+ <source>child window</source>
+ <target>fenêtre enfant</target>
+</phrase>
+<phrase>
+ <source>choose</source>
+ <target>choisir</target>
+</phrase>
+<phrase>
+ <source>click</source>
+ <target>cliquer sur</target>
+ <definition>verb, à l&apos;écran</definition>
+</phrase>
+<phrase>
+ <source>click</source>
+ <target>clic</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>click</source>
+ <target>cliquer le</target>
+ <definition>verb, bouton souris</definition>
+</phrase>
+<phrase>
+ <source>Clipboard</source>
+ <target>Presse-papiers</target>
+</phrase>
+<phrase>
+ <source>Close</source>
+ <target>Fermer</target>
+</phrase>
+<phrase>
+ <source>Close</source>
+ <target>Fermeture</target>
+</phrase>
+<phrase>
+ <source>Close button</source>
+ <target>Fermer</target>
+</phrase>
+<phrase>
+ <source>collapse</source>
+ <target>réduire</target>
+ <definition>outline</definition>
+</phrase>
+<phrase>
+ <source>column heading</source>
+ <target>en-tête de colonne</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>combo box</source>
+ <target>zone de liste modifiable</target>
+</phrase>
+<phrase>
+ <source>command button</source>
+ <target>bouton de commande</target>
+</phrase>
+<phrase>
+ <source>container</source>
+ <target>conteneur</target>
+ <definition>d&apos;objets</definition>
+</phrase>
+<phrase>
+ <source>context-sensitive Help</source>
+ <target>aide contextuelle</target>
+</phrase>
+<phrase>
+ <source>contextual</source>
+ <target>contextuelle</target>
+</phrase>
+<phrase>
+ <source>contextual</source>
+ <target>contextuel</target>
+</phrase>
+<phrase>
+ <source>control</source>
+ <target>contrôle</target>
+</phrase>
+<phrase>
+ <source>Copy</source>
+ <target>Copier</target>
+</phrase>
+<phrase>
+ <source>Copy here</source>
+ <target>Copier ici</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut</source>
+ <target>Copier un raccourci</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut Here</source>
+ <target>Copier un raccourci ice</target>
+</phrase>
+<phrase>
+ <source>Cut</source>
+ <target>Couper</target>
+</phrase>
+<phrase>
+ <source>default</source>
+ <target>par défaut</target>
+</phrase>
+<phrase>
+ <source>default button</source>
+ <target>bouton par défaut</target>
+</phrase>
+<phrase>
+ <source>Delete</source>
+ <target>Supprimer</target>
+</phrase>
+<phrase>
+ <source>desktop</source>
+ <target>bureau</target>
+</phrase>
+<phrase>
+ <source>destination</source>
+ <target>destination</target>
+</phrase>
+<phrase>
+ <source>dialog box</source>
+ <target>boîte de dialogue</target>
+</phrase>
+<phrase>
+ <source>disability</source>
+ <target>incapacité</target>
+</phrase>
+<phrase>
+ <source>disjoint selection</source>
+ <target>sélection d&apos;objets disjoints</target>
+</phrase>
+<phrase>
+ <source>dock</source>
+ <target>aligner</target>
+</phrase>
+<phrase>
+ <source>document</source>
+ <target>document</target>
+</phrase>
+<phrase>
+ <source>double-click</source>
+ <target>cliquer deux fois</target>
+</phrase>
+<phrase>
+ <source>double-tap</source>
+ <target>toucher deux fois</target>
+</phrase>
+<phrase>
+ <source>drag</source>
+ <target>faire glisser</target>
+</phrase>
+<phrase>
+ <source>drag-and-drop</source>
+ <target>glisser-déplacer</target>
+</phrase>
+<phrase>
+ <source>drop-down combo box</source>
+ <target>zone de liste déroulante modifiable</target>
+</phrase>
+<phrase>
+ <source>drop-down list box</source>
+ <target>zone de liste déroulante fixe</target>
+</phrase>
+<phrase>
+ <source>drop-down menu</source>
+ <target>menu déroulant</target>
+</phrase>
+<phrase>
+ <source>Edit</source>
+ <target>Edition</target>
+</phrase>
+<phrase>
+ <source>Edit menu</source>
+ <target>modifier</target>
+</phrase>
+<phrase>
+ <source>ellipsis</source>
+ <target>points de suspension</target>
+</phrase>
+<phrase>
+ <source>embedded object</source>
+ <target>objet incorporé</target>
+</phrase>
+<phrase>
+ <source>Exit</source>
+ <target>Quitter</target>
+</phrase>
+<phrase>
+ <source>expand</source>
+ <target>développer</target>
+ <definition>an outline</definition>
+</phrase>
+<phrase>
+ <source>Explore</source>
+ <target>Explorer</target>
+</phrase>
+<phrase>
+ <source>extended selection</source>
+ <target>sélection étendue</target>
+</phrase>
+<phrase>
+ <source>extended selection list box</source>
+ <target>zone de liste à sélection étendue</target>
+</phrase>
+<phrase>
+ <source>File</source>
+ <target>Fichier</target>
+ <definition>menu</definition>
+</phrase>
+<phrase>
+ <source>file</source>
+ <target>fichier</target>
+</phrase>
+<phrase>
+ <source>Find</source>
+ <target>Rechercher</target>
+</phrase>
+<phrase>
+ <source>Find Next</source>
+ <target>Suivant</target>
+</phrase>
+<phrase>
+ <source>Find What</source>
+ <target>Rechercher</target>
+</phrase>
+<phrase>
+ <source>folder</source>
+ <target>dossier</target>
+</phrase>
+<phrase>
+ <source>font</source>
+ <target>police</target>
+</phrase>
+<phrase>
+ <source>font size</source>
+ <target>taille de police</target>
+</phrase>
+<phrase>
+ <source>font style</source>
+ <target>style de police</target>
+</phrase>
+<phrase>
+ <source>function key</source>
+ <target>touche de fonction</target>
+</phrase>
+<phrase>
+ <source>gesture</source>
+ <target>signe</target>
+</phrase>
+<phrase>
+ <source>glyph</source>
+ <target>glyphe</target>
+</phrase>
+<phrase>
+ <source>group box</source>
+ <target>zone de groupe</target>
+</phrase>
+<phrase>
+ <source>handle</source>
+ <target>handle</target>
+</phrase>
+<phrase>
+ <source>Help</source>
+ <target>?</target>
+ <definition>menu</definition>
+</phrase>
+<phrase>
+ <source>Help</source>
+ <target>Aide</target>
+</phrase>
+<phrase>
+ <source>Hide</source>
+ <target>Masquer</target>
+</phrase>
+<phrase>
+ <source>hierarchical selection</source>
+ <target>sélection hiérarchique</target>
+</phrase>
+<phrase>
+ <source>hold</source>
+ <target>maintenir</target>
+</phrase>
+<phrase>
+ <source>hot spot</source>
+ <target>point d&apos;impact</target>
+</phrase>
+<phrase>
+ <source>hot zone</source>
+ <target>zone critique</target>
+</phrase>
+<phrase>
+ <source>icon</source>
+ <target>icône</target>
+</phrase>
+<phrase>
+ <source>inactive</source>
+ <target>inactive</target>
+</phrase>
+<phrase>
+ <source>inactive</source>
+ <target>inactif</target>
+</phrase>
+<phrase>
+ <source>inactive window</source>
+ <target>fenêtre inactive</target>
+</phrase>
+<phrase>
+ <source>ink</source>
+ <target>dessin à main levée</target>
+</phrase>
+<phrase>
+ <source>ink edit</source>
+ <target>éditeur de dissin à main levée</target>
+</phrase>
+<phrase>
+ <source>input focus</source>
+ <target>zone d&apos;interaction</target>
+</phrase>
+<phrase>
+ <source>Insert</source>
+ <target>Insertion</target>
+</phrase>
+<phrase>
+ <source>Insert Object</source>
+ <target>Insérer un objet</target>
+</phrase>
+<phrase>
+ <source>insertion point</source>
+ <target>point d&apos;insertion</target>
+</phrase>
+<phrase>
+ <source>italic</source>
+ <target>italique</target>
+</phrase>
+<phrase>
+ <source>label</source>
+ <target>nom de volume</target>
+</phrase>
+<phrase>
+ <source>label</source>
+ <target>étiquette</target>
+</phrase>
+<phrase>
+ <source>landscape</source>
+ <target>paysage</target>
+</phrase>
+<phrase>
+ <source>lasso-tap</source>
+ <target>toucher lasso</target>
+</phrase>
+<phrase>
+ <source>lens</source>
+ <target>loupe</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>lier</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>liaison</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>Link Here</source>
+ <target>Lier ici</target>
+</phrase>
+<phrase>
+ <source>list box</source>
+ <target>zone de liste</target>
+</phrase>
+<phrase>
+ <source>list view</source>
+ <target>presentación de iconos </target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>list view</source>
+ <target>Liste icônes</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>manual link</source>
+ <target>liaison manuelle</target>
+</phrase>
+<phrase>
+ <source>Maximize</source>
+ <target>Agrandissement</target>
+</phrase>
+<phrase>
+ <source>maximize button</source>
+ <target>agrandir</target>
+</phrase>
+<phrase>
+ <source>MDI</source>
+ <target>MDI</target>
+</phrase>
+<phrase>
+ <source>menu</source>
+ <target>menu</target>
+</phrase>
+<phrase>
+ <source>menu bar</source>
+ <target>barre de menus</target>
+</phrase>
+<phrase>
+ <source>menu button</source>
+ <target>bouton de menus</target>
+</phrase>
+<phrase>
+ <source>menu item</source>
+ <target>élément de menu</target>
+</phrase>
+<phrase>
+ <source>menu title</source>
+ <target>titre de menu</target>
+</phrase>
+<phrase>
+ <source>message box</source>
+ <target>boîte de message</target>
+</phrase>
+<phrase>
+ <source>Minimize</source>
+ <target>Réduction</target>
+</phrase>
+<phrase>
+ <source>minimize button</source>
+ <target>réduire</target>
+</phrase>
+<phrase>
+ <source>mixed-value</source>
+ <target>valeurs multiples</target>
+</phrase>
+<phrase>
+ <source>modal</source>
+ <target>modal</target>
+</phrase>
+<phrase>
+ <source>mode</source>
+ <target>mode</target>
+</phrase>
+<phrase>
+ <source>modeless</source>
+ <target>non modal</target>
+</phrase>
+<phrase>
+ <source>modifier key</source>
+ <target>touche de modification</target>
+</phrase>
+<phrase>
+ <source>mouse</source>
+ <target>souris</target>
+</phrase>
+<phrase>
+ <source>Move</source>
+ <target>Déplacement</target>
+</phrase>
+<phrase>
+ <source>Move Here</source>
+ <target>Transférer ici</target>
+</phrase>
+<phrase>
+ <source>Multiple Document Interface</source>
+ <target>Interface documents multiples</target>
+</phrase>
+<phrase>
+ <source>multiple selection list box</source>
+ <target>zone de liste à sélection multiple</target>
+</phrase>
+<phrase>
+ <source>My Computer</source>
+ <target>Poste de travail</target>
+ <definition>icon</definition>
+</phrase>
+<phrase>
+ <source>Network Neighborhood</source>
+ <target>Voisinage réseau</target>
+ <definition>icon</definition>
+</phrase>
+<phrase>
+ <source>New</source>
+ <target>Nouveau</target>
+</phrase>
+<phrase>
+ <source>Next</source>
+ <target>Suivant</target>
+</phrase>
+<phrase>
+ <source>object</source>
+ <target>objet</target>
+</phrase>
+<phrase>
+ <source>OK</source>
+ <target>OK</target>
+</phrase>
+<phrase>
+ <source>OLE</source>
+ <target>OLE</target>
+</phrase>
+<phrase>
+ <source>OLE drag and drop</source>
+ <target>glisser-déplacer OLE</target>
+</phrase>
+<phrase>
+ <source>OLE embedded object</source>
+ <target>Objet OLE incorporé</target>
+</phrase>
+<phrase>
+ <source>OLE linked object</source>
+ <target>Objet OLE lié</target>
+</phrase>
+<phrase>
+ <source>OLE nondefault drag and drop</source>
+ <target>glisser-déplacer OLE non standard</target>
+</phrase>
+<phrase>
+ <source>Open</source>
+ <target>Ouvrir</target>
+</phrase>
+<phrase>
+ <source>Open With</source>
+ <target>Ouvrir avec</target>
+</phrase>
+<phrase>
+ <source>option button</source>
+ <target>case d&apos;option</target>
+</phrase>
+<phrase>
+ <source>option-set</source>
+ <target>état des caractéristiques</target>
+</phrase>
+<phrase>
+ <source>package</source>
+ <target>ensemble</target>
+</phrase>
+<phrase>
+ <source>Page Setup</source>
+ <target>Mise en page</target>
+</phrase>
+<phrase>
+ <source>palette window</source>
+ <target>palette</target>
+</phrase>
+<phrase>
+ <source>pane</source>
+ <target>volet</target>
+</phrase>
+<phrase>
+ <source>parent window</source>
+ <target>fenêtre parent</target>
+</phrase>
+<phrase>
+ <source>password</source>
+ <target>mot de passe</target>
+</phrase>
+<phrase>
+ <source>Paste</source>
+ <target>Coller</target>
+</phrase>
+<phrase>
+ <source>Paste Link</source>
+ <target>Coller avec liaison</target>
+</phrase>
+<phrase>
+ <source>Paste Shortcut</source>
+ <target>Coller le raccourci</target>
+</phrase>
+<phrase>
+ <source>Paste Special</source>
+ <target>Collage spécial</target>
+</phrase>
+<phrase>
+ <source>path</source>
+ <target>chemin</target>
+</phrase>
+<phrase>
+ <source>Pause</source>
+ <target>Pause</target>
+</phrase>
+<phrase>
+ <source>pen</source>
+ <target>stylet</target>
+</phrase>
+<phrase>
+ <source>Play</source>
+ <target>Exécuter</target>
+</phrase>
+<phrase>
+ <source>Plug and Play</source>
+ <target>Plug and Play</target>
+</phrase>
+<phrase>
+ <source>point</source>
+ <target>point</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>point</source>
+ <target>amener le pointeur sur</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>pointer</source>
+ <target>pointeur</target>
+</phrase>
+<phrase>
+ <source>pop-up menu</source>
+ <target>menu autonome</target>
+</phrase>
+<phrase>
+ <source>pop-up window</source>
+ <target>fenêtre autonome</target>
+</phrase>
+<phrase>
+ <source>portrait</source>
+ <target>portrait</target>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>appuyer</target>
+ <definition>and hold a mouse button/et maintenir enfoncé</definition>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>appuyer</target>
+ <definition>a key/sur une touche</definition>
+</phrase>
+<phrase>
+ <source>primary container</source>
+ <target>contenuer principale</target>
+</phrase>
+<phrase>
+ <source>primary window</source>
+ <target>fenêtre principale</target>
+</phrase>
+<phrase>
+ <source>Print</source>
+ <target>Imprimer</target>
+</phrase>
+<phrase>
+ <source>printer</source>
+ <target>imprimante</target>
+</phrase>
+<phrase>
+ <source>progress indicator</source>
+ <target>indicateur d&apos;état</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>project</source>
+ <target>projet</target>
+</phrase>
+<phrase>
+ <source>Properties</source>
+ <target>Propriétés</target>
+</phrase>
+<phrase>
+ <source>property inspector</source>
+ <target>inspecteur de propriétés</target>
+</phrase>
+<phrase>
+ <source>property page</source>
+ <target>page de propriétés</target>
+</phrase>
+<phrase>
+ <source>property sheet</source>
+ <target>feuille de propriétés</target>
+</phrase>
+<phrase>
+ <source>property sheet control</source>
+ <target>feuille de propriétés</target>
+</phrase>
+<phrase>
+ <source>Quick View</source>
+ <target>Aperçu</target>
+</phrase>
+<phrase>
+ <source>read-only</source>
+ <target>en lecture seule</target>
+</phrase>
+<phrase>
+ <source>recognition</source>
+ <target>reconnaissance</target>
+</phrase>
+<phrase>
+ <source>Recycle Bin</source>
+ <target>Corbeille</target>
+ <definition>Icon</definition>
+</phrase>
+<phrase>
+ <source>Redo</source>
+ <target>Rétablir</target>
+</phrase>
+<phrase>
+ <source>region selection</source>
+ <target>sélection par zone</target>
+</phrase>
+<phrase>
+ <source>registry</source>
+ <target>base des registres</target>
+</phrase>
+<phrase>
+ <source>Repeat</source>
+ <target>Répéter</target>
+</phrase>
+<phrase>
+ <source>Replace</source>
+ <target>Remplacer</target>
+</phrase>
+<phrase>
+ <source>Restore</source>
+ <target>Restauration</target>
+</phrase>
+<phrase>
+ <source>Restore button</source>
+ <target>Restaurer</target>
+</phrase>
+<phrase>
+ <source>Resume</source>
+ <target>Reprendre</target>
+</phrase>
+<phrase>
+ <source>Retry</source>
+ <target>Essayer de nouveau</target>
+</phrase>
+<phrase>
+ <source>rich-text box</source>
+ <target>zone de texte RTF</target>
+ <definition>Rich Text Format</definition>
+</phrase>
+<phrase>
+ <source>Run</source>
+ <target>Exécuter</target>
+</phrase>
+<phrase>
+ <source>Save</source>
+ <target>Enregistrer</target>
+</phrase>
+<phrase>
+ <source>Save as</source>
+ <target>Enregistrer sous</target>
+</phrase>
+<phrase>
+ <source>scroll</source>
+ <target>faire défiler</target>
+</phrase>
+<phrase>
+ <source>scroll arrow</source>
+ <target>flèche de défilement</target>
+</phrase>
+<phrase>
+ <source>scroll bar</source>
+ <target>barre de défilement</target>
+</phrase>
+<phrase>
+ <source>scroll box</source>
+ <target>curseur de défilement</target>
+</phrase>
+<phrase>
+ <source>secondary window</source>
+ <target>fenêtre secondaire</target>
+</phrase>
+<phrase>
+ <source>select</source>
+ <target>sélectionner</target>
+</phrase>
+<phrase>
+ <source>Select All</source>
+ <target>Tout sélectionner</target>
+</phrase>
+<phrase>
+ <source>selection</source>
+ <target>sélection</target>
+</phrase>
+<phrase>
+ <source>selection handle</source>
+ <target>handle de sélection</target>
+</phrase>
+<phrase>
+ <source>Send To</source>
+ <target>Envoyer vers</target>
+</phrase>
+<phrase>
+ <source>separator</source>
+ <target>séparateur</target>
+</phrase>
+<phrase>
+ <source>Settings</source>
+ <target>Paramètres</target>
+</phrase>
+<phrase>
+ <source>Setup</source>
+ <target>Installation</target>
+</phrase>
+<phrase>
+ <source>Setup</source>
+ <target>INSTALL</target>
+</phrase>
+<phrase>
+ <source>shortcut</source>
+ <target>raccourci</target>
+</phrase>
+<phrase>
+ <source>shortcut button</source>
+ <target>raccourci</target>
+</phrase>
+<phrase>
+ <source>shortcut icon</source>
+ <target>raccourci</target>
+</phrase>
+<phrase>
+ <source>shortcut key</source>
+ <target>touche de raccourci</target>
+</phrase>
+<phrase>
+ <source>shortcut key control</source>
+ <target>touche de raccourci</target>
+</phrase>
+<phrase>
+ <source>Show</source>
+ <target>Afficher</target>
+</phrase>
+<phrase>
+ <source>Shutdown</source>
+ <target>Arrêter l&apos;ordinateur</target>
+</phrase>
+<phrase>
+ <source>single selection list box</source>
+ <target>zone de liste à sélection unique</target>
+</phrase>
+<phrase>
+ <source>Size</source>
+ <target>Dimension</target>
+</phrase>
+<phrase>
+ <source>size grip</source>
+ <target>poignée de redimensionnement</target>
+</phrase>
+<phrase>
+ <source>slider</source>
+ <target>défileur</target>
+</phrase>
+<phrase>
+ <source>spin box</source>
+ <target>compteur</target>
+</phrase>
+<phrase>
+ <source>Split</source>
+ <target>Fractionner</target>
+</phrase>
+<phrase>
+ <source>split bar</source>
+ <target>barre de fractionnement</target>
+</phrase>
+<phrase>
+ <source>split box</source>
+ <target>curseur de fractionnement</target>
+</phrase>
+<phrase>
+ <source>Start button</source>
+ <target>Démarrer</target>
+</phrase>
+<phrase>
+ <source>StartUp folder</source>
+ <target>dossier de démarrage</target>
+</phrase>
+<phrase>
+ <source>status bar</source>
+ <target>barre d&apos;état</target>
+</phrase>
+<phrase>
+ <source>Stop</source>
+ <target>Arrêter</target>
+</phrase>
+<phrase>
+ <source>tab control</source>
+ <target>onglet</target>
+</phrase>
+<phrase>
+ <source>tap</source>
+ <target>toucher</target>
+</phrase>
+<phrase>
+ <source>task bar</source>
+ <target>barre des tâches</target>
+</phrase>
+<phrase>
+ <source>task-oriented Help</source>
+ <target>aide spécifique aux tâches</target>
+</phrase>
+<phrase>
+ <source>template</source>
+ <target>modèle</target>
+</phrase>
+<phrase>
+ <source>text box</source>
+ <target>zone de texte</target>
+</phrase>
+<phrase>
+ <source>title bar</source>
+ <target>barre de titre</target>
+</phrase>
+<phrase>
+ <source>title text</source>
+ <target>texte de la barre de titre</target>
+</phrase>
+<phrase>
+ <source>toggle key</source>
+ <target>touche bascule</target>
+</phrase>
+<phrase>
+ <source>toolbar</source>
+ <target>barre d&apos;outils</target>
+</phrase>
+<phrase>
+ <source>tooltip</source>
+ <target>info-bulle</target>
+</phrase>
+<phrase>
+ <source>tree view control</source>
+ <target>arborescencel</target>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>type</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>taper</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>unavailable</source>
+ <target>pas disponible</target>
+</phrase>
+<phrase>
+ <source>Undo</source>
+ <target>Annuler</target>
+</phrase>
+<phrase>
+ <source>Uninstall</source>
+ <target>Désinstaller</target>
+</phrase>
+<phrase>
+ <source>View</source>
+ <target>Affichage</target>
+</phrase>
+<phrase>
+ <source>visual editing</source>
+ <target>activation sur place</target>
+</phrase>
+<phrase>
+ <source>well control</source>
+ <target>sélection graphique</target>
+</phrase>
+<phrase>
+ <source>What&apos;s This?</source>
+ <target>Qu&apos;est-ce que c&apos;est?</target>
+</phrase>
+<phrase>
+ <source>Window</source>
+ <target>Fenêtre</target>
+</phrase>
+<phrase>
+ <source>window</source>
+ <target>fenêtre</target>
+</phrase>
+<phrase>
+ <source>Windows Explorer</source>
+ <target>Explorateur Windows</target>
+</phrase>
+<phrase>
+ <source>wizard</source>
+ <target>assistant</target>
+</phrase>
+<phrase>
+ <source>workbook</source>
+ <target>classeur</target>
+</phrase>
+<phrase>
+ <source>workgroup</source>
+ <target>groupe de travail</target>
+</phrase>
+<phrase>
+ <source>workspace</source>
+ <target>espace de travail</target>
+</phrase>
+<phrase>
+ <source>Yes</source>
+ <target>Oui</target>
+</phrase>
+<phrase>
+ <source>Split</source>
+ <target>Scinder</target>
+</phrase>
+<phrase>
+ <source>&amp;Edit</source>
+ <target>&amp;Édition</target>
+</phrase>
+<phrase>
+ <source>debugger</source>
+ <target>débogueur</target>
+</phrase>
+<phrase>
+ <source>Start Debugger</source>
+ <target>Lancer le débogueur</target>
+</phrase>
+<phrase>
+ <source>Executable:</source>
+ <target>Exécutable:</target>
+</phrase>
+<phrase>
+ <source>Filter:</source>
+ <target>Filtre:</target>
+</phrase>
+<phrase>
+ <source>Clear</source>
+ <target>Effacer</target>
+</phrase>
+<phrase>
+ <source>Host and port:</source>
+ <target>Hôte et port:</target>
+</phrase>
+<phrase>
+ <source>Architecture:</source>
+ <target>Architecture:</target>
+</phrase>
+<phrase>
+ <source>Server start script:</source>
+ <target>Script de démarrage du serveur:</target>
+</phrase>
+<phrase>
+ <source>&amp;Undo</source>
+ <target>Annu&amp;ler</target>
+</phrase>
+<phrase>
+ <source>Add Bookmark</source>
+ <target>Ajouter un signet</target>
+</phrase>
+<phrase>
+ <source>Bookmark:</source>
+ <target>Signet:</target>
+</phrase>
+<phrase>
+ <source>Add in Folder:</source>
+ <target>Ajouter dans le dossier:</target>
+</phrase>
+<phrase>
+ <source>+</source>
+ <target>+</target>
+</phrase>
+<phrase>
+ <source>New Folder</source>
+ <target>Nouveau dossier</target>
+</phrase>
+<phrase>
+ <source>Bookmarks</source>
+ <target>Signets</target>
+</phrase>
+<phrase>
+ <source>Rename Folder</source>
+ <target>Renommer le dossier</target>
+</phrase>
+<phrase>
+ <source>Bookmark</source>
+ <target>Signet</target>
+</phrase>
+<phrase>
+ <source>Remove</source>
+ <target>Retirer</target>
+</phrase>
+<phrase>
+ <source>Delete Folder</source>
+ <target>Supprimer le dossier</target>
+</phrase>
+<phrase>
+ <source>Add</source>
+ <target>Ajouter</target>
+</phrase>
+<phrase>
+ <source>Move Up</source>
+ <target>Vers le Haut</target>
+</phrase>
+<phrase>
+ <source>Move Down</source>
+ <target>Vers le Bas</target>
+</phrase>
+<phrase>
+ <source>Show Bookmark</source>
+ <target>Afficher le signet</target>
+</phrase>
+<phrase>
+ <source>Show Bookmark in New Tab</source>
+ <target>Afficher le signet dans un nouvel onglet</target>
+</phrase>
+<phrase>
+ <source>Delete Bookmark</source>
+ <target>Supprimer le signet</target>
+</phrase>
+<phrase>
+ <source>Rename Bookmark</source>
+ <target>Renommer le signet</target>
+</phrase>
+<phrase>
+ <source>Previous Bookmark</source>
+ <target>Signet précédent</target>
+</phrase>
+<phrase>
+ <source>Next Bookmark</source>
+ <target>Signet suivant</target>
+</phrase>
+<phrase>
+ <source>Condition:</source>
+ <target>Condition:</target>
+</phrase>
+<phrase>
+ <source>Working Directory:</source>
+ <target>Répertoire de travail:</target>
+</phrase>
+<phrase>
+ <source>Environment</source>
+ <target>Environnement</target>
+</phrase>
+<phrase>
+ <source>Arguments</source>
+ <target>Arguments</target>
+</phrase>
+<phrase>
+ <source>Build directory:</source>
+ <target>Répertoire de compilation:</target>
+</phrase>
+<phrase>
+ <source>Path:</source>
+ <target>Chemin:</target>
+</phrase>
+<phrase>
+ <source>General</source>
+ <target>Général</target>
+</phrase>
+<phrase>
+ <source>Username:</source>
+ <target>Nom d&apos;utilisateur:</target>
+</phrase>
+<phrase>
+ <source>User interface</source>
+ <target>Interface utilisateur</target>
+</phrase>
+<phrase>
+ <source>Open Link</source>
+ <target>Ouvrir le lien</target>
+</phrase>
+<phrase>
+ <source> [read only]</source>
+ <target> [lecture seule]</target>
+</phrase>
+<phrase>
+ <source> [directory]</source>
+ <target> [répertoire]</target>
+</phrase>
+<phrase>
+ <source>Close All</source>
+ <target>Fermer tout</target>
+</phrase>
+<phrase>
+ <source>Failed!</source>
+ <target>Échec!</target>
+</phrase>
+<phrase>
+ <source>Proceed</source>
+ <target>Continuer</target>
+</phrase>
+<phrase>
+ <source>Make writable</source>
+ <target>Rendre inscriptible</target>
+</phrase>
+<phrase>
+ <source>Qt Creator</source>
+ <target>Qt Creator</target>
+</phrase>
+<phrase>
+ <source>&amp;File</source>
+ <target>&amp;Fichier</target>
+</phrase>
+<phrase>
+ <source>Activate %1</source>
+ <target>Activer %1</target>
+</phrase>
+<phrase>
+ <source>New Project</source>
+ <target>Nouveau projet</target>
+</phrase>
+<phrase>
+ <source>Close %1</source>
+ <target>Fermer %1</target>
+</phrase>
+<phrase>
+ <source>*</source>
+ <target>*</target>
+</phrase>
+<phrase>
+ <source>&amp;Change</source>
+ <target>&amp;Modifier</target>
+</phrase>
+<phrase>
+ <source>Close Other Editors</source>
+ <target>Fermer les autres éditeurs</target>
+</phrase>
+<phrase>
+ <source>Close All Except %1</source>
+ <target>Fermer tout sauf %1</target>
+</phrase>
+<phrase>
+ <source>Remove</source>
+ <target>Suppression</target>
+</phrase>
+<phrase>
+ <source>About...</source>
+ <target>À propos…</target>
+</phrase>
+<phrase>
+ <source>Minimize</source>
+ <target>Minimiser</target>
+</phrase>
+<phrase>
+ <source>Remove</source>
+ <target>Supprimer</target>
+</phrase>
+<phrase>
+ <source>Select All</source>
+ <target>Sélectionner tout</target>
+</phrase>
+<phrase>
+ <source>Cannot create directory: %1</source>
+ <target>Impossible de créer le répertoire : %1</target>
+</phrase>
+<phrase>
+ <source></source>
+ <target></target>
+</phrase>
+<phrase>
+ <source>Whole &amp;words</source>
+ <target>M&amp;ots complets</target>
+</phrase>
+<phrase>
+ <source>Title:</source>
+ <target>Titre :</target>
+</phrase>
+<phrase>
+ <source>Fonts</source>
+ <target>Polices</target>
+</phrase>
+<phrase>
+ <source>Insert</source>
+ <target>Insérer</target>
+</phrase>
+<phrase>
+ <source>Size</source>
+ <target>Taille</target>
+</phrase>
+<phrase>
+ <source>List View</source>
+ <target>Affichage liste</target>
+</phrase>
+<phrase>
+ <source>Read-only</source>
+ <target>Lecture seule</target>
+</phrase>
+<phrase>
+ <source>Minimize</source>
+ <target>Réduire</target>
+</phrase>
+<phrase>
+ <source>Maximize</source>
+ <target>Maximiser</target>
+</phrase>
+<phrase>
+ <source>Retry</source>
+ <target>Réessayer</target>
+</phrase>
+<phrase>
+ <source>Dock</source>
+ <target>Attacher</target>
+</phrase>
+<phrase>
+ <source>&amp;Redo</source>
+ <target>&amp;Rétablir</target>
+</phrase>
+<phrase>
+ <source>Edit</source>
+ <target>Éditer</target>
+</phrase>
+<phrase>
+ <source>PATH:</source>
+ <target>PATH :</target>
+</phrase>
+<phrase>
+ <source>Change:</source>
+ <target>Modification :</target>
+</phrase>
+<phrase>
+ <source>Edit...</source>
+ <target>Modifier...</target>
+</phrase>
+<phrase>
+ <source>&amp;Username:</source>
+ <target>&amp;Utilisateur :</target>
+</phrase>
+<phrase>
+ <source>Link</source>
+ <target>Lien</target>
+</phrase>
+<phrase>
+ <source>Paste:</source>
+ <target>Collage :</target>
+</phrase>
+<phrase>
+ <source>Label</source>
+ <target>Libellé</target>
+</phrase>
+<phrase>
+ <source>&amp;Debug</source>
+ <target>&amp;Déboguer</target>
+</phrase>
+<phrase>
+ <source>Slider</source>
+ <target>Barre de défilement</target>
+</phrase>
+<phrase>
+ <source>&amp;Restore</source>
+ <target>&amp;Restaurer</target>
+</phrase>
+<phrase>
+ <source>&amp;Move</source>
+ <target>&amp;Déplacer</target>
+</phrase>
+<phrase>
+ <source>New</source>
+ <target>Créer</target>
+</phrase>
+<phrase>
+ <source>Play</source>
+ <target>Lecture</target>
+</phrase>
+<phrase>
+ <source>Slider</source>
+ <target>Barre de défilement</target>
+</phrase>
+<phrase>
+ <source>&amp;Restore</source>
+ <target>&amp;Restaurer</target>
+</phrase>
+<phrase>
+ <source>&amp;Move</source>
+ <target>&amp;Déplacer</target>
+</phrase>
+<phrase>
+ <source>New</source>
+ <target>Créer</target>
+</phrase>
+<phrase>
+ <source>Play</source>
+ <target>Lecture</target>
+</phrase>
+<phrase>
+ <source>&amp;Redo</source>
+ <target>&amp;Refaire</target>
+</phrase>
+<phrase>
+ <source>Raised</source>
+ <target>Bombé</target>
+</phrase>
+<phrase>
+ <source>Sunken</source>
+ <target>Enfoncé</target>
+</phrase>
+<phrase>
+ <source>Run:</source>
+ <target>Exécution :</target>
+</phrase>
+</QPH>
diff --git a/src/linguist/phrasebooks/german.qph b/src/linguist/phrasebooks/german.qph
new file mode 100644
index 000000000..2be8c77bd
--- /dev/null
+++ b/src/linguist/phrasebooks/german.qph
@@ -0,0 +1,1075 @@
+<!DOCTYPE QPH><QPH language="de">
+<phrase>
+ <source>About</source>
+ <target>Info</target>
+</phrase>
+<phrase>
+ <source>access key</source>
+ <target>Zugriffstaste</target>
+</phrase>
+<phrase>
+ <source>accessibility</source>
+ <target>Eingabehilfe</target>
+</phrase>
+<phrase>
+ <source>action handle</source>
+ <target>Aktionspunkt</target>
+</phrase>
+<phrase>
+ <source>active</source>
+ <target>Aktiv</target>
+</phrase>
+<phrase>
+ <source>active end</source>
+ <target>Aktives Ende</target>
+</phrase>
+<phrase>
+ <source>active object</source>
+ <target>Aktives Objekt</target>
+</phrase>
+<phrase>
+ <source>active window</source>
+ <target>Aktives Fenster</target>
+</phrase>
+<phrase>
+ <source>adornment</source>
+ <target>Zubehör</target>
+</phrase>
+<phrase>
+ <source>Always on Top</source>
+ <target>Immer im Vordergrund</target>
+</phrase>
+<phrase>
+ <source>anchor point</source>
+ <target>Ankerpunkt</target>
+</phrase>
+<phrase>
+ <source>Apply</source>
+ <target>Zuweisen</target>
+</phrase>
+<phrase>
+ <source>auto-exit</source>
+ <target>Textfeld mit automatischer Freigabe</target>
+</phrase>
+<phrase>
+ <source>auto-repeat</source>
+ <target>Automatische Wiederholung</target>
+</phrase>
+<phrase>
+ <source>automatic link</source>
+ <target>Automatische OLE-Verknüpfung</target>
+</phrase>
+<phrase>
+ <source>automatic scrolling</source>
+ <target>Automatischer Bildlauf</target>
+</phrase>
+<phrase>
+ <source>autoscroll</source>
+ <target>Automatischer Bildlauf</target>
+</phrase>
+<phrase>
+ <source>Back</source>
+ <target>Zurück</target>
+</phrase>
+<phrase>
+ <source>barrel button</source>
+ <target>Pen-Knopf</target>
+ <definition>pen</definition>
+</phrase>
+<phrase>
+ <source>barrel-tap</source>
+ <target>Tippen mit Pen-Knopf</target>
+</phrase>
+<phrase>
+ <source>boxed edit</source>
+ <target>Texteditor</target>
+ <definition>control/Steuerelement</definition>
+</phrase>
+<phrase>
+ <source>Browse</source>
+ <target>Durchsuchen</target>
+</phrase>
+<phrase>
+ <source>Cancel</source>
+ <target>Abbrechen</target>
+</phrase>
+<phrase>
+ <source>cascading menu</source>
+ <target>Überlappendes Menü</target>
+</phrase>
+<phrase>
+ <source>check box</source>
+ <target>Kontrollkästchen</target>
+</phrase>
+<phrase>
+ <source>check mark</source>
+ <target>Markierung</target>
+ <definition>Kontrollkästchen</definition>
+</phrase>
+<phrase>
+ <source>child window</source>
+ <target>Untergeordnetes Fenster</target>
+</phrase>
+<phrase>
+ <source>choose</source>
+ <target>Wählen</target>
+</phrase>
+<phrase>
+ <source>click</source>
+ <target>Klicken</target>
+</phrase>
+<phrase>
+ <source>Clipboard</source>
+ <target>Zwischenablage</target>
+</phrase>
+<phrase>
+ <source>Close</source>
+ <target>Schließen</target>
+</phrase>
+<phrase>
+ <source>Close button</source>
+ <target>Schließen</target>
+ <definition>Schaltfläche</definition>
+</phrase>
+<phrase>
+ <source>collapse</source>
+ <target>Ausblenden</target>
+ <definition>outline/Gliederung</definition>
+</phrase>
+<phrase>
+ <source>column heading</source>
+ <target>Spaltenüberschrift</target>
+ <definition>control/Steuerelement</definition>
+</phrase>
+<phrase>
+ <source>combo box</source>
+ <target>Kombinationsfeld</target>
+</phrase>
+<phrase>
+ <source>command button</source>
+ <target>Schaltfläche</target>
+</phrase>
+<phrase>
+ <source>container</source>
+ <target>Container</target>
+</phrase>
+<phrase>
+ <source>context-sensitive Help</source>
+ <target>Kontextbezogene Hilfe</target>
+</phrase>
+<phrase>
+ <source>contextual</source>
+ <target>Kontextbezogen</target>
+</phrase>
+<phrase>
+ <source>control</source>
+ <target>Steuerelement</target>
+</phrase>
+<phrase>
+ <source>Copy</source>
+ <target>Kopieren</target>
+</phrase>
+<phrase>
+ <source>Copy here</source>
+ <target>Hierher kopieren</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut</source>
+ <target>Verknüpfung erstellen</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut Here</source>
+ <target>Hiermit verknüpfen</target>
+</phrase>
+<phrase>
+ <source>Cut</source>
+ <target>Ausschneiden</target>
+</phrase>
+<phrase>
+ <source>default</source>
+ <target>Standard</target>
+</phrase>
+<phrase>
+ <source>default button</source>
+ <target>Standardschaltfläche</target>
+</phrase>
+<phrase>
+ <source>Delete</source>
+ <target>Löschen</target>
+</phrase>
+<phrase>
+ <source>desktop</source>
+ <target>Desktop</target>
+</phrase>
+<phrase>
+ <source>destination</source>
+ <target>Ziel</target>
+</phrase>
+<phrase>
+ <source>dialog box</source>
+ <target>Dialogfeld</target>
+</phrase>
+<phrase>
+ <source>disability</source>
+ <target>Behinderung</target>
+</phrase>
+<phrase>
+ <source>disjoint selection</source>
+ <target>Nichtzusammenhängende Auswahl</target>
+</phrase>
+<phrase>
+ <source>dock</source>
+ <target>Verankern</target>
+</phrase>
+<phrase>
+ <source>document</source>
+ <target>Dokument</target>
+</phrase>
+<phrase>
+ <source>double-click</source>
+ <target>Doppelklicken</target>
+</phrase>
+<phrase>
+ <source>double-tap</source>
+ <target>Doppeltippen</target>
+</phrase>
+<phrase>
+ <source>drag</source>
+ <target>Ziehen</target>
+</phrase>
+<phrase>
+ <source>drag-and-drop</source>
+ <target>Drag &amp; Drop</target>
+</phrase>
+<phrase>
+ <source>drop-down combo box</source>
+ <target>Dropdown-Kombinationsfeld</target>
+</phrase>
+<phrase>
+ <source>drop-down list box</source>
+ <target>Dropdown-Listenfeld</target>
+</phrase>
+<phrase>
+ <source>drop-down menu</source>
+ <target>Dropdown-Menü</target>
+</phrase>
+<phrase>
+ <source>Edit</source>
+ <target>Bearbeiten</target>
+</phrase>
+<phrase>
+ <source>Edit</source>
+ <target>Bearbeiten</target>
+ <definition>menu/Menü</definition>
+</phrase>
+<phrase>
+ <source>ellipsis</source>
+ <target>Auslassungspunkte</target>
+</phrase>
+<phrase>
+ <source>embedded object</source>
+ <target>Eingebettetes</target>
+</phrase>
+<phrase>
+ <source>Exit</source>
+ <target>Beenden</target>
+</phrase>
+<phrase>
+ <source>expand</source>
+ <target>Einblenden</target>
+ <definition>an outline/einer Struktur</definition>
+</phrase>
+<phrase>
+ <source>Explore</source>
+ <target>Explorer</target>
+ <definition>Befehl</definition>
+</phrase>
+<phrase>
+ <source>extended selection</source>
+ <target>Erweiterte Auswahl</target>
+</phrase>
+<phrase>
+ <source>extended selection list box</source>
+ <target>Listenfeld für erweiterte Auswahl</target>
+</phrase>
+<phrase>
+ <source>File</source>
+ <target>Datei</target>
+ <definition>menu</definition>
+</phrase>
+<phrase>
+ <source>file</source>
+ <target>Datei</target>
+</phrase>
+<phrase>
+ <source>Find</source>
+ <target>Suchen</target>
+</phrase>
+<phrase>
+ <source>Find Next</source>
+ <target>Weitersuchen</target>
+</phrase>
+<phrase>
+ <source>Find What</source>
+ <target>Suchen nach</target>
+</phrase>
+<phrase>
+ <source>folder</source>
+ <target>Ordner</target>
+</phrase>
+<phrase>
+ <source>font</source>
+ <target>Schriftart</target>
+</phrase>
+<phrase>
+ <source>font size</source>
+ <target>Schriftgrad</target>
+</phrase>
+<phrase>
+ <source>font style</source>
+ <target>Schriftschnitt</target>
+</phrase>
+<phrase>
+ <source>function key</source>
+ <target>Funktionstaste</target>
+</phrase>
+<phrase>
+ <source>gesture</source>
+ <target>Schriftzug</target>
+</phrase>
+<phrase>
+ <source>glyph</source>
+ <target>Zeichen</target>
+</phrase>
+<phrase>
+ <source>group box</source>
+ <target>Gruppenfeld</target>
+</phrase>
+<phrase>
+ <source>handle</source>
+ <target>Ziehpunkt</target>
+</phrase>
+<phrase>
+ <source>Help</source>
+ <target>?</target>
+ <definition>menu</definition>
+</phrase>
+<phrase>
+ <source>Help</source>
+ <target>Hilfe</target>
+</phrase>
+<phrase>
+ <source>Hide</source>
+ <target>Ausblenden</target>
+</phrase>
+<phrase>
+ <source>hierarchical selection</source>
+ <target>Hierarchische Auswahl</target>
+</phrase>
+<phrase>
+ <source>hold</source>
+ <target>Halten</target>
+</phrase>
+<phrase>
+ <source>hot spot</source>
+ <target>Hot Spot</target>
+</phrase>
+<phrase>
+ <source>hot zone</source>
+ <target>Hot Zone</target>
+</phrase>
+<phrase>
+ <source>icon</source>
+ <target>Symbol</target>
+</phrase>
+<phrase>
+ <source>inactive</source>
+ <target>Inaktiv</target>
+</phrase>
+<phrase>
+ <source>inactive window</source>
+ <target>Inaktives Fenster</target>
+</phrase>
+<phrase>
+ <source>ink</source>
+ <target>Ink</target>
+</phrase>
+<phrase>
+ <source>ink edit</source>
+ <target>Inkeditor</target>
+</phrase>
+<phrase>
+ <source>input focus</source>
+ <target>Eingabefokus</target>
+</phrase>
+<phrase>
+ <source>Insert</source>
+ <target>Einfügen</target>
+ <definition>Menü</definition>
+</phrase>
+<phrase>
+ <source>Insert Object</source>
+ <target>Objekt einfügen</target>
+</phrase>
+<phrase>
+ <source>insertion point</source>
+ <target>Einfügemarke</target>
+</phrase>
+<phrase>
+ <source>italic</source>
+ <target>Kursiv</target>
+</phrase>
+<phrase>
+ <source>label</source>
+ <target>Bezeichnung</target>
+</phrase>
+<phrase>
+ <source>landscape</source>
+ <target>Querformat</target>
+</phrase>
+<phrase>
+ <source>lasso-tap</source>
+ <target>Lasso-tippen</target>
+</phrase>
+<phrase>
+ <source>lens</source>
+ <target>Lupe</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>Verknüpfung</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>Verknüpfen</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>Link Here</source>
+ <target>Hiermit verknüpfen</target>
+</phrase>
+<phrase>
+ <source>list box</source>
+ <target>Listenfeld</target>
+</phrase>
+<phrase>
+ <source>list view</source>
+ <target>Listenansicht</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>manual link</source>
+ <target>Manuelle OLE-Verknüpfung</target>
+</phrase>
+<phrase>
+ <source>Maximize</source>
+ <target>Maximieren</target>
+</phrase>
+<phrase>
+ <source>maximize button</source>
+ <target>Maximieren</target>
+ <definition>Schaltfläche</definition>
+</phrase>
+<phrase>
+ <source>MDI</source>
+ <target>MDI</target>
+</phrase>
+<phrase>
+ <source>menu</source>
+ <target>Menü</target>
+</phrase>
+<phrase>
+ <source>menu bar</source>
+ <target>Menüleiste</target>
+</phrase>
+<phrase>
+ <source>menu button</source>
+ <target>Menü</target>
+ <definition>Schaltfläche</definition>
+</phrase>
+<phrase>
+ <source>menu item</source>
+ <target>Menüelement</target>
+</phrase>
+<phrase>
+ <source>menu title</source>
+ <target>Menütitel</target>
+</phrase>
+<phrase>
+ <source>message box</source>
+ <target>Meldungsfeld</target>
+</phrase>
+<phrase>
+ <source>Minimize</source>
+ <target>Minimieren</target>
+</phrase>
+<phrase>
+ <source>minimize button</source>
+ <target>Minimieren</target>
+ <definition>Schaltfläche</definition>
+</phrase>
+<phrase>
+ <source>mixed-value</source>
+ <target>Gemischt</target>
+</phrase>
+<phrase>
+ <source>modal</source>
+ <target>Modal</target>
+</phrase>
+<phrase>
+ <source>mode</source>
+ <target>Modus</target>
+</phrase>
+<phrase>
+ <source>modeless</source>
+ <target>Interaktiv</target>
+</phrase>
+<phrase>
+ <source>modifier key</source>
+ <target>Zusatztaste</target>
+</phrase>
+<phrase>
+ <source>mouse</source>
+ <target>Maus</target>
+</phrase>
+<phrase>
+ <source>Move</source>
+ <target>Verschieben</target>
+</phrase>
+<phrase>
+ <source>Move Here</source>
+ <target>Hierher verschieben</target>
+</phrase>
+<phrase>
+ <source>Multiple Document Interface</source>
+ <target>MDI</target>
+ <definition>Multiple Document Interface</definition>
+</phrase>
+<phrase>
+ <source>multiple selection list box</source>
+ <target>Listenfeld für Mehrfachauswahl</target>
+</phrase>
+<phrase>
+ <source>My Computer</source>
+ <target>Arbeitsplatz</target>
+ <definition>icon/Symbol</definition>
+</phrase>
+<phrase>
+ <source>Network Neighborhood</source>
+ <target>Netzwerk</target>
+ <definition>icon/Symbol</definition>
+</phrase>
+<phrase>
+ <source>New</source>
+ <target>Neu</target>
+</phrase>
+<phrase>
+ <source>Next</source>
+ <target>Weiter</target>
+</phrase>
+<phrase>
+ <source>object</source>
+ <target>Objekt</target>
+</phrase>
+<phrase>
+ <source>OK</source>
+ <target>OK</target>
+</phrase>
+<phrase>
+ <source>OLE</source>
+ <target>OLE</target>
+</phrase>
+<phrase>
+ <source>OLE drag and drop</source>
+ <target>OLE-Drag &amp; Drop</target>
+</phrase>
+<phrase>
+ <source>OLE embedded object</source>
+ <target>Eingebettetes OLE-Objekt</target>
+</phrase>
+<phrase>
+ <source>OLE linked object</source>
+ <target>Verknüpftes OLE-Objekt</target>
+</phrase>
+<phrase>
+ <source>OLE nondefault drag and drop</source>
+ <target>Vom Standard abweichendes OLE-Drag &amp; Drop</target>
+</phrase>
+<phrase>
+ <source>OLE nondefault drag and drop</source>
+ <target>Aangepast slepen en neerzetten via OLE</target>
+</phrase>
+<phrase>
+ <source>Open</source>
+ <target>Öffnen</target>
+</phrase>
+<phrase>
+ <source>Open With</source>
+ <target>Öffnen mit</target>
+</phrase>
+<phrase>
+ <source>option button</source>
+ <target>Optionsfeld</target>
+</phrase>
+<phrase>
+ <source>option-set</source>
+ <target>Aktivierte Option</target>
+</phrase>
+<phrase>
+ <source>package</source>
+ <target>Paket</target>
+</phrase>
+<phrase>
+ <source>Page Setup</source>
+ <target>Seite einrichten</target>
+</phrase>
+<phrase>
+ <source>palette window</source>
+ <target>Palettenfenster</target>
+</phrase>
+<phrase>
+ <source>pane</source>
+ <target>Fensterbereich</target>
+</phrase>
+<phrase>
+ <source>parent window</source>
+ <target>Übergeordnetes Fenster</target>
+</phrase>
+<phrase>
+ <source>password</source>
+ <target>Kennwort</target>
+</phrase>
+<phrase>
+ <source>Paste</source>
+ <target>Einfügen</target>
+</phrase>
+<phrase>
+ <source>Paste Link</source>
+ <target>Verknüpfung einfügen</target>
+</phrase>
+<phrase>
+ <source>Paste Shortcut</source>
+ <target>Verknüpfung einfügen</target>
+</phrase>
+<phrase>
+ <source>Paste Special</source>
+ <target>Inhalte einfügen</target>
+</phrase>
+<phrase>
+ <source>path</source>
+ <target>Pfad</target>
+</phrase>
+<phrase>
+ <source>Pause</source>
+ <target>Anhalten</target>
+</phrase>
+<phrase>
+ <source>pen</source>
+ <target>Pen</target>
+</phrase>
+<phrase>
+ <source>Play</source>
+ <target>Wiedergeben</target>
+</phrase>
+<phrase>
+ <source>Plug and Play</source>
+ <target>Plug &amp; Play</target>
+</phrase>
+<phrase>
+ <source>point</source>
+ <target>Zeigen</target>
+</phrase>
+<phrase>
+ <source>pointer</source>
+ <target>Zeiger</target>
+</phrase>
+<phrase>
+ <source>pop-up menu</source>
+ <target>Kontextmenü</target>
+</phrase>
+<phrase>
+ <source>pop-up window</source>
+ <target>Popup-Fenster</target>
+</phrase>
+<phrase>
+ <source>portrait</source>
+ <target>Hochformat</target>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>Drücken</target>
+ <definition>and hold a mouse button/und Halten einer Maustaste</definition>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>Drücken</target>
+ <definition>a key/einer Taste</definition>
+</phrase>
+<phrase>
+ <source>primary container</source>
+ <target>Primär-Container</target>
+</phrase>
+<phrase>
+ <source>primary window</source>
+ <target>Primärfenster</target>
+</phrase>
+<phrase>
+ <source>Print</source>
+ <target>Drucken</target>
+</phrase>
+<phrase>
+ <source>printer</source>
+ <target>Drucker</target>
+</phrase>
+<phrase>
+ <source>progress indicator</source>
+ <target>Statusanzeige</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>project</source>
+ <target>Projekt</target>
+</phrase>
+<phrase>
+ <source>Properties</source>
+ <target>Eigenschaften</target>
+</phrase>
+<phrase>
+ <source>property inspector</source>
+ <target>Eigenschaftenanzeige</target>
+</phrase>
+<phrase>
+ <source>property page</source>
+ <target>Eigenschaftengruppe</target>
+</phrase>
+<phrase>
+ <source>property sheet</source>
+ <target>Eigenschaftenfenster</target>
+</phrase>
+<phrase>
+ <source>property sheet control</source>
+ <target>Eigenschaftenfenster-Steuerelement</target>
+</phrase>
+<phrase>
+ <source>Quick View</source>
+ <target>Schnellansicht</target>
+</phrase>
+<phrase>
+ <source>read-only</source>
+ <target>Schreibgeschützt</target>
+</phrase>
+<phrase>
+ <source>recognition</source>
+ <target>Schrifterkennung</target>
+</phrase>
+<phrase>
+ <source>Recycle Bin</source>
+ <target>Papierkorb</target>
+ <definition>Icon/Symbol</definition>
+</phrase>
+<phrase>
+ <source>Redo</source>
+ <target>Wiederherstellen</target>
+</phrase>
+<phrase>
+ <source>region selection</source>
+ <target>Bereichsauswahl</target>
+</phrase>
+<phrase>
+ <source>registry</source>
+ <target>Registrierung</target>
+</phrase>
+<phrase>
+ <source>Repeat</source>
+ <target>Wiederholen</target>
+</phrase>
+<phrase>
+ <source>Replace</source>
+ <target>Ersetzen</target>
+</phrase>
+<phrase>
+ <source>Restore</source>
+ <target>Wiederherstellen</target>
+</phrase>
+<phrase>
+ <source>Restore button</source>
+ <target>Wiederherstellen </target>
+ <definition>Schaltfläche</definition>
+</phrase>
+<phrase>
+ <source>Resume</source>
+ <target>Fortsetzen</target>
+</phrase>
+<phrase>
+ <source>Retry</source>
+ <target>Wiederholen</target>
+</phrase>
+<phrase>
+ <source>rich-text box</source>
+ <target>RTF-Textfeld</target>
+</phrase>
+<phrase>
+ <source>Run</source>
+ <target>Ausführen</target>
+</phrase>
+<phrase>
+ <source>Save</source>
+ <target>Speichern</target>
+</phrase>
+<phrase>
+ <source>Save as</source>
+ <target>Speichern unter</target>
+</phrase>
+<phrase>
+ <source>scroll</source>
+ <target>Bildlauf durchführen</target>
+</phrase>
+<phrase>
+ <source>scroll arrow</source>
+ <target>Bildlaufpfeil</target>
+</phrase>
+<phrase>
+ <source>scroll bar</source>
+ <target>Bildlaufleiste</target>
+</phrase>
+<phrase>
+ <source>scroll box</source>
+ <target>Bildlauffeld</target>
+</phrase>
+<phrase>
+ <source>secondary window</source>
+ <target>Sekundärfenster</target>
+</phrase>
+<phrase>
+ <source>select</source>
+ <target>Auswählen</target>
+</phrase>
+<phrase>
+ <source>Select All</source>
+ <target>Alle markieren</target>
+</phrase>
+<phrase>
+ <source>selection</source>
+ <target>Auswahl</target>
+</phrase>
+<phrase>
+ <source>selection handle</source>
+ <target>Auswahlpunkt</target>
+</phrase>
+<phrase>
+ <source>Send To</source>
+ <target>Senden an</target>
+</phrase>
+<phrase>
+ <source>separator</source>
+ <target>Trennelement</target>
+</phrase>
+<phrase>
+ <source>Settings</source>
+ <target>Einstellungen</target>
+</phrase>
+<phrase>
+ <source>Setup</source>
+ <target>Einrichten</target>
+</phrase>
+<phrase>
+ <source>shortcut</source>
+ <target>Verknüpfung</target>
+</phrase>
+<phrase>
+ <source>shortcut button</source>
+ <target>Verknüpfte Schaltfläche</target>
+</phrase>
+<phrase>
+ <source>shortcut icon</source>
+ <target>Verknüpfungssymbol</target>
+</phrase>
+<phrase>
+ <source>shortcut key</source>
+ <target>Tastenkombination</target>
+</phrase>
+<phrase>
+ <source>shortcut key control</source>
+ <target>Steuerelement für Tastenbelegung</target>
+</phrase>
+<phrase>
+ <source>Show</source>
+ <target>Anzeigen</target>
+</phrase>
+<phrase>
+ <source>Shutdown</source>
+ <target>Beenden</target>
+</phrase>
+<phrase>
+ <source>single selection list box</source>
+ <target>Listenfeld für Einfachauswahl</target>
+</phrase>
+<phrase>
+ <source>Size</source>
+ <target>Größe ändern</target>
+</phrase>
+<phrase>
+ <source>size grip</source>
+ <target>Element für Größenänderung</target>
+</phrase>
+<phrase>
+ <source>slider</source>
+ <target>Schieber</target>
+</phrase>
+<phrase>
+ <source>spin box</source>
+ <target>Drehfeld</target>
+</phrase>
+<phrase>
+ <source>Split</source>
+ <target>Teilen</target>
+</phrase>
+<phrase>
+ <source>split bar</source>
+ <target>Fensterteiler</target>
+</phrase>
+<phrase>
+ <source>split box</source>
+ <target>Teilungsfeld</target>
+</phrase>
+<phrase>
+ <source>Start button</source>
+ <target>Start</target>
+ <definition>Schaltfläche</definition>
+</phrase>
+<phrase>
+ <source>StartUp folder</source>
+ <target>Autostart</target>
+ <definition>Ordner</definition>
+</phrase>
+<phrase>
+ <source>status bar</source>
+ <target>Statusleiste</target>
+</phrase>
+<phrase>
+ <source>Stop</source>
+ <target>Beenden</target>
+</phrase>
+<phrase>
+ <source>tab control</source>
+ <target>Register</target>
+</phrase>
+<phrase>
+ <source>tap</source>
+ <target>Tippen</target>
+</phrase>
+<phrase>
+ <source>task bar</source>
+ <target>Task-Leiste</target>
+</phrase>
+<phrase>
+ <source>task-oriented Help</source>
+ <target>Vorgangsbezogene Hilfe</target>
+</phrase>
+<phrase>
+ <source>template</source>
+ <target>Vorlage</target>
+</phrase>
+<phrase>
+ <source>text box</source>
+ <target>Textfeld</target>
+</phrase>
+<phrase>
+ <source>title bar</source>
+ <target>Titelleiste</target>
+</phrase>
+<phrase>
+ <source>title text</source>
+ <target>Titeltext</target>
+</phrase>
+<phrase>
+ <source>toggle key</source>
+ <target>Ein-/Aus-Taste</target>
+</phrase>
+<phrase>
+ <source>toolbar</source>
+ <target>Symbolleiste</target>
+</phrase>
+<phrase>
+ <source>tooltip</source>
+ <target>QuickInfo</target>
+</phrase>
+<phrase>
+ <source>tree view control</source>
+ <target>Strukturansicht</target>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>Eingeben</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>Typ</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>unavailable</source>
+ <target>Nicht verfügbar</target>
+</phrase>
+<phrase>
+ <source>Undo</source>
+ <target>Rückgängig</target>
+</phrase>
+<phrase>
+ <source>Uninstall</source>
+ <target>Deinstallieren</target>
+</phrase>
+<phrase>
+ <source>View</source>
+ <target>Ansicht</target>
+ <definition>Menü</definition>
+</phrase>
+<phrase>
+ <source>visual editing</source>
+ <target>Direkte Bearbeitung</target>
+</phrase>
+<phrase>
+ <source>well control</source>
+ <target>Steuerelement zur Grafikanzeige</target>
+</phrase>
+<phrase>
+ <source>What&apos;s This?</source>
+ <target>Direkthilfe</target>
+</phrase>
+<phrase>
+ <source>Window</source>
+ <target>Fenster</target>
+ <definition>Menü</definition>
+</phrase>
+<phrase>
+ <source>window</source>
+ <target>Fenster</target>
+</phrase>
+<phrase>
+ <source>Windows Explorer</source>
+ <target>Explorer</target>
+</phrase>
+<phrase>
+ <source>wizard</source>
+ <target>Assistent</target>
+</phrase>
+<phrase>
+ <source>workbook</source>
+ <target>Arbeitsmappe</target>
+</phrase>
+<phrase>
+ <source>workgroup</source>
+ <target>Arbeitsgruppe</target>
+</phrase>
+<phrase>
+ <source>workspace</source>
+ <target>Arbeitsbereich</target>
+</phrase>
+<phrase>
+ <source>Yes</source>
+ <target>Ja</target>
+</phrase>
+</QPH>
diff --git a/src/linguist/phrasebooks/hungarian.qph b/src/linguist/phrasebooks/hungarian.qph
new file mode 100644
index 000000000..0e1dd122a
--- /dev/null
+++ b/src/linguist/phrasebooks/hungarian.qph
@@ -0,0 +1,752 @@
+<!DOCTYPE QPH>
+<QPH language="hu_HU">
+<phrase>
+ <source>OK</source>
+ <target>OK</target>
+</phrase>
+<phrase>
+ <source>Bookmarks</source>
+ <target>Könyvjelzők</target>
+</phrase>
+<phrase>
+ <source>Remove</source>
+ <target>Törlés</target>
+</phrase>
+<phrase>
+ <source>New Folder</source>
+ <target>Új könyvtár</target>
+</phrase>
+<phrase>
+ <source>Add</source>
+ <target>Hozzáadás</target>
+</phrase>
+<phrase>
+ <source>Previous</source>
+ <target>Előző</target>
+</phrase>
+<phrase>
+ <source>Font</source>
+ <target>Betűtípus</target>
+</phrase>
+<phrase>
+ <source>Help</source>
+ <target>Segítség</target>
+</phrase>
+<phrase>
+ <source>Done</source>
+ <target>Kész</target>
+</phrase>
+<phrase>
+ <source>Install</source>
+ <target>Telepítés</target>
+</phrase>
+<phrase>
+ <source>Cancel</source>
+ <target>Mégsem</target>
+</phrase>
+<phrase>
+ <source>Installation Path:</source>
+ <target>Telepítési útvonal:</target>
+</phrase>
+<phrase>
+ <source>Index</source>
+ <target>Index</target>
+</phrase>
+<phrase>
+ <source>Contents</source>
+ <target>Tartalom</target>
+</phrase>
+<phrase>
+ <source>Search</source>
+ <target>Keresés</target>
+</phrase>
+<phrase>
+ <source>Print Preview...</source>
+ <target>Nyomtatási kép...</target>
+</phrase>
+<phrase>
+ <source>About...</source>
+ <target>Névjegy...</target>
+</phrase>
+<phrase>
+ <source>Toolbars</source>
+ <target>Eszköztárak</target>
+</phrase>
+<phrase>
+ <source>Address:</source>
+ <target>Cím:</target>
+</phrase>
+<phrase>
+ <source>Open Source Edition</source>
+ <target>Nyíltforrású kiadás</target>
+</phrase>
+<phrase>
+ <source>Yes</source>
+ <target>Igen</target>
+</phrase>
+<phrase>
+ <source>No</source>
+ <target>Nem</target>
+</phrase>
+<phrase>
+ <source>Apply</source>
+ <target>Alkalmaz</target>
+</phrase>
+<phrase>
+ <source>Ignore</source>
+ <target>Kihagyás</target>
+</phrase>
+<phrase>
+ <source>Abort</source>
+ <target>Megszakítás</target>
+</phrase>
+<phrase>
+ <source>Play</source>
+ <target>Lejátszás</target>
+</phrase>
+<phrase>
+ <source>Pause</source>
+ <target>Szünet</target>
+</phrase>
+<phrase>
+ <source>File</source>
+ <target>Fájl</target>
+ <definition>menu</definition>
+</phrase>
+<phrase>
+ <source>file</source>
+ <target>fájl</target>
+</phrase>
+<phrase>
+ <source>directory</source>
+ <target>könyvtár</target>
+</phrase>
+<phrase>
+ <source>Symlink</source>
+ <target>Szimbolikus link</target>
+</phrase>
+<phrase>
+ <source>Delete</source>
+ <target>Törlés</target>
+</phrase>
+<phrase>
+ <source>Rename</source>
+ <target>Átnevezés</target>
+</phrase>
+<phrase>
+ <source>Back</source>
+ <target>Vissza</target>
+</phrase>
+<phrase>
+ <source>Forward</source>
+ <target>Előre</target>
+</phrase>
+<phrase>
+ <source>Finish</source>
+ <target>Befejezés</target>
+</phrase>
+<phrase>
+ <source>Quit</source>
+ <target>Kilépés</target>
+</phrase>
+<phrase>
+ <source>Exit</source>
+ <target>Kilépés</target>
+</phrase>
+<phrase>
+ <source>Close</source>
+ <target>Bezárás</target>
+</phrase>
+<phrase>
+ <source>Close All</source>
+ <target>Mindent bezár</target>
+</phrase>
+<phrase>
+ <source>Error</source>
+ <target>Hiba</target>
+</phrase>
+<phrase>
+ <source>Warning</source>
+ <target>Figyelmeztetés</target>
+</phrase>
+<phrase>
+ <source>Information</source>
+ <target>Információ</target>
+</phrase>
+<phrase>
+ <source>Question</source>
+ <target>Kérdés</target>
+</phrase>
+<phrase>
+ <source>document</source>
+ <target>dokumentum</target>
+</phrase>
+<phrase>
+ <source>author</source>
+ <target>szerző</target>
+</phrase>
+<phrase>
+ <source>subsidiary(-ies)</source>
+ <target>leányvállalata(i)</target>
+</phrase>
+<phrase>
+ <source>Help</source>
+ <target>Súgó</target>
+</phrase>
+<phrase>
+ <source>Undo</source>
+ <target>Visszavonás</target>
+</phrase>
+<phrase>
+ <source>Redo</source>
+ <target>Újra</target>
+</phrase>
+<phrase>
+ <source>Open</source>
+ <target>Megnyitás</target>
+</phrase>
+<phrase>
+ <source>Save</source>
+ <target>Mentés</target>
+</phrase>
+<phrase>
+ <source>Save all</source>
+ <target>Mindet menti</target>
+</phrase>
+<phrase>
+ <source>click</source>
+ <target>kattintás</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>click</source>
+ <target>kattintson</target>
+ <definition>ask</definition>
+</phrase>
+<phrase>
+ <source>Recent</source>
+ <target>Előző</target>
+ <definition>Recent files/Előző fájlok</definition>
+</phrase>
+<phrase>
+ <source>Window</source>
+ <target>Ablak</target>
+</phrase>
+<phrase>
+ <source>Cascade</source>
+ <target>Lépcsőzetes</target>
+</phrase>
+<phrase>
+ <source>Tile</source>
+ <target>Mozaikszerű</target>
+</phrase>
+<phrase>
+ <source>Game</source>
+ <target>Játék</target>
+</phrase>
+<phrase>
+ <source>New Game</source>
+ <target>Új játék</target>
+</phrase>
+<phrase>
+ <source>Untitled</source>
+ <target>Névtelen</target>
+</phrase>
+<phrase>
+ <source>Unknown error</source>
+ <target>Ismeretlen hiba</target>
+</phrase>
+<phrase>
+ <source>New Folder</source>
+ <target>Új mappa</target>
+</phrase>
+<phrase>
+ <source>folder</source>
+ <target>mappa</target>
+</phrase>
+<phrase>
+ <source>New Directory</source>
+ <target>Új könyvtár</target>
+</phrase>
+<phrase>
+ <source>unknown</source>
+ <target>ismeretlen</target>
+</phrase>
+<phrase>
+ <source>Copy</source>
+ <target>Másolás</target>
+</phrase>
+<phrase>
+ <source>Cut</source>
+ <target>Kivágás</target>
+</phrase>
+<phrase>
+ <source>Paste</source>
+ <target>Beillesztés</target>
+</phrase>
+<phrase>
+ <source>Paste special</source>
+ <target>Speciális beillesztés</target>
+</phrase>
+<phrase>
+ <source>Insert</source>
+ <target>Beszúrás</target>
+</phrase>
+<phrase>
+ <source>Copy here</source>
+ <target>Másolás ide</target>
+</phrase>
+<phrase>
+ <source>Minimize</source>
+ <target>Minimalizálás</target>
+</phrase>
+<phrase>
+ <source>Zoom</source>
+ <target>Nagyítás</target>
+</phrase>
+<phrase>
+ <source>&amp;Edit</source>
+ <target>S&amp;zerkesztés</target>
+</phrase>
+<phrase>
+ <source>&amp;View</source>
+ <target>&amp;Nézet</target>
+</phrase>
+<phrase>
+ <source>Application</source>
+ <target>Alkalmazás</target>
+</phrase>
+<phrase>
+ <source>Documentation</source>
+ <target>Dokumentáció</target>
+</phrase>
+<phrase>
+ <source>Network</source>
+ <target>Hálózat</target>
+</phrase>
+<phrase>
+ <source>Current Page</source>
+ <target>Aktuális oldal</target>
+</phrase>
+<phrase>
+ <source>Find</source>
+ <target>Keresés</target>
+</phrase>
+<phrase>
+ <source>Locale</source>
+ <target>Nyelv</target>
+</phrase>
+<phrase>
+ <source>Cannot open &apos;%1&apos;.</source>
+ <target>&apos;%1&apos; nem nyitható meg.</target>
+</phrase>
+<phrase>
+ <source>Cannot find the string &apos;%1&apos;.</source>
+ <target>A szöveg nem található: &apos;%1&apos;.</target>
+</phrase>
+<phrase>
+ <source>A file called &apos;%1&apos; already exists. Please choose another name.</source>
+ <target>&apos;%1&apos; nevű fájl már létezik. Válasszon egy másik nevet.</target>
+</phrase>
+<phrase>
+ <source>The program is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.</source>
+ <target>Ez a program abban a reményben került közreadásra, hogy hasznos lesz, de minden egyéb GARANCIA NÉLKÜL, az ELADHATÓSÁGRA vagy VALAMELY CÉLRA VALÓ ALKALMAZHATÓSÁGRA való származtatott garanciát is beleértve.</target>
+ <definition>License text in most Qt programs</definition>
+</phrase>
+<phrase>
+ <source>Open Link</source>
+ <target>Link megnyitása</target>
+</phrase>
+<phrase>
+ <source>Next</source>
+ <target>Következő</target>
+</phrase>
+<phrase>
+ <source>Case Sensitive</source>
+ <target>Kis/nagybetű érzékeny</target>
+</phrase>
+<phrase>
+ <source>Whole words</source>
+ <target>Teljes szó</target>
+</phrase>
+<phrase>
+ <source>Open Link in New Tab</source>
+ <target>Link megnyitása új lapon</target>
+</phrase>
+<phrase>
+ <source>Download canceled.</source>
+ <target>Letöltés megszakítva.</target>
+</phrase>
+<phrase>
+ <source>The file %1 already exists. Do you want to overwrite it?</source>
+ <target>A(z) &apos;%1&apos; nevű fájl már létezik. Felülírja?</target>
+</phrase>
+<phrase>
+ <source>Qt Assistant</source>
+ <target>Qt Asszisztens</target>
+</phrase>
+<phrase>
+ <source>Preferences...</source>
+ <target>Beállítások...</target>
+</phrase>
+<phrase>
+ <source>&amp;File</source>
+ <target>&amp;Fájl</target>
+</phrase>
+<phrase>
+ <source>Browser</source>
+ <target>Böngésző</target>
+</phrase>
+<phrase>
+ <source>Filters</source>
+ <target>Szűrők</target>
+</phrase>
+<phrase>
+ <source>Delete Folder</source>
+ <target>Mappa törlése</target>
+</phrase>
+<phrase>
+ <source>Rename Folder</source>
+ <target>Mappa átnevezése</target>
+</phrase>
+<phrase>
+ <source>You need a commercial Qt license for development of proprietary (closed source) applications. Please see &lt;a href=&quot;http://www.trolltech.com/company/model.html&quot;&gt;www.trolltech.com/company/model.html&lt;/a&gt; for an overview of Qt licensing.</source>
+ <target>Amennyiben zárt forrású alkalmazás fejlesztéséhez kívánja a Qt-t felhasználni, szüksége lesz a Qt kereskedelmi verziójára. Kérem tekintse meg az &lt;tt&gt;http://www.trolltech.com/company/model.html&lt;/tt&gt; oldalt a Qt licenszelésének áttekintéséhez.</target>
+</phrase>
+<phrase>
+ <source>New Tab</source>
+ <target>Új fül</target>
+</phrase>
+<phrase>
+ <source>Close Tab</source>
+ <target>Fül bezárása</target>
+</phrase>
+<phrase>
+ <source>Close Other Tabs</source>
+ <target>A többi fül bezárása</target>
+</phrase>
+<phrase>
+ <source>True</source>
+ <target>Igaz</target>
+</phrase>
+<phrase>
+ <source>False</source>
+ <target>Hamis</target>
+</phrase>
+<phrase>
+ <source>Update</source>
+ <target>Frissítés</target>
+</phrase>
+<phrase>
+ <source>Normal</source>
+ <target>Normál</target>
+</phrase>
+<phrase>
+ <source>Bold</source>
+ <target>Félkövér</target>
+</phrase>
+<phrase>
+ <source>Italic</source>
+ <target>Dőlt</target>
+</phrase>
+<phrase>
+ <source>Greek</source>
+ <target>Görög</target>
+</phrase>
+<phrase>
+ <source>Cyrillic</source>
+ <target>Cirill</target>
+</phrase>
+<phrase>
+ <source>Armenian</source>
+ <target>Örmény</target>
+</phrase>
+<phrase>
+ <source>Hebrew</source>
+ <target>Héber</target>
+</phrase>
+<phrase>
+ <source>Arabic</source>
+ <target>Arab</target>
+</phrase>
+<phrase>
+ <source>Syriac</source>
+ <target>Szír</target>
+</phrase>
+<phrase>
+ <source>Permission denied</source>
+ <target>Hozzáférés megtagadva</target>
+</phrase>
+<phrase>
+ <source>Too many open files</source>
+ <target>Túl sok fájl van nyitva</target>
+</phrase>
+<phrase>
+ <source>No such file or directory</source>
+ <target>Nincs ilyen fájl vagy könyvtár</target>
+</phrase>
+<phrase>
+ <source>No space left on device</source>
+ <target>Nincs több hely az eszközön</target>
+</phrase>
+<phrase>
+ <source>Width:</source>
+ <target>Szélesség:</target>
+</phrase>
+<phrase>
+ <source>Height:</source>
+ <target>Magasság:</target>
+</phrase>
+<phrase>
+ <source>Margins</source>
+ <target>Margók</target>
+</phrase>
+<phrase>
+ <source>top margin</source>
+ <target>felső margó</target>
+</phrase>
+<phrase>
+ <source>left margin</source>
+ <target>bal margó</target>
+</phrase>
+<phrase>
+ <source>right margin</source>
+ <target>jobb margó</target>
+</phrase>
+<phrase>
+ <source>bottom margin</source>
+ <target>alsó margó</target>
+</phrase>
+<phrase>
+ <source>Next page</source>
+ <target>Következő oldal</target>
+</phrase>
+<phrase>
+ <source>Previous page</source>
+ <target>Előző oldal</target>
+</phrase>
+<phrase>
+ <source>First page</source>
+ <target>Első oldal</target>
+</phrase>
+<phrase>
+ <source>Last page</source>
+ <target>Utolsó oldal</target>
+</phrase>
+<phrase>
+ <source>Fit width</source>
+ <target>Szélesség igazítása</target>
+</phrase>
+<phrase>
+ <source>Fit page</source>
+ <target>Oldal igazítása</target>
+</phrase>
+<phrase>
+ <source>Zoom in</source>
+ <target>Nagyítás</target>
+</phrase>
+<phrase>
+ <source>Zoom out</source>
+ <target>Kicsinyítés</target>
+</phrase>
+<phrase>
+ <source>Portrait</source>
+ <target>Álló</target>
+</phrase>
+<phrase>
+ <source>Landscape</source>
+ <target>Fekvő</target>
+</phrase>
+<phrase>
+ <source>Print</source>
+ <target>Nyomtatás</target>
+</phrase>
+<phrase>
+ <source>Page setup</source>
+ <target>Oldalbeállítás</target>
+</phrase>
+<phrase>
+ <source>Color</source>
+ <target>Szín</target>
+</phrase>
+<phrase>
+ <source>Grayscale</source>
+ <target>Szürkeárnyalatos</target>
+</phrase>
+<phrase>
+ <source>None</source>
+ <target>Nincs</target>
+</phrase>
+<phrase>
+ <source>...</source>
+ <target>...</target>
+</phrase>
+<phrase>
+ <source>Menu</source>
+ <target>Menü</target>
+</phrase>
+<phrase>
+ <source>Keep</source>
+ <target>Meghagy</target>
+</phrase>
+<phrase>
+ <source>Pause</source>
+ <target>Megállít</target>
+</phrase>
+<phrase>
+ <source>Log</source>
+ <target>Napló</target>
+</phrase>
+<phrase>
+ <source>Mi&amp;nimize</source>
+ <target>&amp;Kis méret</target>
+</phrase>
+<phrase>
+ <source>Press</source>
+ <target>Nyomja meg</target>
+</phrase>
+<phrase>
+ <source>Submit</source>
+ <target>Küldés</target>
+</phrase>
+<phrase>
+ <source>Underline</source>
+ <target>Aláhúzott</target>
+</phrase>
+<phrase>
+ <source>Outline</source>
+ <target>Áthúzott</target>
+</phrase>
+<phrase>
+ <source>Direction</source>
+ <target>Irány</target>
+</phrase>
+<phrase>
+ <source>Configuration</source>
+ <target>Konfiguráció</target>
+</phrase>
+<phrase>
+ <source>None</source>
+ <target>Semmi</target>
+</phrase>
+<phrase>
+ <source>Save As...</source>
+ <target>Mentés másként...</target>
+</phrase>
+<phrase>
+ <source>Context</source>
+ <target>Környezet</target>
+</phrase>
+<phrase>
+ <source>Edit</source>
+ <target>Szerkesztés</target>
+</phrase>
+<phrase>
+ <source>Toolbar</source>
+ <target>Eszköztár</target>
+</phrase>
+<phrase>
+ <source>&amp;Print...</source>
+ <target>&amp;Nyomtatás...</target>
+</phrase>
+<phrase>
+ <source>E&amp;xit</source>
+ <target>&amp;Kilépés</target>
+</phrase>
+<phrase>
+ <source>Sidebar</source>
+ <target>Oldalsáv</target>
+</phrase>
+<phrase>
+ <source>Bookmark</source>
+ <target>Könyvjelző</target>
+</phrase>
+<phrase>
+ <source>Save As</source>
+ <target>Mentés másként</target>
+</phrase>
+<phrase>
+ <source>Select All</source>
+ <target>Összes kijelölése</target>
+</phrase>
+<phrase>
+ <source>Name</source>
+ <target>Név</target>
+</phrase>
+<phrase>
+ <source>Type</source>
+ <target>Típus</target>
+</phrase>
+<phrase>
+ <source>Date</source>
+ <target>Dátum</target>
+</phrase>
+<phrase>
+ <source>Name</source>
+ <target>Név</target>
+</phrase>
+<phrase>
+ <source>Unknown</source>
+ <target>Ismeretlen</target>
+</phrase>
+<phrase>
+ <source>Value</source>
+ <target>Érték</target>
+</phrase>
+<phrase>
+ <source>Default</source>
+ <target>Alapértelmezett</target>
+</phrase>
+<phrase>
+ <source>New</source>
+ <target>Új</target>
+</phrase>
+<phrase>
+ <source>Level</source>
+ <target>Szint</target>
+</phrase>
+<phrase>
+ <source>Location</source>
+ <target>Hely</target>
+</phrase>
+<phrase>
+ <source>Reload</source>
+ <target>Újratöltés</target>
+</phrase>
+<phrase>
+ <source>Pictures</source>
+ <target>Képek</target>
+</phrase>
+<phrase>
+ <source>Battery</source>
+ <target>Elem</target>
+</phrase>
+<phrase>
+ <source>No error</source>
+ <target>Nincs hiba</target>
+</phrase>
+<phrase>
+ <source>Whole words</source>
+ <target>Teljes szó</target>
+</phrase>
+<phrase>
+ <source>(Új bejegyzés)</source>
+ <target></target>
+</phrase>
+<phrase>
+ <source>Horizontal</source>
+ <target>Vízszintes</target>
+</phrase>
+<phrase>
+ <source>Enabled</source>
+ <target>Engedélyezve</target>
+</phrase>
+<phrase>
+ <source>Up</source>
+ <target>Fel</target>
+</phrase>
+<phrase>
+ <source>Down</source>
+ <target>Le</target>
+</phrase>
+</QPH>
diff --git a/src/linguist/phrasebooks/italian.qph b/src/linguist/phrasebooks/italian.qph
new file mode 100644
index 000000000..0a4ea1dcc
--- /dev/null
+++ b/src/linguist/phrasebooks/italian.qph
@@ -0,0 +1,1105 @@
+<!DOCTYPE QPH><QPH language="it">
+<phrase>
+ <source>About</source>
+ <target>Informazioni su</target>
+</phrase>
+<phrase>
+ <source>access key</source>
+ <target>tasto di scelta</target>
+</phrase>
+<phrase>
+ <source>accessibility</source>
+ <target>accesso facilitato</target>
+</phrase>
+<phrase>
+ <source>action handle</source>
+ <target>identificatore dell&apos;azione</target>
+</phrase>
+<phrase>
+ <source>active</source>
+ <target>attivo</target>
+</phrase>
+<phrase>
+ <source>active end</source>
+ <target>punto finale</target>
+</phrase>
+<phrase>
+ <source>active object</source>
+ <target>oggetto attivo</target>
+</phrase>
+<phrase>
+ <source>active window</source>
+ <target>finestra attiva</target>
+</phrase>
+<phrase>
+ <source>adornment</source>
+ <target>barra</target>
+</phrase>
+<phrase>
+ <source>Always on Top</source>
+ <target>Sempre in primo piano</target>
+</phrase>
+<phrase>
+ <source>anchor point</source>
+ <target>punto di ancoraggio</target>
+</phrase>
+<phrase>
+ <source>Apply</source>
+ <target>Applica</target>
+</phrase>
+<phrase>
+ <source>auto-exit</source>
+ <target>ad uscita automatica</target>
+</phrase>
+<phrase>
+ <source>auto-repeat</source>
+ <target>a ripetizione automatica</target>
+</phrase>
+<phrase>
+ <source>automatic link</source>
+ <target>collegamento automatico</target>
+</phrase>
+<phrase>
+ <source>automatic scrolling</source>
+ <target>scorrimento automatico</target>
+</phrase>
+<phrase>
+ <source>autoscroll</source>
+ <target>scorrimento automatico</target>
+</phrase>
+<phrase>
+ <source>Back</source>
+ <target>Indietro</target>
+</phrase>
+<phrase>
+ <source>barrel button</source>
+ <target>pulsante della penna</target>
+ <definition>pen</definition>
+</phrase>
+<phrase>
+ <source>barrel-tap</source>
+ <target>tocco con il pulsante premuto</target>
+</phrase>
+<phrase>
+ <source>boxed edit</source>
+ <target>casella di testo a griglia</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>Browse</source>
+ <target>Sfoglia</target>
+</phrase>
+<phrase>
+ <source>Cancel</source>
+ <target>Annulla</target>
+</phrase>
+<phrase>
+ <source>cascading menu</source>
+ <target>menu sovrapposto</target>
+</phrase>
+<phrase>
+ <source>check box</source>
+ <target>casella di controllo</target>
+</phrase>
+<phrase>
+ <source>check mark</source>
+ <target>segno di spunta</target>
+</phrase>
+<phrase>
+ <source>child window</source>
+ <target>finestra secondaria</target>
+</phrase>
+<phrase>
+ <source>choose</source>
+ <target>scegliere</target>
+</phrase>
+<phrase>
+ <source>click</source>
+ <target>fare clic</target>
+</phrase>
+<phrase>
+ <source>Clipboard</source>
+ <target>Appunti</target>
+</phrase>
+<phrase>
+ <source>Close</source>
+ <target>Chiudi</target>
+</phrase>
+<phrase>
+ <source>Close button</source>
+ <target>pulsante di chiusura</target>
+</phrase>
+<phrase>
+ <source>collapse</source>
+ <target>comprimere</target>
+ <definition>outline, verb</definition>
+</phrase>
+<phrase>
+ <source>column heading</source>
+ <target>intestazione di colonna</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>combo box</source>
+ <target>casella combinata</target>
+</phrase>
+<phrase>
+ <source>command button</source>
+ <target>pulsante di comando</target>
+</phrase>
+<phrase>
+ <source>container</source>
+ <target>contenitore</target>
+</phrase>
+<phrase>
+ <source>context-sensitive Help</source>
+ <target>guida sensibile al contesto</target>
+</phrase>
+<phrase>
+ <source>contextual</source>
+ <target>contestuale</target>
+</phrase>
+<phrase>
+ <source>control</source>
+ <target>controllo</target>
+</phrase>
+<phrase>
+ <source>Copy</source>
+ <target>Copia</target>
+</phrase>
+<phrase>
+ <source>Copy here</source>
+ <target>Copia</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut</source>
+ <target>Crea collegamento</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut Here</source>
+ <target>Crea collegamento</target>
+</phrase>
+<phrase>
+ <source>Cut</source>
+ <target>Taglia</target>
+</phrase>
+<phrase>
+ <source>default</source>
+ <target>predefinito</target>
+</phrase>
+<phrase>
+ <source>default button</source>
+ <target>pulsante predefinito</target>
+</phrase>
+<phrase>
+ <source>Delete</source>
+ <target>Elimina</target>
+</phrase>
+<phrase>
+ <source>desktop</source>
+ <target>desktop</target>
+</phrase>
+<phrase>
+ <source>destination</source>
+ <target>destinazione</target>
+</phrase>
+<phrase>
+ <source>dialog box</source>
+ <target>finestra di dialogo</target>
+</phrase>
+<phrase>
+ <source>disability</source>
+ <target>disabilità</target>
+</phrase>
+<phrase>
+ <source>disjoint selection</source>
+ <target>selezione multipla</target>
+</phrase>
+<phrase>
+ <source>dock</source>
+ <target>posizionare</target>
+</phrase>
+<phrase>
+ <source>document</source>
+ <target>documento</target>
+</phrase>
+<phrase>
+ <source>double-click</source>
+ <target>doppio clic</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>double-click</source>
+ <target>fare doppio clic</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>double-tap</source>
+ <target>toccare due volte in rapida successione</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>double-tap</source>
+ <target>doppio tocco</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>drag</source>
+ <target>trascinare</target>
+</phrase>
+<phrase>
+ <source>drag-and-drop</source>
+ <target>trascinare e rilasciare</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>drag-and-drop</source>
+ <target>caratteristica Trascina selezione</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>drop-down combo box</source>
+ <target>casella combinata a discesa</target>
+</phrase>
+<phrase>
+ <source>drop-down list box</source>
+ <target>casella di riepilogo a discesa</target>
+</phrase>
+<phrase>
+ <source>drop-down menu</source>
+ <target>menu a discesa</target>
+</phrase>
+<phrase>
+ <source>Edit</source>
+ <target>Modifica </target>
+</phrase>
+<phrase>
+ <source>Edit menu</source>
+ <target>menu Modifica</target>
+</phrase>
+<phrase>
+ <source>ellipsis</source>
+ <target>puntini di sospensione</target>
+</phrase>
+<phrase>
+ <source>embedded object</source>
+ <target>oggetto incorporato</target>
+</phrase>
+<phrase>
+ <source>Exit</source>
+ <target>Esci</target>
+</phrase>
+<phrase>
+ <source>expand</source>
+ <target>espandere</target>
+ <definition>an outline/una struttura</definition>
+</phrase>
+<phrase>
+ <source>Explore</source>
+ <target>Gestione risorse</target>
+</phrase>
+<phrase>
+ <source>extended selection</source>
+ <target>selezione estesa</target>
+</phrase>
+<phrase>
+ <source>extended selection list box</source>
+ <target>casella di riepilogo a selezione estesa</target>
+</phrase>
+<phrase>
+ <source>file</source>
+ <target>file</target>
+</phrase>
+<phrase>
+ <source>File menu</source>
+ <target>menu File</target>
+</phrase>
+<phrase>
+ <source>Find</source>
+ <target>Trova</target>
+</phrase>
+<phrase>
+ <source>Find Next</source>
+ <target>Trova successivo</target>
+</phrase>
+<phrase>
+ <source>Find What</source>
+ <target>Trova</target>
+</phrase>
+<phrase>
+ <source>folder</source>
+ <target>cartella</target>
+</phrase>
+<phrase>
+ <source>font</source>
+ <target>tipo di carattere</target>
+</phrase>
+<phrase>
+ <source>font size</source>
+ <target>dimensione carattere</target>
+</phrase>
+<phrase>
+ <source>font style</source>
+ <target>stile carattere</target>
+</phrase>
+<phrase>
+ <source>function key</source>
+ <target>tasto funzione</target>
+</phrase>
+<phrase>
+ <source>gesture</source>
+ <target>segno</target>
+</phrase>
+<phrase>
+ <source>glyph</source>
+ <target>icona</target>
+</phrase>
+<phrase>
+ <source>group box</source>
+ <target>casella di gruppo</target>
+</phrase>
+<phrase>
+ <source>handle</source>
+ <target>quadratino di ridimensionamento </target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>Help</source>
+ <target>Guida</target>
+</phrase>
+<phrase>
+ <source>Help menu</source>
+ <target>menu ?</target>
+</phrase>
+<phrase>
+ <source>Hide</source>
+ <target>Nascondi</target>
+</phrase>
+<phrase>
+ <source>hierarchical selection</source>
+ <target>selezione gerarchica</target>
+</phrase>
+<phrase>
+ <source>hold</source>
+ <target>tenere premuto</target>
+</phrase>
+<phrase>
+ <source>hot spot</source>
+ <target>area sensibile del puntatore</target>
+</phrase>
+<phrase>
+ <source>hot zone</source>
+ <target>area sensibile dell&apos;oggetto</target>
+</phrase>
+<phrase>
+ <source>icon</source>
+ <target>icona</target>
+</phrase>
+<phrase>
+ <source>inactive</source>
+ <target>inattivo</target>
+</phrase>
+<phrase>
+ <source>inactive window</source>
+ <target>finestra inattiva</target>
+</phrase>
+<phrase>
+ <source>ink</source>
+ <target>tratto</target>
+</phrase>
+<phrase>
+ <source>ink edit</source>
+ <target>modifica tratto</target>
+</phrase>
+<phrase>
+ <source>input focus</source>
+ <target>elemento attivo</target>
+</phrase>
+<phrase>
+ <source>Insert</source>
+ <target>menu Inserisci</target>
+</phrase>
+<phrase>
+ <source>Insert Object</source>
+ <target>Inserisci oggetto</target>
+</phrase>
+<phrase>
+ <source>insertion point</source>
+ <target>punto di inserimento</target>
+</phrase>
+<phrase>
+ <source>italic</source>
+ <target>corsivo</target>
+</phrase>
+<phrase>
+ <source>label</source>
+ <target>etichetta</target>
+</phrase>
+<phrase>
+ <source>landscape</source>
+ <target>orizzontale</target>
+</phrase>
+<phrase>
+ <source>lasso-tap</source>
+ <target>selezione circolare</target>
+</phrase>
+<phrase>
+ <source>lens</source>
+ <target>lente</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>collegamento</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>collegare</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>Link Here</source>
+ <target>Collega</target>
+</phrase>
+<phrase>
+ <source>list box</source>
+ <target>casella di riepilogo</target>
+</phrase>
+<phrase>
+ <source>list view</source>
+ <target>visualizzazione elementi</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>manual link</source>
+ <target>collegamento manuale</target>
+</phrase>
+<phrase>
+ <source>Maximize</source>
+ <target>Ingrandisci</target>
+</phrase>
+<phrase>
+ <source>maximize button</source>
+ <target>pulsante di ingrandimento</target>
+</phrase>
+<phrase>
+ <source>MDI</source>
+ <target>MDI</target>
+</phrase>
+<phrase>
+ <source>menu</source>
+ <target>menu</target>
+</phrase>
+<phrase>
+ <source>menu bar</source>
+ <target>barra dei menu</target>
+</phrase>
+<phrase>
+ <source>menu button</source>
+ <target>pulsante menu</target>
+</phrase>
+<phrase>
+ <source>menu item</source>
+ <target>voce di menu</target>
+</phrase>
+<phrase>
+ <source>menu title</source>
+ <target>titolo di menu</target>
+</phrase>
+<phrase>
+ <source>message box</source>
+ <target>finestra di messaggio</target>
+</phrase>
+<phrase>
+ <source>Minimize</source>
+ <target>Riduci a icona</target>
+</phrase>
+<phrase>
+ <source>minimize button</source>
+ <target>pulsante di riduzione ad icona</target>
+</phrase>
+<phrase>
+ <source>mixed-value</source>
+ <target>valori misti</target>
+</phrase>
+<phrase>
+ <source>modal</source>
+ <target>a scelta obbligatoria</target>
+</phrase>
+<phrase>
+ <source>mode</source>
+ <target>modalità</target>
+</phrase>
+<phrase>
+ <source>modeless</source>
+ <target>non a scelta obbligatoria</target>
+</phrase>
+<phrase>
+ <source>modifier key</source>
+ <target>tasto di modifica delle funzioni di tastiera</target>
+</phrase>
+<phrase>
+ <source>modifier key</source>
+ <target>tasto di modifica dell&apos;output di tastiera</target>
+</phrase>
+<phrase>
+ <source>mouse</source>
+ <target>mouse</target>
+</phrase>
+<phrase>
+ <source>Move</source>
+ <target>Sposta</target>
+</phrase>
+<phrase>
+ <source>Move Here</source>
+ <target>Sposta</target>
+</phrase>
+<phrase>
+ <source>Multiple Document Interface</source>
+ <target>interfaccia a documenti multipli</target>
+</phrase>
+<phrase>
+ <source>multiple selection list box</source>
+ <target>casella di riepilogo a selezione multipla</target>
+</phrase>
+<phrase>
+ <source>My Computer</source>
+ <target>Risorse del computer</target>
+ <definition>icon/icono</definition>
+</phrase>
+<phrase>
+ <source>Network Neighborhood</source>
+ <target>Risorse di rete</target>
+ <definition>icon</definition>
+</phrase>
+<phrase>
+ <source>New</source>
+ <target>Nuovo</target>
+</phrase>
+<phrase>
+ <source>Next</source>
+ <target>Avanti</target>
+</phrase>
+<phrase>
+ <source>object</source>
+ <target>oggetto</target>
+</phrase>
+<phrase>
+ <source>OK</source>
+ <target>OK</target>
+</phrase>
+<phrase>
+ <source>OLE</source>
+ <target>OLE</target>
+</phrase>
+<phrase>
+ <source>OLE drag and drop</source>
+ <target>caratteristica Trascina selezione</target>
+</phrase>
+<phrase>
+ <source>OLE embedded object</source>
+ <target>oggetto incorporato OLE</target>
+</phrase>
+<phrase>
+ <source>OLE linked object</source>
+ <target>oggetto collegato OLE</target>
+</phrase>
+<phrase>
+ <source>OLE nondefault drag and drop</source>
+ <target>caratteristica Trascina selezione OLE non predefinita</target>
+</phrase>
+<phrase>
+ <source>Open</source>
+ <target>Apri</target>
+</phrase>
+<phrase>
+ <source>Open With</source>
+ <target>Apri con</target>
+</phrase>
+<phrase>
+ <source>option button</source>
+ <target>pulsante di opzione</target>
+</phrase>
+<phrase>
+ <source>option-set</source>
+ <target>opzione impostata</target>
+</phrase>
+<phrase>
+ <source>option-set</source>
+ <target>opzione attivata</target>
+</phrase>
+<phrase>
+ <source>package</source>
+ <target>package</target>
+</phrase>
+<phrase>
+ <source>Page Setup</source>
+ <target>Imposta pagina</target>
+</phrase>
+<phrase>
+ <source>palette window</source>
+ <target>casella degli tavolozza di colori</target>
+</phrase>
+<phrase>
+ <source>palette window</source>
+ <target>casella degli strumenti</target>
+</phrase>
+<phrase>
+ <source>pane</source>
+ <target>riquadro</target>
+</phrase>
+<phrase>
+ <source>parent window</source>
+ <target>finestra principale</target>
+</phrase>
+<phrase>
+ <source>password</source>
+ <target>password</target>
+</phrase>
+<phrase>
+ <source>Paste</source>
+ <target>Incolla </target>
+</phrase>
+<phrase>
+ <source>Paste Link</source>
+ <target>Incolla collegamento</target>
+</phrase>
+<phrase>
+ <source>Paste Shortcut</source>
+ <target>Incolla collegamento</target>
+</phrase>
+<phrase>
+ <source>Paste Special</source>
+ <target>Incolla speciale</target>
+</phrase>
+<phrase>
+ <source>path</source>
+ <target>percorso</target>
+</phrase>
+<phrase>
+ <source>Pause</source>
+ <target>Interrompi</target>
+</phrase>
+<phrase>
+ <source>pen</source>
+ <target>penna</target>
+</phrase>
+<phrase>
+ <source>Play</source>
+ <target>Riproduci</target>
+</phrase>
+<phrase>
+ <source>Plug and Play</source>
+ <target>Plug and Play</target>
+</phrase>
+<phrase>
+ <source>point</source>
+ <target>punto</target>
+</phrase>
+<phrase>
+ <source>pointer</source>
+ <target>puntatore</target>
+</phrase>
+<phrase>
+ <source>pop-up menu</source>
+ <target>menu di scelta rapida</target>
+</phrase>
+<phrase>
+ <source>pop-up window</source>
+ <target>finestra popup</target>
+</phrase>
+<phrase>
+ <source>portrait</source>
+ <target>verticale</target>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>premere</target>
+ <definition>a key/un tasto</definition>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>premere</target>
+ <definition>and hold a mouse button/e tenere premuto un pulsante del mouse</definition>
+</phrase>
+<phrase>
+ <source>primary container</source>
+ <target>contenitore principale</target>
+</phrase>
+<phrase>
+ <source>primary window</source>
+ <target>finestra principale</target>
+</phrase>
+<phrase>
+ <source>Print</source>
+ <target>Stampa</target>
+</phrase>
+<phrase>
+ <source>printer</source>
+ <target>stampante</target>
+</phrase>
+<phrase>
+ <source>progress indicator</source>
+ <target>indicatore di avanzamento del processo</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>project</source>
+ <target>progetto</target>
+</phrase>
+<phrase>
+ <source>Properties</source>
+ <target>Proprietà</target>
+</phrase>
+<phrase>
+ <source>property inspector</source>
+ <target>visualizzatore proprietà</target>
+</phrase>
+<phrase>
+ <source>property page</source>
+ <target>scheda proprietà</target>
+</phrase>
+<phrase>
+ <source>property sheet</source>
+ <target>finestra proprietà</target>
+</phrase>
+<phrase>
+ <source>property sheet control</source>
+ <target>controllo finestra proprietà</target>
+</phrase>
+<phrase>
+ <source>Quick View</source>
+ <target>Anteprima</target>
+</phrase>
+<phrase>
+ <source>read-only</source>
+ <target>sola lettura</target>
+</phrase>
+<phrase>
+ <source>recognition</source>
+ <target>riconoscimento</target>
+</phrase>
+<phrase>
+ <source>Recycle Bin</source>
+ <target>Cestino</target>
+ <definition>Icon</definition>
+</phrase>
+<phrase>
+ <source>Redo</source>
+ <target>Ripeti</target>
+</phrase>
+<phrase>
+ <source>region selection</source>
+ <target>selezione dell&apos;area</target>
+</phrase>
+<phrase>
+ <source>registry</source>
+ <target>registro di configurazione</target>
+</phrase>
+<phrase>
+ <source>Repeat</source>
+ <target>Ripeti</target>
+</phrase>
+<phrase>
+ <source>Replace</source>
+ <target>Sostituisci</target>
+</phrase>
+<phrase>
+ <source>Restore</source>
+ <target>Ripristina</target>
+</phrase>
+<phrase>
+ <source>Restore button</source>
+ <target>pulsante di ripristino</target>
+</phrase>
+<phrase>
+ <source>Resume</source>
+ <target>Riprendi</target>
+</phrase>
+<phrase>
+ <source>Retry</source>
+ <target>Riprova</target>
+</phrase>
+<phrase>
+ <source>rich-text box</source>
+ <target>casello di testo RTF</target>
+</phrase>
+<phrase>
+ <source>Run</source>
+ <target>Esegui</target>
+</phrase>
+<phrase>
+ <source>Save</source>
+ <target>Salva</target>
+</phrase>
+<phrase>
+ <source>Save as</source>
+ <target>Salva con nome</target>
+</phrase>
+<phrase>
+ <source>scroll</source>
+ <target>scorrere</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>scroll arrow</source>
+ <target>freccia di scorrimento</target>
+</phrase>
+<phrase>
+ <source>scroll bar</source>
+ <target>barra di scorrimento</target>
+</phrase>
+<phrase>
+ <source>scroll box</source>
+ <target>casella di scorrimento</target>
+</phrase>
+<phrase>
+ <source>secondary window</source>
+ <target>finestra secondaria</target>
+</phrase>
+<phrase>
+ <source>select</source>
+ <target>selezionare</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>Select All</source>
+ <target>Seleziona tutto</target>
+</phrase>
+<phrase>
+ <source>selection</source>
+ <target>selezione</target>
+</phrase>
+<phrase>
+ <source>selection handle</source>
+ <target>quadratino di selezione</target>
+</phrase>
+<phrase>
+ <source>Send To</source>
+ <target>Invia a</target>
+</phrase>
+<phrase>
+ <source>separator</source>
+ <target>separatore</target>
+</phrase>
+<phrase>
+ <source>Settings</source>
+ <target>Impostazioni</target>
+</phrase>
+<phrase>
+ <source>Setup</source>
+ <target>Imposta</target>
+</phrase>
+<phrase>
+ <source>shortcut</source>
+ <target>collegamento</target>
+</phrase>
+<phrase>
+ <source>shortcut button</source>
+ <target>pulsante di collegamento</target>
+</phrase>
+<phrase>
+ <source>shortcut icon</source>
+ <target>icona di collegamento</target>
+</phrase>
+<phrase>
+ <source>shortcut key</source>
+ <target>tasto di scelta rapida</target>
+</phrase>
+<phrase>
+ <source>shortcut key control</source>
+ <target>controllo tasto di scelta rapida</target>
+</phrase>
+<phrase>
+ <source>Show</source>
+ <target>Mostra</target>
+</phrase>
+<phrase>
+ <source>Shutdown</source>
+ <target>Chiudi sessione</target>
+ <definition>comando del menu Avvio</definition>
+</phrase>
+<phrase>
+ <source>Shutdown</source>
+ <target>Arresta il sistema</target>
+ <definition>pulsanti e opzioni</definition>
+</phrase>
+<phrase>
+ <source>single selection list box</source>
+ <target>casella di riepilogo a selezione singola</target>
+</phrase>
+<phrase>
+ <source>Size</source>
+ <target>Dimensione</target>
+</phrase>
+<phrase>
+ <source>size grip</source>
+ <target>punto di ridimensionamento</target>
+</phrase>
+<phrase>
+ <source>slider</source>
+ <target>dispositivo di scorrimento</target>
+</phrase>
+<phrase>
+ <source>spin box</source>
+ <target>casella di selezione</target>
+</phrase>
+<phrase>
+ <source>Split</source>
+ <target>Dividi</target>
+</phrase>
+<phrase>
+ <source>split bar</source>
+ <target>barra di divisione</target>
+</phrase>
+<phrase>
+ <source>split box</source>
+ <target>casella di divisione</target>
+</phrase>
+<phrase>
+ <source>Start button</source>
+ <target>pulsante Avvio</target>
+</phrase>
+<phrase>
+ <source>StartUp folder</source>
+ <target>cartella Esecuzione automatica</target>
+</phrase>
+<phrase>
+ <source>status bar</source>
+ <target>barra di stato</target>
+</phrase>
+<phrase>
+ <source>Stop</source>
+ <target>Ferma</target>
+</phrase>
+<phrase>
+ <source>tab control</source>
+ <target>controllo a schede</target>
+</phrase>
+<phrase>
+ <source>tap</source>
+ <target>toccare</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>tap</source>
+ <target>tocco</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>task bar</source>
+ <target>barra delle applicazioni</target>
+</phrase>
+<phrase>
+ <source>task-oriented Help</source>
+ <target>guida orientata alle attività</target>
+</phrase>
+<phrase>
+ <source>template</source>
+ <target>modello</target>
+</phrase>
+<phrase>
+ <source>text box</source>
+ <target>casella di testo</target>
+</phrase>
+<phrase>
+ <source>title bar</source>
+ <target>barra del titolo</target>
+</phrase>
+<phrase>
+ <source>title text</source>
+ <target>testo della barra del titolo</target>
+</phrase>
+<phrase>
+ <source>toggle key</source>
+ <target>tasto interruttore</target>
+</phrase>
+<phrase>
+ <source>toolbar</source>
+ <target>barra degli strumenti</target>
+</phrase>
+<phrase>
+ <source>tooltip</source>
+ <target>descrizione comando</target>
+</phrase>
+<phrase>
+ <source>tree view control</source>
+ <target>controllo per la visualizzazione ad albero</target>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>inserire</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>digitare</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>tipo</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>unavailable</source>
+ <target>non disponibile</target>
+</phrase>
+<phrase>
+ <source>Undo</source>
+ <target>Annulla</target>
+</phrase>
+<phrase>
+ <source>Uninstall</source>
+ <target>Rimozione</target>
+</phrase>
+<phrase>
+ <source>View</source>
+ <target>menu Visualizza</target>
+</phrase>
+<phrase>
+ <source>visual editing</source>
+ <target>modifica diretta</target>
+</phrase>
+<phrase>
+ <source>well control</source>
+ <target>controllo di selezione grafica</target>
+</phrase>
+<phrase>
+ <source>What&apos;s This?</source>
+ <target>Guida rapida</target>
+</phrase>
+<phrase>
+ <source>Window</source>
+ <target>menu Finestra</target>
+</phrase>
+<phrase>
+ <source>window</source>
+ <target>finestra</target>
+</phrase>
+<phrase>
+ <source>Windows Explorer</source>
+ <target>Gestione risorse</target>
+</phrase>
+<phrase>
+ <source>wizard</source>
+ <target>installazione guidata</target>
+</phrase>
+<phrase>
+ <source>workbook</source>
+ <target>cartella di lavoro</target>
+</phrase>
+<phrase>
+ <source>workgroup</source>
+ <target>gruppo di lavoro</target>
+</phrase>
+<phrase>
+ <source>workspace</source>
+ <target>area di lavoro</target>
+</phrase>
+<phrase>
+ <source>Yes</source>
+ <target>Sì</target>
+</phrase>
+</QPH>
diff --git a/src/linguist/phrasebooks/japanese.qph b/src/linguist/phrasebooks/japanese.qph
new file mode 100644
index 000000000..3ecd9e332
--- /dev/null
+++ b/src/linguist/phrasebooks/japanese.qph
@@ -0,0 +1,1021 @@
+<!DOCTYPE QPH><QPH language="ja">
+<phrase>
+ <source>About</source>
+ <target>について</target>
+</phrase>
+<phrase>
+ <source>access key</source>
+ <target>アクセスキー</target>
+</phrase>
+<phrase>
+ <source>accessibility</source>
+ <target>ユーザー補助</target>
+</phrase>
+<phrase>
+ <source>action handle</source>
+ <target>アクションハンドル</target>
+</phrase>
+<phrase>
+ <source>active</source>
+ <target>アクティブ</target>
+</phrase>
+<phrase>
+ <source>active end</source>
+ <target>アクティブエンド</target>
+</phrase>
+<phrase>
+ <source>active object</source>
+ <target>アクティブオブジェクト</target>
+</phrase>
+<phrase>
+ <source>active window</source>
+ <target>アクティブウィンドウ</target>
+</phrase>
+<phrase>
+ <source>adornment</source>
+ <target>装飾</target>
+</phrase>
+<phrase>
+ <source>Always on Top</source>
+ <target>常に手前に表示</target>
+</phrase>
+<phrase>
+ <source>anchor point</source>
+ <target>アンカーポイント</target>
+</phrase>
+<phrase>
+ <source>Apply</source>
+ <target>適用</target>
+</phrase>
+<phrase>
+ <source>auto-exit</source>
+ <target>自動終了</target>
+</phrase>
+<phrase>
+ <source>automatic link</source>
+ <target>オートリンク</target>
+</phrase>
+<phrase>
+ <source>automatic scrolling</source>
+ <target>オートスクロール</target>
+</phrase>
+<phrase>
+ <source>auto-repeat</source>
+ <target>自動繰り返し</target>
+</phrase>
+<phrase>
+ <source>autoscroll</source>
+ <target>自動スクロール</target>
+</phrase>
+<phrase>
+ <source>Back</source>
+ <target>戻る</target>
+</phrase>
+<phrase>
+ <source>barrel button</source>
+ <target>丸ボタン</target>
+</phrase>
+<phrase>
+ <source>Browse</source>
+ <target>参照</target>
+</phrase>
+<phrase>
+ <source>Cancel</source>
+ <target>キャンセル</target>
+</phrase>
+<phrase>
+ <source>cascading menu</source>
+ <target>カスケードメニュー</target>
+</phrase>
+<phrase>
+ <source>check box</source>
+ <target>チェックボックス</target>
+</phrase>
+<phrase>
+ <source>check mark</source>
+ <target>チェックマーク</target>
+</phrase>
+<phrase>
+ <source>child window</source>
+ <target>子ウィンドウ</target>
+</phrase>
+<phrase>
+ <source>choose</source>
+ <target>選択</target>
+</phrase>
+<phrase>
+ <source>click</source>
+ <target>クリック</target>
+</phrase>
+<phrase>
+ <source>Clipboard</source>
+ <target>クリップボード</target>
+</phrase>
+<phrase>
+ <source>Close</source>
+ <target>閉じる</target>
+</phrase>
+<phrase>
+ <source>Close button</source>
+ <target>閉じるボタン</target>
+</phrase>
+<phrase>
+ <source>collapse</source>
+ <target>非表示</target>
+ <definition>セルなど</definition>
+</phrase>
+<phrase>
+ <source>collapse</source>
+ <target>折りたたみ</target>
+ <definition>XML</definition>
+</phrase>
+<phrase>
+ <source>column heading</source>
+ <target>列見出し</target>
+</phrase>
+<phrase>
+ <source>combo box</source>
+ <target>コンボボックス</target>
+</phrase>
+<phrase>
+ <source>command button</source>
+ <target>コマンドボタン</target>
+</phrase>
+<phrase>
+ <source>container</source>
+ <target>コンテナ</target>
+</phrase>
+<phrase>
+ <source>context-sensitive Help</source>
+ <target>文脈依存のヘルプ</target>
+</phrase>
+<phrase>
+ <source>contextual</source>
+ <target>文脈の</target>
+</phrase>
+<phrase>
+ <source>control</source>
+ <target>コントロール</target>
+</phrase>
+<phrase>
+ <source>Copy</source>
+ <target>コピー</target>
+</phrase>
+<phrase>
+ <source>Copy here</source>
+ <target>ここにコピー</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut</source>
+ <target>ショートカットを作成</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut Here</source>
+ <target>ショートカットをここに作成</target>
+</phrase>
+<phrase>
+ <source>Cut</source>
+ <target>切り取り</target>
+</phrase>
+<phrase>
+ <source>default</source>
+ <target>既定</target>
+</phrase>
+<phrase>
+ <source>default button</source>
+ <target>既定のボタン</target>
+</phrase>
+<phrase>
+ <source>Delete</source>
+ <target>削除</target>
+</phrase>
+<phrase>
+ <source>desktop</source>
+ <target>デスクトップ</target>
+</phrase>
+<phrase>
+ <source>destination</source>
+ <target>コピー先</target>
+ <definition>ファイルなど</definition>
+</phrase>
+<phrase>
+ <source>destination</source>
+ <target>送信先</target>
+ <definition>FAX、ネットワークなど</definition>
+</phrase>
+<phrase>
+ <source>destination</source>
+ <target>変換先</target>
+ <definition>文字コード、ファイル形式など</definition>
+</phrase>
+<phrase>
+ <source>destination</source>
+ <target>出力先</target>
+ <definition>ログ、ファイルなど</definition>
+</phrase>
+<phrase>
+ <source>dialog box</source>
+ <target>ダイアログボックス</target>
+</phrase>
+<phrase>
+ <source>disability</source>
+ <target>障害</target>
+</phrase>
+<phrase>
+ <source>disjoint selection</source>
+ <target>離れた複数項目を選択</target>
+</phrase>
+<phrase>
+ <source>dock</source>
+ <target>ドック</target>
+</phrase>
+<phrase>
+ <source>document</source>
+ <target>ドキュメント</target>
+</phrase>
+<phrase>
+ <source>double-click</source>
+ <target>ダブルクリック</target>
+</phrase>
+<phrase>
+ <source>double-tap</source>
+ <target>ダブルタップ</target>
+</phrase>
+<phrase>
+ <source>drag</source>
+ <target>ドラッグ</target>
+</phrase>
+<phrase>
+ <source>drag-and-drop</source>
+ <target>ドラッグアンドドロップ</target>
+</phrase>
+<phrase>
+ <source>drop-down combo box</source>
+ <target>ドロップダウンコンボボックス</target>
+</phrase>
+<phrase>
+ <source>drop-down list box</source>
+ <target>ドロップダウンリストボックス</target>
+</phrase>
+<phrase>
+ <source>drop-down menu</source>
+ <target>ドロップダウンメニュー</target>
+</phrase>
+<phrase>
+ <source>Edit</source>
+ <target>編集</target>
+</phrase>
+<phrase>
+ <source>Edit menu</source>
+ <target>編集メニュー</target>
+</phrase>
+<phrase>
+ <source>ellipsis</source>
+ <target>省略</target>
+</phrase>
+<phrase>
+ <source>embedded object</source>
+ <target>埋め込みオブジェクト</target>
+</phrase>
+<phrase>
+ <source>Exit</source>
+ <target>終了</target>
+</phrase>
+<phrase>
+ <source>expand</source>
+ <target>展開する</target>
+</phrase>
+<phrase>
+ <source>Explore</source>
+ <target>エクスプローラ</target>
+</phrase>
+<phrase>
+ <source>extended selection</source>
+ <target>拡張選択</target>
+ <definition>F8</definition>
+</phrase>
+<phrase>
+ <source>extended selection list box</source>
+ <target>拡張選択リストボックス</target>
+</phrase>
+<phrase>
+ <source>file</source>
+ <target>ファイル</target>
+</phrase>
+<phrase>
+ <source>File</source>
+ <target>ファイル</target>
+</phrase>
+<phrase>
+ <source>Find</source>
+ <target>検索</target>
+</phrase>
+<phrase>
+ <source>Find Next</source>
+ <target>次を検索</target>
+</phrase>
+<phrase>
+ <source>Find What</source>
+ <target>検索する文字列</target>
+</phrase>
+<phrase>
+ <source>folder</source>
+ <target>フォルダ</target>
+</phrase>
+<phrase>
+ <source>font</source>
+ <target>フォント</target>
+</phrase>
+<phrase>
+ <source>font size</source>
+ <target>フォントサイズ</target>
+</phrase>
+<phrase>
+ <source>font style</source>
+ <target>フォントスタイル</target>
+</phrase>
+<phrase>
+ <source>function key</source>
+ <target>ファンクションキー</target>
+</phrase>
+<phrase>
+ <source>gesture</source>
+ <target>ジェスチャー</target>
+</phrase>
+<phrase>
+ <source>glyph</source>
+ <target>標識</target>
+</phrase>
+<phrase>
+ <source>group box</source>
+ <target>グループボックス</target>
+</phrase>
+<phrase>
+ <source>handle</source>
+ <target>ハンドル</target>
+</phrase>
+<phrase>
+ <source>Help</source>
+ <target>ヘルプ</target>
+</phrase>
+<phrase>
+ <source>Hide</source>
+ <target>非表示</target>
+</phrase>
+<phrase>
+ <source>hold</source>
+ <target>保留</target>
+</phrase>
+<phrase>
+ <source>hot spot</source>
+ <target>ホットスポット</target>
+</phrase>
+<phrase>
+ <source>hot zone</source>
+ <target>ホットゾーン</target>
+</phrase>
+<phrase>
+ <source>icon</source>
+ <target>アイコン</target>
+</phrase>
+<phrase>
+ <source>inactive</source>
+ <target>非アクティブ</target>
+</phrase>
+<phrase>
+ <source>inactive window</source>
+ <target>非アクティブウィンドウ</target>
+</phrase>
+<phrase>
+ <source>ink</source>
+ <target>インク</target>
+</phrase>
+<phrase>
+ <source>ink edit</source>
+ <target>インクエディット</target>
+</phrase>
+<phrase>
+ <source>input focus</source>
+ <target>入力フォーカス</target>
+</phrase>
+<phrase>
+ <source>Insert</source>
+ <target>挿入</target>
+</phrase>
+<phrase>
+ <source>Insert Object</source>
+ <target>オブジェクトの挿入</target>
+</phrase>
+<phrase>
+ <source>insertion point</source>
+ <target>挿入位置</target>
+</phrase>
+<phrase>
+ <source>italic</source>
+ <target>斜体</target>
+</phrase>
+<phrase>
+ <source>label</source>
+ <target>ラベル</target>
+</phrase>
+<phrase>
+ <source>landscape</source>
+ <target>横</target>
+</phrase>
+<phrase>
+ <source>lens</source>
+ <target>レンズ</target>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>リンク</target>
+</phrase>
+<phrase>
+ <source>Link Here</source>
+ <target>ここにリンク</target>
+</phrase>
+<phrase>
+ <source>list box</source>
+ <target>リストボックス</target>
+</phrase>
+<phrase>
+ <source>list view</source>
+ <target>一覧の表示</target>
+</phrase>
+<phrase>
+ <source>manual link</source>
+ <target>手動リンク</target>
+</phrase>
+<phrase>
+ <source>Maximize</source>
+ <target>最大化</target>
+</phrase>
+<phrase>
+ <source>maximize button</source>
+ <target>最大化ボタン</target>
+</phrase>
+<phrase>
+ <source>MDI</source>
+ <target>MDI</target>
+</phrase>
+<phrase>
+ <source>menu</source>
+ <target>メニュー</target>
+</phrase>
+<phrase>
+ <source>menu bar</source>
+ <target>メニューバー</target>
+</phrase>
+<phrase>
+ <source>menu button</source>
+ <target>メニューボタン</target>
+</phrase>
+<phrase>
+ <source>menu item</source>
+ <target>メニュー項目</target>
+</phrase>
+<phrase>
+ <source>menu title</source>
+ <target>メニュータイトル</target>
+</phrase>
+<phrase>
+ <source>message box</source>
+ <target>メッセージボックス</target>
+</phrase>
+<phrase>
+ <source>Minimize</source>
+ <target>最小化</target>
+</phrase>
+<phrase>
+ <source>minimize button</source>
+ <target>最小化ボタン</target>
+</phrase>
+<phrase>
+ <source>modal</source>
+ <target>モーダル</target>
+</phrase>
+<phrase>
+ <source>mode</source>
+ <target>モード</target>
+</phrase>
+<phrase>
+ <source>modeless</source>
+ <target>モードレス</target>
+</phrase>
+<phrase>
+ <source>modifier key</source>
+ <target>修飾キー</target>
+</phrase>
+<phrase>
+ <source>mouse</source>
+ <target>マウス</target>
+</phrase>
+<phrase>
+ <source>Move</source>
+ <target>移動</target>
+</phrase>
+<phrase>
+ <source>Move Here</source>
+ <target>ここに移動</target>
+</phrase>
+<phrase>
+ <source>Multiple Document Interface</source>
+ <target>マルチドキュメントインターフェイス</target>
+</phrase>
+<phrase>
+ <source>multiple selection list box</source>
+ <target>複数選択リストボックス</target>
+</phrase>
+<phrase>
+ <source>My Computer</source>
+ <target>マイコンピュータ</target>
+</phrase>
+<phrase>
+ <source>Network Neighborhood</source>
+ <target>ネットワークコンピュータ</target>
+</phrase>
+<phrase>
+ <source>New</source>
+ <target>新規</target>
+</phrase>
+<phrase>
+ <source>Next</source>
+ <target>次へ</target>
+</phrase>
+<phrase>
+ <source>object</source>
+ <target>オブジェクト</target>
+</phrase>
+<phrase>
+ <source>OK</source>
+ <target>OK</target>
+</phrase>
+<phrase>
+ <source>OLE</source>
+ <target>OLE</target>
+</phrase>
+<phrase>
+ <source>OLE drag and drop</source>
+ <target>OLEドラッグアンドドロップ</target>
+</phrase>
+<phrase>
+ <source>OLE embedded object</source>
+ <target>OLE 埋め込みオブジェクト</target>
+</phrase>
+<phrase>
+ <source>OLE linked object</source>
+ <target>OLE リンクオブジェクト</target>
+</phrase>
+<phrase>
+ <source>OLE nondefault drag and drop</source>
+ <target>OLE ノンデフォルトドラッグアンドドロップ</target>
+</phrase>
+<phrase>
+ <source>Open</source>
+ <target>開く</target>
+</phrase>
+<phrase>
+ <source>Open With</source>
+ <target>アプリケーションから開く</target>
+</phrase>
+<phrase>
+ <source>option button</source>
+ <target>オプションボタン</target>
+</phrase>
+<phrase>
+ <source>option-set</source>
+ <target>オプションセット</target>
+</phrase>
+<phrase>
+ <source>package</source>
+ <target>パッケージ</target>
+</phrase>
+<phrase>
+ <source>Page Setup</source>
+ <target>ページ設定</target>
+</phrase>
+<phrase>
+ <source>palette window</source>
+ <target>パレットウィンドウ</target>
+</phrase>
+<phrase>
+ <source>pane</source>
+ <target>ペイン</target>
+</phrase>
+<phrase>
+ <source>parent window</source>
+ <target>親ウィンドウ</target>
+</phrase>
+<phrase>
+ <source>password</source>
+ <target>パスワード</target>
+</phrase>
+<phrase>
+ <source>Paste</source>
+ <target>貼り付け</target>
+</phrase>
+<phrase>
+ <source>Paste Link</source>
+ <target>リンク貼り付け</target>
+</phrase>
+<phrase>
+ <source>Paste Shortcut</source>
+ <target>ショートカットの貼り付け</target>
+</phrase>
+<phrase>
+ <source>Paste Special</source>
+ <target>形式を選択して貼り付け</target>
+</phrase>
+<phrase>
+ <source>path</source>
+ <target>パス</target>
+</phrase>
+<phrase>
+ <source>Pause</source>
+ <target>一時停止</target>
+</phrase>
+<phrase>
+ <source>pen</source>
+ <target>ペン</target>
+</phrase>
+<phrase>
+ <source>Play</source>
+ <target>再生</target>
+</phrase>
+<phrase>
+ <source>Plug and Play</source>
+ <target>プラグアンドプレイ</target>
+</phrase>
+<phrase>
+ <source>point</source>
+ <target>ポイント</target>
+</phrase>
+<phrase>
+ <source>pointer</source>
+ <target>ポインタ</target>
+</phrase>
+<phrase>
+ <source>pop-up menu</source>
+ <target>ポップアップメニュー</target>
+</phrase>
+<phrase>
+ <source>pop-up window</source>
+ <target>ポップアップウィンドウ</target>
+</phrase>
+<phrase>
+ <source>portrait</source>
+ <target>縦</target>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>押す</target>
+</phrase>
+<phrase>
+ <source>primary container</source>
+ <target>プライマリコンテナ</target>
+</phrase>
+<phrase>
+ <source>primary window</source>
+ <target>プライマリウィンドウ</target>
+</phrase>
+<phrase>
+ <source>Print</source>
+ <target>印刷</target>
+</phrase>
+<phrase>
+ <source>printer</source>
+ <target>プリンタ</target>
+</phrase>
+<phrase>
+ <source>progress indicator</source>
+ <target>進行状況インジケータ</target>
+</phrase>
+<phrase>
+ <source>project</source>
+ <target>プロジェクト</target>
+</phrase>
+<phrase>
+ <source>Properties</source>
+ <target>プロパティ</target>
+</phrase>
+<phrase>
+ <source>property inspector</source>
+ <target>プロパティインスペクター</target>
+</phrase>
+<phrase>
+ <source>property page</source>
+ <target>プロパティページ</target>
+</phrase>
+<phrase>
+ <source>property sheet</source>
+ <target>プロパティシート</target>
+</phrase>
+<phrase>
+ <source>property sheet control</source>
+ <target>プロパティシートコントロール</target>
+</phrase>
+<phrase>
+ <source>Quick View</source>
+ <target>クイックビューア</target>
+</phrase>
+<phrase>
+ <source>read-only</source>
+ <target>読み取り専用</target>
+</phrase>
+<phrase>
+ <source>recognition</source>
+ <target>認識</target>
+</phrase>
+<phrase>
+ <source>Recycle Bin</source>
+ <target>ごみ箱</target>
+</phrase>
+<phrase>
+ <source>Redo</source>
+ <target>やり直す</target>
+</phrase>
+<phrase>
+ <source>region selection</source>
+ <target>範囲選択</target>
+</phrase>
+<phrase>
+ <source>registry</source>
+ <target>レジストリ</target>
+</phrase>
+<phrase>
+ <source>Repeat</source>
+ <target>繰り返す</target>
+</phrase>
+<phrase>
+ <source>Replace</source>
+ <target>置換</target>
+</phrase>
+<phrase>
+ <source>Restore</source>
+ <target>元に戻す</target>
+</phrase>
+<phrase>
+ <source>Restore button</source>
+ <target>リストアボタン</target>
+</phrase>
+<phrase>
+ <source>Resume</source>
+ <target>再開</target>
+</phrase>
+<phrase>
+ <source>Retry</source>
+ <target>再試行</target>
+</phrase>
+<phrase>
+ <source>rich-text box</source>
+ <target>リッチテキストボックス</target>
+</phrase>
+<phrase>
+ <source>Run</source>
+ <target>ファイル名を指定して実行</target>
+</phrase>
+<phrase>
+ <source>Save</source>
+ <target>保存</target>
+</phrase>
+<phrase>
+ <source>Save as</source>
+ <target>名前を付けて保存</target>
+</phrase>
+<phrase>
+ <source>scroll</source>
+ <target>スクロール</target>
+</phrase>
+<phrase>
+ <source>scroll arrow</source>
+ <target>スクロール矢印</target>
+</phrase>
+<phrase>
+ <source>scroll bar</source>
+ <target>スクロールバー</target>
+</phrase>
+<phrase>
+ <source>scroll box</source>
+ <target>スクロールボックス</target>
+</phrase>
+<phrase>
+ <source>secondary window</source>
+ <target>セカンダリウィンドウ</target>
+</phrase>
+<phrase>
+ <source>select</source>
+ <target>選択</target>
+</phrase>
+<phrase>
+ <source>Select All</source>
+ <target>すべてを選択</target>
+</phrase>
+<phrase>
+ <source>selection</source>
+ <target>選択した部分</target>
+</phrase>
+<phrase>
+ <source>selection handle</source>
+ <target>セレクションハンドル</target>
+</phrase>
+<phrase>
+ <source>Send To</source>
+ <target>送る</target>
+</phrase>
+<phrase>
+ <source>separator</source>
+ <target>セパレータ</target>
+</phrase>
+<phrase>
+ <source>Settings</source>
+ <target>設定</target>
+</phrase>
+<phrase>
+ <source>Setup</source>
+ <target>設定</target>
+</phrase>
+<phrase>
+ <source>shortcut</source>
+ <target>ショートカット</target>
+</phrase>
+<phrase>
+ <source>shortcut button</source>
+ <target>ショートカットボタン</target>
+</phrase>
+<phrase>
+ <source>shortcut icon</source>
+ <target>ショートカットアイコン</target>
+</phrase>
+<phrase>
+ <source>shortcut key</source>
+ <target>ショートカットキー</target>
+</phrase>
+<phrase>
+ <source>shortcut key control</source>
+ <target>ショートカットキーコントロール</target>
+</phrase>
+<phrase>
+ <source>Show</source>
+ <target>表示</target>
+</phrase>
+<phrase>
+ <source>Shutdown</source>
+ <target>シャットダウン</target>
+</phrase>
+<phrase>
+ <source>single selection list box</source>
+ <target>単一選択リストボックス</target>
+</phrase>
+<phrase>
+ <source>Size</source>
+ <target>サイズ</target>
+</phrase>
+<phrase>
+ <source>size grip</source>
+ <target>サイズグリップ</target>
+</phrase>
+<phrase>
+ <source>slider</source>
+ <target>スライダー</target>
+</phrase>
+<phrase>
+ <source>spin box</source>
+ <target>スピンボックス</target>
+</phrase>
+<phrase>
+ <source>Split</source>
+ <target>分割</target>
+</phrase>
+<phrase>
+ <source>split bar</source>
+ <target>分割バー</target>
+</phrase>
+<phrase>
+ <source>split box</source>
+ <target>分割ボックス</target>
+</phrase>
+<phrase>
+ <source>Start button</source>
+ <target>スタートボタン</target>
+</phrase>
+<phrase>
+ <source>StartUp folder</source>
+ <target>スタートアップフォルダ</target>
+</phrase>
+<phrase>
+ <source>status bar</source>
+ <target>ステータスバー</target>
+</phrase>
+<phrase>
+ <source>Stop</source>
+ <target>停止</target>
+</phrase>
+<phrase>
+ <source>tab control</source>
+ <target>タブコントロール</target>
+</phrase>
+<phrase>
+ <source>tap</source>
+ <target>タップ</target>
+</phrase>
+<phrase>
+ <source>task bar</source>
+ <target>タスクバー</target>
+</phrase>
+<phrase>
+ <source>task-oriented Help</source>
+ <target>タスク志向のヘルプ</target>
+</phrase>
+<phrase>
+ <source>template</source>
+ <target>テンプレート</target>
+</phrase>
+<phrase>
+ <source>text box</source>
+ <target>テキストボックス</target>
+</phrase>
+<phrase>
+ <source>title bar</source>
+ <target>タイトルバー</target>
+</phrase>
+<phrase>
+ <source>title text</source>
+ <target>タイトルテキスト</target>
+</phrase>
+<phrase>
+ <source>toggle key</source>
+ <target>トグルキー</target>
+</phrase>
+<phrase>
+ <source>toolbar</source>
+ <target>ツールバー</target>
+</phrase>
+<phrase>
+ <source>tooltip</source>
+ <target>ツールチップ</target>
+</phrase>
+<phrase>
+ <source>tree view control</source>
+ <target>ツリービューコントロール</target>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>タイプ</target>
+</phrase>
+<phrase>
+ <source>unavailable</source>
+ <target>利用不可</target>
+</phrase>
+<phrase>
+ <source>Undo</source>
+ <target>元に戻す</target>
+</phrase>
+<phrase>
+ <source>Uninstall</source>
+ <target>アンインストール</target>
+</phrase>
+<phrase>
+ <source>View</source>
+ <target>表示</target>
+</phrase>
+<phrase>
+ <source>visual editing</source>
+ <target>画面編集</target>
+</phrase>
+<phrase>
+ <source>What&apos;s This?</source>
+ <target>ヘルプ</target>
+</phrase>
+<phrase>
+ <source>window</source>
+ <target>ウィンドウ</target>
+</phrase>
+<phrase>
+ <source>Window</source>
+ <target>ウィンドウ</target>
+</phrase>
+<phrase>
+ <source>Windows Explorer</source>
+ <target>ウィンドウズエクスプローラ</target>
+</phrase>
+<phrase>
+ <source>wizard</source>
+ <target>ウィザード</target>
+</phrase>
+<phrase>
+ <source>workbook</source>
+ <target>ワークブック</target>
+</phrase>
+<phrase>
+ <source>workgroup</source>
+ <target>ワークグループ</target>
+</phrase>
+<phrase>
+ <source>workspace</source>
+ <target>ワークスペース</target>
+</phrase>
+<phrase>
+ <source>Yes</source>
+ <target>はい</target>
+</phrase>
+</QPH>
diff --git a/src/linguist/phrasebooks/norwegian.qph b/src/linguist/phrasebooks/norwegian.qph
new file mode 100644
index 000000000..9fda2ad68
--- /dev/null
+++ b/src/linguist/phrasebooks/norwegian.qph
@@ -0,0 +1,1004 @@
+<!DOCTYPE QPH><QPH language="no">
+<phrase>
+ <source>About</source>
+ <target>Om</target>
+</phrase>
+<phrase>
+ <source>access key</source>
+ <target>tilgangstast</target>
+</phrase>
+<phrase>
+ <source>accessibility</source>
+ <target>tilgjengelighet</target>
+</phrase>
+<phrase>
+ <source>action handle</source>
+ <target>handlingshåndtak</target>
+</phrase>
+<phrase>
+ <source>active</source>
+ <target>aktiv</target>
+</phrase>
+<phrase>
+ <source>active end</source>
+ <target>markeringsavslutning</target>
+</phrase>
+<phrase>
+ <source>active object</source>
+ <target>aktivt objekt</target>
+</phrase>
+<phrase>
+ <source>active window</source>
+ <target>aktivt vindu</target>
+</phrase>
+<phrase>
+ <source>adornment</source>
+ <target>verktøyselement</target>
+</phrase>
+<phrase>
+ <source>Always on Top</source>
+ <target>Alltid øverst</target>
+</phrase>
+<phrase>
+ <source>anchor point</source>
+ <target>forankringspunkt</target>
+</phrase>
+<phrase>
+ <source>Apply</source>
+ <target>Bruk</target>
+</phrase>
+<phrase>
+ <source>auto-exit</source>
+ <target>automatisk avslutning</target>
+</phrase>
+<phrase>
+ <source>auto-repeat</source>
+ <target>automatisk gjentagelse</target>
+</phrase>
+<phrase>
+ <source>automatic link</source>
+ <target>automatisk kobling</target>
+</phrase>
+<phrase>
+ <source>automatic scrolling</source>
+ <target>automatisk rulling</target>
+</phrase>
+<phrase>
+ <source>autoscroll</source>
+ <target>autorulling</target>
+</phrase>
+<phrase>
+ <source>Back</source>
+ <target>Tilbake</target>
+</phrase>
+<phrase>
+ <source>Browse</source>
+ <target>Bla gjennom</target>
+</phrase>
+<phrase>
+ <source>Cancel</source>
+ <target>Avbryt</target>
+</phrase>
+<phrase>
+ <source>cascading menu</source>
+ <target>undermeny</target>
+</phrase>
+<phrase>
+ <source>check box</source>
+ <target>avmerkingsboks</target>
+</phrase>
+<phrase>
+ <source>check mark</source>
+ <target>merke</target>
+</phrase>
+<phrase>
+ <source>child window</source>
+ <target>undervindu</target>
+</phrase>
+<phrase>
+ <source>choose</source>
+ <target>velge</target>
+</phrase>
+<phrase>
+ <source>click</source>
+ <target>klikke</target>
+</phrase>
+<phrase>
+ <source>Clipboard</source>
+ <target>utklippstavle</target>
+</phrase>
+<phrase>
+ <source>Close</source>
+ <target>Lukk</target>
+</phrase>
+<phrase>
+ <source>Close button</source>
+ <target>lukkeknapp</target>
+</phrase>
+<phrase>
+ <source>collapse</source>
+ <target>skjule</target>
+ <definition>outline/disposisjon</definition>
+</phrase>
+<phrase>
+ <source>column heading</source>
+ <target>kolonneoverskrift</target>
+ <definition>control/kontroll</definition>
+</phrase>
+<phrase>
+ <source>combo box</source>
+ <target>kombinasjonsboks</target>
+</phrase>
+<phrase>
+ <source>command button</source>
+ <target>kommandoknapp</target>
+</phrase>
+<phrase>
+ <source>container</source>
+ <target>beholder</target>
+</phrase>
+<phrase>
+ <source>context-sensitive Help</source>
+ <target>kontekstavhengig hjelp</target>
+</phrase>
+<phrase>
+ <source>contextual</source>
+ <target>kontekstavhengig</target>
+</phrase>
+<phrase>
+ <source>control</source>
+ <target>kontroll</target>
+</phrase>
+<phrase>
+ <source>Copy</source>
+ <target>Kopier</target>
+</phrase>
+<phrase>
+ <source>Copy here</source>
+ <target>Kopier hit</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut</source>
+ <target>Lag snarvei</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut Here</source>
+ <target>Lag snarvei her</target>
+</phrase>
+<phrase>
+ <source>Cut</source>
+ <target>Klipp ut</target>
+</phrase>
+<phrase>
+ <source>default</source>
+ <target>standard</target>
+</phrase>
+<phrase>
+ <source>default button</source>
+ <target>standardknapp</target>
+</phrase>
+<phrase>
+ <source>Delete</source>
+ <target>Slett</target>
+</phrase>
+<phrase>
+ <source>desktop</source>
+ <target>skrivebord</target>
+</phrase>
+<phrase>
+ <source>destination</source>
+ <target>mål</target>
+</phrase>
+<phrase>
+ <source>dialog box</source>
+ <target>dialogboks</target>
+</phrase>
+<phrase>
+ <source>disability</source>
+ <target>funksjonshemning</target>
+</phrase>
+<phrase>
+ <source>disjoint selection</source>
+ <target>ikke sammenhengende utvalg</target>
+</phrase>
+<phrase>
+ <source>dock</source>
+ <target>forankre</target>
+</phrase>
+<phrase>
+ <source>document</source>
+ <target>dokument</target>
+</phrase>
+<phrase>
+ <source>double-click</source>
+ <target>dobbeltklikke</target>
+</phrase>
+<phrase>
+ <source>drag</source>
+ <target>dra</target>
+</phrase>
+<phrase>
+ <source>drag-and-drop</source>
+ <target>dra och slippe</target>
+</phrase>
+<phrase>
+ <source>drop-down combo box</source>
+ <target>kombinasjonsboks</target>
+</phrase>
+<phrase>
+ <source>drop-down list box</source>
+ <target>rullegardinliste</target>
+</phrase>
+<phrase>
+ <source>drop-down menu</source>
+ <target>rullegardinmeny</target>
+</phrase>
+<phrase>
+ <source>Edit</source>
+ <target>Rediger</target>
+</phrase>
+<phrase>
+ <source>Edit menu</source>
+ <target>Rediger-menyen</target>
+</phrase>
+<phrase>
+ <source>ellipsis</source>
+ <target>ellipse</target>
+</phrase>
+<phrase>
+ <source>embedded object</source>
+ <target>innebygd objekt</target>
+</phrase>
+<phrase>
+ <source>Exit</source>
+ <target>Avslutt</target>
+</phrase>
+<phrase>
+ <source>expand</source>
+ <target>utvide</target>
+ <definition>an outline/en disposisjon</definition>
+</phrase>
+<phrase>
+ <source>Explore</source>
+ <target>Utforsk</target>
+</phrase>
+<phrase>
+ <source>extended selection</source>
+ <target>utvidet merking</target>
+</phrase>
+<phrase>
+ <source>extended selection list box</source>
+ <target>liste med utvidet merking</target>
+</phrase>
+<phrase>
+ <source>file</source>
+ <target>fil</target>
+</phrase>
+<phrase>
+ <source>File menu</source>
+ <target>Fil-menyen</target>
+</phrase>
+<phrase>
+ <source>Find</source>
+ <target>Søk etter</target>
+</phrase>
+<phrase>
+ <source>Find Next</source>
+ <target>Søk etter neste</target>
+</phrase>
+<phrase>
+ <source>Find What</source>
+ <target>Søk etter</target>
+</phrase>
+<phrase>
+ <source>folder</source>
+ <target>mappe</target>
+</phrase>
+<phrase>
+ <source>font</source>
+ <target>skrift</target>
+</phrase>
+<phrase>
+ <source>font size</source>
+ <target>skriftstørrelse</target>
+</phrase>
+<phrase>
+ <source>font style</source>
+ <target>skriftstil</target>
+</phrase>
+<phrase>
+ <source>function key</source>
+ <target>funksjonstast</target>
+</phrase>
+<phrase>
+ <source>group box</source>
+ <target>gruppeboks</target>
+</phrase>
+<phrase>
+ <source>handle</source>
+ <target>håndtak</target>
+</phrase>
+<phrase>
+ <source>Help</source>
+ <target>Hjelp</target>
+</phrase>
+<phrase>
+ <source>Help menu</source>
+ <target>Hjelp-menyen</target>
+</phrase>
+<phrase>
+ <source>Hide</source>
+ <target>Skjul</target>
+</phrase>
+<phrase>
+ <source>hierarchical selection</source>
+ <target>hierarkisk merking</target>
+</phrase>
+<phrase>
+ <source>hold</source>
+ <target>holde</target>
+</phrase>
+<phrase>
+ <source>hot spot</source>
+ <target>fokus</target>
+</phrase>
+<phrase>
+ <source>hot zone</source>
+ <target>fokuseringssone</target>
+</phrase>
+<phrase>
+ <source>icon</source>
+ <target>ikon</target>
+</phrase>
+<phrase>
+ <source>inactive</source>
+ <target>inaktiv</target>
+</phrase>
+<phrase>
+ <source>inactive window</source>
+ <target>inaktivt vindu</target>
+</phrase>
+<phrase>
+ <source>input focus</source>
+ <target>indatafokus</target>
+</phrase>
+<phrase>
+ <source>Insert</source>
+ <target>Sett inn-menyen</target>
+</phrase>
+<phrase>
+ <source>Insert Object</source>
+ <target>Sett inn objekt</target>
+</phrase>
+<phrase>
+ <source>insertion point</source>
+ <target>innsettingspunkt</target>
+</phrase>
+<phrase>
+ <source>italic</source>
+ <target>kursiv</target>
+</phrase>
+<phrase>
+ <source>label</source>
+ <target>etikett</target>
+</phrase>
+<phrase>
+ <source>landscape</source>
+ <target>liggende</target>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>koble</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>kobling</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>Link Here</source>
+ <target>Lag kobling her</target>
+</phrase>
+<phrase>
+ <source>list box</source>
+ <target>listeboks</target>
+</phrase>
+<phrase>
+ <source>list view</source>
+ <target>listevisning</target>
+ <definition>control/kontroll</definition>
+</phrase>
+<phrase>
+ <source>manual link</source>
+ <target>manuell kobling</target>
+</phrase>
+<phrase>
+ <source>Maximize</source>
+ <target>Maksimer</target>
+</phrase>
+<phrase>
+ <source>maximize button</source>
+ <target>maksimeringsknapp</target>
+</phrase>
+<phrase>
+ <source>MDI</source>
+ <target>MDI</target>
+</phrase>
+<phrase>
+ <source>menu</source>
+ <target>meny</target>
+</phrase>
+<phrase>
+ <source>menu bar</source>
+ <target>menylinje</target>
+</phrase>
+<phrase>
+ <source>menu button</source>
+ <target>menyknapp</target>
+</phrase>
+<phrase>
+ <source>menu item</source>
+ <target>menyelement</target>
+</phrase>
+<phrase>
+ <source>menu title</source>
+ <target>menytittel</target>
+</phrase>
+<phrase>
+ <source>message box</source>
+ <target>meldingsboks</target>
+</phrase>
+<phrase>
+ <source>Minimize</source>
+ <target>Minimer</target>
+</phrase>
+<phrase>
+ <source>minimize button</source>
+ <target>minimeringsknapp</target>
+</phrase>
+<phrase>
+ <source>mixed-value</source>
+ <target>blandet verdi</target>
+</phrase>
+<phrase>
+ <source>modal</source>
+ <target>modal</target>
+</phrase>
+<phrase>
+ <source>mode</source>
+ <target>modus</target>
+</phrase>
+<phrase>
+ <source>modeless</source>
+ <target>icke-modal</target>
+</phrase>
+<phrase>
+ <source>modifier key</source>
+ <target>modifiseringstast</target>
+</phrase>
+<phrase>
+ <source>mouse</source>
+ <target>mus</target>
+</phrase>
+<phrase>
+ <source>Move</source>
+ <target>Flytt</target>
+</phrase>
+<phrase>
+ <source>Move Here</source>
+ <target>Flytt hit</target>
+</phrase>
+<phrase>
+ <source>Multiple Document Interface</source>
+ <target>flerdokumentgrensesnitt</target>
+</phrase>
+<phrase>
+ <source>multiple selection list box</source>
+ <target>flervalgsliste</target>
+</phrase>
+<phrase>
+ <source>My Computer</source>
+ <target>Min datamaskin</target>
+ <definition>icon/ikon</definition>
+</phrase>
+<phrase>
+ <source>Network Neighborhood</source>
+ <target>Andre maskiner</target>
+ <definition>icon/ikon</definition>
+</phrase>
+<phrase>
+ <source>New</source>
+ <target>Ny</target>
+</phrase>
+<phrase>
+ <source>Next</source>
+ <target>Neste</target>
+</phrase>
+<phrase>
+ <source>object</source>
+ <target>objekt</target>
+</phrase>
+<phrase>
+ <source>OK</source>
+ <target>OK</target>
+</phrase>
+<phrase>
+ <source>OLE</source>
+ <target>OLE</target>
+</phrase>
+<phrase>
+ <source>OLE drag and drop</source>
+ <target>OLE dra og slipp</target>
+</phrase>
+<phrase>
+ <source>OLE embedded object</source>
+ <target>innebyggd OLE-objekt</target>
+</phrase>
+<phrase>
+ <source>OLE linked object</source>
+ <target>koblet OLE-objekt</target>
+</phrase>
+<phrase>
+ <source>OLE nondefault drag and drop</source>
+ <target>utvidet OLE dra og slipp</target>
+</phrase>
+<phrase>
+ <source>Open</source>
+ <target>Åpne</target>
+</phrase>
+<phrase>
+ <source>Open With</source>
+ <target>Åpne i</target>
+</phrase>
+<phrase>
+ <source>option button</source>
+ <target>alternativknapp</target>
+</phrase>
+<phrase>
+ <source>option-set</source>
+ <target>valgt alternativ</target>
+</phrase>
+<phrase>
+ <source>package</source>
+ <target>pakke</target>
+</phrase>
+<phrase>
+ <source>Page Setup</source>
+ <target>Utskriftsformat</target>
+</phrase>
+<phrase>
+ <source>palette window</source>
+ <target>palettvindu</target>
+</phrase>
+<phrase>
+ <source>pane</source>
+ <target>rute</target>
+</phrase>
+<phrase>
+ <source>parent window</source>
+ <target>hovedvindu</target>
+</phrase>
+<phrase>
+ <source>password</source>
+ <target>passord</target>
+</phrase>
+<phrase>
+ <source>Paste</source>
+ <target>Lim inn</target>
+</phrase>
+<phrase>
+ <source>Paste Link</source>
+ <target>Lim inn kobling</target>
+</phrase>
+<phrase>
+ <source>Paste Shortcut</source>
+ <target>Lim inn snarvei</target>
+</phrase>
+<phrase>
+ <source>Paste Special</source>
+ <target>Lim inn utvalg</target>
+</phrase>
+<phrase>
+ <source>path</source>
+ <target>bane</target>
+</phrase>
+<phrase>
+ <source>Pause</source>
+ <target>Pause</target>
+</phrase>
+<phrase>
+ <source>Play</source>
+ <target>Spill</target>
+</phrase>
+<phrase>
+ <source>Plug and Play</source>
+ <target>Plug and Play</target>
+</phrase>
+<phrase>
+ <source>point</source>
+ <target>punkt</target>
+</phrase>
+<phrase>
+ <source>point</source>
+ <target>peke</target>
+</phrase>
+<phrase>
+ <source>pointer</source>
+ <target>peker</target>
+</phrase>
+<phrase>
+ <source>pop-up menu</source>
+ <target>hurtigmeny</target>
+</phrase>
+<phrase>
+ <source>pop-up window</source>
+ <target>forklaringsvindu</target>
+</phrase>
+<phrase>
+ <source>portrait</source>
+ <target>stående</target>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>klikke</target>
+ <definition>and hold a mouse button/og holde nede en musetast</definition>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>trykke</target>
+ <definition>a key/en tast</definition>
+</phrase>
+<phrase>
+ <source>primary container</source>
+ <target>primærbeholder</target>
+</phrase>
+<phrase>
+ <source>primary window</source>
+ <target>primærvindu</target>
+</phrase>
+<phrase>
+ <source>Print</source>
+ <target>Skriv ut</target>
+</phrase>
+<phrase>
+ <source>printer</source>
+ <target>skriver</target>
+</phrase>
+<phrase>
+ <source>progress indicator</source>
+ <target>fremdriftsindikator</target>
+ <definition>control/kontroll</definition>
+</phrase>
+<phrase>
+ <source>project</source>
+ <target>prosjekt</target>
+</phrase>
+<phrase>
+ <source>Properties</source>
+ <target>Egenskaper</target>
+</phrase>
+<phrase>
+ <source>property inspector</source>
+ <target>egenskapsvisning</target>
+</phrase>
+<phrase>
+ <source>property page</source>
+ <target>egenskapsside</target>
+</phrase>
+<phrase>
+ <source>property sheet</source>
+ <target>egenskapsark</target>
+</phrase>
+<phrase>
+ <source>property sheet control</source>
+ <target>egenskapsarkkontroll</target>
+</phrase>
+<phrase>
+ <source>Quick View</source>
+ <target>Hurtigvisning</target>
+</phrase>
+<phrase>
+ <source>read-only</source>
+ <target>skrivebeskyttet</target>
+</phrase>
+<phrase>
+ <source>Recycle Bin</source>
+ <target>Papirkurv</target>
+ <definition>Icon/ikon</definition>
+</phrase>
+<phrase>
+ <source>Redo</source>
+ <target>Gjør om</target>
+</phrase>
+<phrase>
+ <source>region selection</source>
+ <target>områdemerking</target>
+</phrase>
+<phrase>
+ <source>registry</source>
+ <target>register</target>
+</phrase>
+<phrase>
+ <source>Repeat</source>
+ <target>Gjenta</target>
+</phrase>
+<phrase>
+ <source>Replace</source>
+ <target>Erstatt</target>
+</phrase>
+<phrase>
+ <source>Restore</source>
+ <target>Gjenopprett</target>
+</phrase>
+<phrase>
+ <source>Restore button</source>
+ <target>gjenopprettingsknapp</target>
+</phrase>
+<phrase>
+ <source>Resume</source>
+ <target>Fortsett</target>
+</phrase>
+<phrase>
+ <source>Retry</source>
+ <target>Prøv på nytt</target>
+</phrase>
+<phrase>
+ <source>rich-text box</source>
+ <target>boks for rik tekst</target>
+</phrase>
+<phrase>
+ <source>Run</source>
+ <target>Kjør</target>
+</phrase>
+<phrase>
+ <source>Save</source>
+ <target>Lagre</target>
+</phrase>
+<phrase>
+ <source>Save as</source>
+ <target>Lagre som</target>
+</phrase>
+<phrase>
+ <source>scroll</source>
+ <target>rulle</target>
+</phrase>
+<phrase>
+ <source>scroll arrow</source>
+ <target>rullepil</target>
+</phrase>
+<phrase>
+ <source>scroll bar</source>
+ <target>rullefelt</target>
+</phrase>
+<phrase>
+ <source>scroll box</source>
+ <target>rulleboks</target>
+</phrase>
+<phrase>
+ <source>secondary window</source>
+ <target>sekundærvindu</target>
+</phrase>
+<phrase>
+ <source>select</source>
+ <target>merke</target>
+</phrase>
+<phrase>
+ <source>Select All</source>
+ <target>Merk alt</target>
+</phrase>
+<phrase>
+ <source>selection</source>
+ <target>merket område</target>
+</phrase>
+<phrase>
+ <source>selection handle</source>
+ <target>markeringshåndtak</target>
+</phrase>
+<phrase>
+ <source>Send To</source>
+ <target>Send til</target>
+</phrase>
+<phrase>
+ <source>separator</source>
+ <target>skilletegn</target>
+</phrase>
+<phrase>
+ <source>Settings</source>
+ <target>Innstillinger</target>
+</phrase>
+<phrase>
+ <source>Setup</source>
+ <target>Installasjon</target>
+</phrase>
+<phrase>
+ <source>shortcut</source>
+ <target>snarvei</target>
+</phrase>
+<phrase>
+ <source>shortcut button</source>
+ <target>snarveisknapp</target>
+</phrase>
+<phrase>
+ <source>shortcut icon</source>
+ <target>snarveisikon</target>
+</phrase>
+<phrase>
+ <source>shortcut key</source>
+ <target>hurtigtast</target>
+</phrase>
+<phrase>
+ <source>shortcut key control</source>
+ <target>hurtigtastkontroll</target>
+</phrase>
+<phrase>
+ <source>Show</source>
+ <target>Vis</target>
+</phrase>
+<phrase>
+ <source>Shutdown</source>
+ <target>Avslutt</target>
+</phrase>
+<phrase>
+ <source>single selection list box</source>
+ <target>enkeltvalgsliste</target>
+</phrase>
+<phrase>
+ <source>Size</source>
+ <target>Størrelse</target>
+</phrase>
+<phrase>
+ <source>size grip</source>
+ <target>skaleringshåndtak</target>
+</phrase>
+<phrase>
+ <source>slider</source>
+ <target>glidebryter</target>
+</phrase>
+<phrase>
+ <source>spin box</source>
+ <target>verdisettingsboks</target>
+</phrase>
+<phrase>
+ <source>Split</source>
+ <target>Del</target>
+</phrase>
+<phrase>
+ <source>split bar</source>
+ <target>delelinje</target>
+</phrase>
+<phrase>
+ <source>split box</source>
+ <target>deleboks</target>
+</phrase>
+<phrase>
+ <source>Start button</source>
+ <target>startknapp</target>
+</phrase>
+<phrase>
+ <source>StartUp folder</source>
+ <target>Oppstart-mappen</target>
+</phrase>
+<phrase>
+ <source>status bar</source>
+ <target>statuslinje</target>
+</phrase>
+<phrase>
+ <source>Stop</source>
+ <target>Stopp</target>
+</phrase>
+<phrase>
+ <source>tab control</source>
+ <target>kategorikontroll</target>
+</phrase>
+<phrase>
+ <source>task bar</source>
+ <target>oppgavelinje</target>
+</phrase>
+<phrase>
+ <source>task-oriented Help</source>
+ <target>oppgaveorientert hjelp</target>
+</phrase>
+<phrase>
+ <source>template</source>
+ <target>mal</target>
+</phrase>
+<phrase>
+ <source>text box</source>
+ <target>tekstboks</target>
+</phrase>
+<phrase>
+ <source>title bar</source>
+ <target>tittellinje</target>
+</phrase>
+<phrase>
+ <source>title text</source>
+ <target>titteltekst</target>
+</phrase>
+<phrase>
+ <source>toggle key</source>
+ <target>veksletast</target>
+</phrase>
+<phrase>
+ <source>toolbar</source>
+ <target>verktøylinje</target>
+</phrase>
+<phrase>
+ <source>tooltip</source>
+ <target>verktøytips</target>
+</phrase>
+<phrase>
+ <source>tree view control</source>
+ <target>trevisningskontroll</target>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>skrive</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>type</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>unavailable</source>
+ <target>ikke tilgjengelig</target>
+</phrase>
+<phrase>
+ <source>Undo</source>
+ <target>Angre</target>
+</phrase>
+<phrase>
+ <source>Uninstall</source>
+ <target>Avinstaller</target>
+</phrase>
+<phrase>
+ <source>View</source>
+ <target>Vis-meny</target>
+</phrase>
+<phrase>
+ <source>visual editing</source>
+ <target>visuell redigering</target>
+</phrase>
+<phrase>
+ <source>well control</source>
+ <target>grafikkontroll</target>
+</phrase>
+<phrase>
+ <source>What&apos;s This?</source>
+ <target>Hva er dette?</target>
+</phrase>
+<phrase>
+ <source>Window</source>
+ <target>Vindu-menyen</target>
+</phrase>
+<phrase>
+ <source>window</source>
+ <target>vindu</target>
+</phrase>
+<phrase>
+ <source>Windows Explorer</source>
+ <target>Windows Utforsker</target>
+</phrase>
+<phrase>
+ <source>wizard</source>
+ <target>veiviser</target>
+</phrase>
+<phrase>
+ <source>workbook</source>
+ <target>arbeidsbok</target>
+</phrase>
+<phrase>
+ <source>workgroup</source>
+ <target>arbeidsgruppe</target>
+</phrase>
+<phrase>
+ <source>workspace</source>
+ <target>arbeidsområde</target>
+</phrase>
+<phrase>
+ <source>Yes</source>
+ <target>Ja</target>
+</phrase>
+</QPH>
diff --git a/src/linguist/phrasebooks/polish.qph b/src/linguist/phrasebooks/polish.qph
new file mode 100644
index 000000000..9cedaecae
--- /dev/null
+++ b/src/linguist/phrasebooks/polish.qph
@@ -0,0 +1,527 @@
+<!DOCTYPE QPH><QPH language="pl">
+<phrase>
+ <source>About</source>
+ <target>Informacje o</target>
+</phrase>
+<phrase>
+ <source>About Qt</source>
+ <target>Informacje o Qt</target>
+</phrase>
+<phrase>
+ <source>Accelerator</source>
+ <target>Klawisz szybkiego dostępu</target>
+</phrase>
+<phrase>
+ <source>Alloc</source>
+ <target>Przydzielić</target>
+</phrase>
+<phrase>
+ <source>Appearance</source>
+ <target>Wygląd</target>
+</phrase>
+<phrase>
+ <source>at line</source>
+ <target>w linii</target>
+</phrase>
+<phrase>
+ <source>Background</source>
+ <target>Tło</target>
+</phrase>
+<phrase>
+ <source>Batch</source>
+ <target>Wsadowy....</target>
+</phrase>
+<phrase>
+ <source>Build </source>
+ <target>Wersja</target>
+</phrase>
+<phrase>
+ <source>Cannot create</source>
+ <target>Nie można utworzyć</target>
+</phrase>
+<phrase>
+ <source>Cannot find</source>
+ <target>Nie można znaleść</target>
+</phrase>
+<phrase>
+ <source>Cannot read</source>
+ <target>Nie można odczytać</target>
+</phrase>
+<phrase>
+ <source>Cannot save</source>
+ <target>Nie można zapisać</target>
+</phrase>
+<phrase>
+ <source>Case sensitive</source>
+ <target>Uwzględniaj wielkość liter</target>
+</phrase>
+<phrase>
+ <source>Child</source>
+ <target>Potomek Dziecko</target>
+</phrase>
+<phrase>
+ <source>Collapse</source>
+ <target>Zwinąć</target>
+</phrase>
+<phrase>
+ <source>ComboBox</source>
+ <target>Lista kombi</target>
+</phrase>
+<phrase>
+ <source>Conflicting</source>
+ <target>Kolidujący</target>
+</phrase>
+<phrase>
+ <source>Content</source>
+ <target>Treść</target>
+</phrase>
+<phrase>
+ <source>Contents</source>
+ <target>Treść Zawartość Spis treści</target>
+</phrase>
+<phrase>
+ <source>Copy</source>
+ <target>Kopiuj</target>
+</phrase>
+<phrase>
+ <source>Custom</source>
+ <target>Niestandardowy, użytkownika</target>
+</phrase>
+<phrase>
+ <source>Cut</source>
+ <target>Wytnij</target>
+</phrase>
+<phrase>
+ <source>Declare</source>
+ <target>Deklarować</target>
+</phrase>
+<phrase>
+ <source>Declared</source>
+ <target>Zadeklarować</target>
+</phrase>
+<phrase>
+ <source>Delete</source>
+ <target>Usuń</target>
+</phrase>
+<phrase>
+ <source>Disable</source>
+ <target>Wyłącz, zablokuj</target>
+</phrase>
+<phrase>
+ <source>Display</source>
+ <target>Pokaż, wyświetl</target>
+</phrase>
+<phrase>
+ <source>Dock</source>
+ <target>Osadzony Zakotwiczony</target>
+</phrase>
+<phrase>
+ <source>Dock window</source>
+ <target>Okno osadzone Okno zakotwiczone</target>
+</phrase>
+<phrase>
+ <source>Down</source>
+ <target>W dół</target>
+</phrase>
+<phrase>
+ <source>Enable</source>
+ <target>Włącz</target>
+</phrase>
+<phrase>
+ <source>Enabled</source>
+ <target>Włączony</target>
+</phrase>
+<phrase>
+ <source>encoding name</source>
+ <target>nazwa kodowania</target>
+</phrase>
+<phrase>
+ <source>Entity</source>
+ <target>Obiekt</target>
+</phrase>
+<phrase>
+ <source>Exit</source>
+ <target>Zakończ</target>
+</phrase>
+<phrase>
+ <source>Expand</source>
+ <target>Rozszerzyć</target>
+</phrase>
+<phrase>
+ <source>Fade</source>
+ <target>Wyłanianie</target>
+</phrase>
+<phrase>
+ <source>Failed to open</source>
+ <target>Nie można otworzyć</target>
+</phrase>
+<phrase>
+ <source>Feel</source>
+ <target>działanie</target>
+</phrase>
+<phrase>
+ <source>File</source>
+ <target>Plik</target>
+</phrase>
+<phrase>
+ <source>Find in text</source>
+ <target>Znajdź w tekście</target>
+</phrase>
+<phrase>
+ <source>Find next</source>
+ <target>Znajdź następne</target>
+</phrase>
+<phrase>
+ <source>Focal</source>
+ <target>Ogniskowa</target>
+</phrase>
+<phrase>
+ <source>Font family</source>
+ <target>Nazwa czcionki</target>
+</phrase>
+<phrase>
+ <source>Foreground</source>
+ <target>PIerwszy plan</target>
+</phrase>
+<phrase>
+ <source>Form</source>
+ <target>Okno formy</target>
+</phrase>
+<phrase>
+ <source>full text search</source>
+ <target>szukanie w treści tesktu</target>
+</phrase>
+<phrase>
+ <source>fulltext search</source>
+ <target>szukanie w treści tesktu</target>
+</phrase>
+<phrase>
+ <source>Go</source>
+ <target>Przejdź</target>
+ <definition>przy przeglądarkach</definition>
+</phrase>
+<phrase>
+ <source>Help</source>
+ <target>Pomoc</target>
+</phrase>
+<phrase>
+ <source>Home</source>
+ <target>Strona startowa</target>
+ <definition>przy przeglądarkach</definition>
+</phrase>
+<phrase>
+ <source>Illegal</source>
+ <target>Niepoprawny</target>
+</phrase>
+<phrase>
+ <source>Invalid</source>
+ <target>Niepoprawny</target>
+</phrase>
+<phrase>
+ <source>Invoke</source>
+ <target>Wywołaj</target>
+</phrase>
+<phrase>
+ <source>is missing</source>
+ <target>Brakuje ...</target>
+</phrase>
+<phrase>
+ <source>Item</source>
+ <target>Pozycja</target>
+</phrase>
+<phrase>
+ <source>Item</source>
+ <target>Wpis</target>
+</phrase>
+<phrase>
+ <source>Lay out</source>
+ <target>Ułóż</target>
+</phrase>
+<phrase>
+ <source>layout</source>
+ <target>ułożenie</target>
+</phrase>
+<phrase>
+ <source>Link</source>
+ <target>Odnośnik</target>
+ <definition>(w pliku HTML)</definition>
+</phrase>
+<phrase>
+ <source>Link</source>
+ <target>Dowiązanie</target>
+ <definition>(lokalizacja)</definition>
+</phrase>
+<phrase>
+ <source>literal</source>
+ <target>stała znakowa</target>
+</phrase>
+<phrase>
+ <source>Load</source>
+ <target>Wczytaj</target>
+</phrase>
+<phrase>
+ <source>Look and feel</source>
+ <target>Wygląd i działanie</target>
+</phrase>
+<phrase>
+ <source>Marker</source>
+ <target>Znacznik</target>
+</phrase>
+<phrase>
+ <source>Match</source>
+ <target>dopasowanie</target>
+</phrase>
+<phrase>
+ <source>Menu bar</source>
+ <target>Pasek menu</target>
+</phrase>
+<phrase>
+ <source>Minimize</source>
+ <target>Minimalizuj</target>
+</phrase>
+<phrase>
+ <source>Mismatch</source>
+ <target>Niezgodność, niedopasowanie</target>
+</phrase>
+<phrase>
+ <source>Namespace</source>
+ <target>Przesteń nazw</target>
+</phrase>
+<phrase>
+ <source>Occure</source>
+ <target>Wystąpić</target>
+</phrase>
+<phrase>
+ <source>Occured</source>
+ <target>Wystąpił, pojawił się</target>
+</phrase>
+<phrase>
+ <source>Off The Spot</source>
+ <target>Na pasku stanu</target>
+ <definition>Zmiany wykonywane sa w dodatkowym miejscu np na pasku stanu i dopiero pozniej wprowadzane do tesktu</definition>
+</phrase>
+<phrase>
+ <source>On The Spot</source>
+ <target>W oknie dokumentu</target>
+ <definition>Metoda wprowadzania znaków - bezposrednio w oknie</definition>
+</phrase>
+<phrase>
+ <source>Open Source Edition</source>
+ <target>Wydanie Open Source</target>
+</phrase>
+<phrase>
+ <source>Over The Spot</source>
+ <target>Nad oknem dokumentu</target>
+ <definition>Zmiany są wprowadzane z wyższej warstwie i renderowane później</definition>
+</phrase>
+<phrase>
+ <source>Pad</source>
+ <target>Dopełnić, Podstawka, Dopełnienie</target>
+</phrase>
+<phrase>
+ <source>Parse</source>
+ <target>Przetwarzać</target>
+</phrase>
+<phrase>
+ <source>Parsing</source>
+ <target>Przetwarzanie</target>
+</phrase>
+<phrase>
+ <source>Paste</source>
+ <target>Wklej</target>
+</phrase>
+<phrase>
+ <source>Pattern</source>
+ <target>Wzorzec</target>
+</phrase>
+<phrase>
+ <source>Phrase</source>
+ <target>Wyrażenie, fraza</target>
+</phrase>
+<phrase>
+ <source>Place</source>
+ <target>Wstaw</target>
+</phrase>
+<phrase>
+ <source>Preference</source>
+ <target>Wyróżnienie</target>
+</phrase>
+<phrase>
+ <source>Preferences</source>
+ <target>Wyróżnienia</target>
+</phrase>
+<phrase>
+ <source>Premature</source>
+ <target>Przedwczesny</target>
+</phrase>
+<phrase>
+ <source>Promote</source>
+ <target>Wypromuj</target>
+</phrase>
+<phrase>
+ <source>Properties</source>
+ <target>Właściwości</target>
+</phrase>
+<phrase>
+ <source>Property</source>
+ <target>Właściwość</target>
+</phrase>
+<phrase>
+ <source>Quotation mark</source>
+ <target>Znak cudzysłowu</target>
+</phrase>
+<phrase>
+ <source>Reference</source>
+ <target>Odwołanie</target>
+</phrase>
+<phrase>
+ <source>Refresh</source>
+ <target>Odśwież</target>
+</phrase>
+<phrase>
+ <source>Render</source>
+ <target>Renderuj</target>
+ <definition>odnosi się jedynie do grafiki</definition>
+</phrase>
+<phrase>
+ <source>Reset</source>
+ <target>Przywróc ustawienia</target>
+</phrase>
+<phrase>
+ <source>Root</source>
+ <target>W dodatkowym oknie</target>
+ <definition>Zmiany wprowadzane sa w zupelnie dodatkowym oknie i pozniej wprowadzane do calego tekstu</definition>
+</phrase>
+<phrase>
+ <source>Saturation</source>
+ <target>Nasycenie</target>
+</phrase>
+<phrase>
+ <source>search word</source>
+ <target>wyrażenie do szukania</target>
+</phrase>
+<phrase>
+ <source>Search wrapped</source>
+ <target>Szukanie od początku</target>
+</phrase>
+<phrase>
+ <source>searchword</source>
+ <target>wyrażenie do szukania</target>
+</phrase>
+<phrase>
+ <source>Select All</source>
+ <target>Zaznacz wszystko</target>
+</phrase>
+<phrase>
+ <source>Sequence</source>
+ <target>Ciąg</target>
+</phrase>
+<phrase>
+ <source>Sidebar</source>
+ <target>Panel</target>
+</phrase>
+<phrase>
+ <source>Sidebar</source>
+ <target>Panel boczny</target>
+</phrase>
+<phrase>
+ <source>Size</source>
+ <target>Rozmiar</target>
+</phrase>
+<phrase>
+ <source>Slider</source>
+ <target>Suwak</target>
+</phrase>
+<phrase>
+ <source>Specified</source>
+ <target>Określony</target>
+</phrase>
+<phrase>
+ <source>Status</source>
+ <target>Stan</target>
+</phrase>
+<phrase>
+ <source>Status Bar</source>
+ <target>Pasek stanu</target>
+</phrase>
+<phrase>
+ <source>Style sheet</source>
+ <target>Wzorzec stylu</target>
+</phrase>
+<phrase>
+ <source>StyleSheet</source>
+ <target>Wzorzec stylu</target>
+</phrase>
+<phrase>
+ <source>superfluous</source>
+ <target>przypadkowe, niecelowe, zbędny</target>
+</phrase>
+<phrase>
+ <source>Support</source>
+ <target>Obsługa</target>
+</phrase>
+<phrase>
+ <source>supported</source>
+ <target>obsługiwany</target>
+</phrase>
+<phrase>
+ <source>This program is licensed</source>
+ <target>Ten program wydany jest na licencji Qt Commercial. Aby sprawdzić szczegóły licencji, proszę sprawdzić plik LICENSE, który dołączany jest do pakietu Qt.</target>
+</phrase>
+<phrase>
+ <source>This version of</source>
+ <target>Ta wersja Qt Assistant jest częścią wydania Qt Open Source, przeznaczonego dla tworzenia i publikowania aplikacji Open Source. Qt jest zaawansowanym zestawem bibliotek wykorzystywanym do pisania aplikacji cross-platformowych.</target>
+</phrase>
+<phrase>
+ <source>to display</source>
+ <target>pokazywać, wyświetlać</target>
+</phrase>
+<phrase>
+ <source>Tool tips</source>
+ <target>Etykietki narzędzi</target>
+</phrase>
+<phrase>
+ <source>Toolbox</source>
+ <target>Narzędzie</target>
+</phrase>
+<phrase>
+ <source>ToolTips</source>
+ <target>Etykietki narzędzi</target>
+</phrase>
+<phrase>
+ <source>Tune</source>
+ <target>Ustawić</target>
+</phrase>
+<phrase>
+ <source>unsupported</source>
+ <target>nieobsługiwany</target>
+</phrase>
+<phrase>
+ <source>Up</source>
+ <target>W górę</target>
+</phrase>
+<phrase>
+ <source>Update</source>
+ <target>Uaktualnij</target>
+</phrase>
+<phrase>
+ <source>Value</source>
+ <target>Wartość</target>
+</phrase>
+<phrase>
+ <source>Widget</source>
+ <target>Element interfejsu</target>
+</phrase>
+<phrase>
+ <source>You need a commercial</source>
+ <target>Aby móc sprzedawać aplikację utworzone przy pomocy Qt potrzebujesz wersji komercyjnej. Proszę sprawdzić &lt;a href=&quot;http://qt.nokia.com/about&quot;&gt;qt.nokia.com/about&lt;/a&gt; dla poznania sposobu licencjonowania Qt.</target>
+</phrase>
+<phrase>
+ <source>Zoom in</source>
+ <target>Zwiększ</target>
+</phrase>
+<phrase>
+ <source>Zoom out</source>
+ <target>Zmniejsz</target>
+</phrase>
+</QPH>
diff --git a/src/linguist/phrasebooks/russian.qph b/src/linguist/phrasebooks/russian.qph
new file mode 100644
index 000000000..5876ee9ab
--- /dev/null
+++ b/src/linguist/phrasebooks/russian.qph
@@ -0,0 +1,1219 @@
+<!DOCTYPE QPH>
+<QPH language="ru">
+<phrase>
+ <source>About</source>
+ <target>О программе</target>
+</phrase>
+<phrase>
+ <source>access key</source>
+ <target>горячая клавиша</target>
+</phrase>
+<phrase>
+ <source>accessibility</source>
+ <target>специальные возможности</target>
+</phrase>
+<phrase>
+ <source>action handle</source>
+ <target>управление поведением</target>
+</phrase>
+<phrase>
+ <source>active</source>
+ <target>активный</target>
+</phrase>
+<phrase>
+ <source>active end</source>
+ <target>окончание выбора</target>
+</phrase>
+<phrase>
+ <source>active object</source>
+ <target>активный объект</target>
+</phrase>
+<phrase>
+ <source>active window</source>
+ <target>активное окно</target>
+</phrase>
+<phrase>
+ <source>adornment</source>
+ <target>украшение</target>
+</phrase>
+<phrase>
+ <source>Always on Top</source>
+ <target>Всегда наверху</target>
+</phrase>
+<phrase>
+ <source>anchor point</source>
+ <target>точка начала выбора</target>
+</phrase>
+<phrase>
+ <source>Apply</source>
+ <target>Применить</target>
+</phrase>
+<phrase>
+ <source>auto-exit</source>
+ <target>автоматический выход</target>
+</phrase>
+<phrase>
+ <source>auto-repeat</source>
+ <target>авто-повтор</target>
+</phrase>
+<phrase>
+ <source>automatic link</source>
+ <target>автоматическое связывание</target>
+</phrase>
+<phrase>
+ <source>automatic scrolling</source>
+ <target>автоматическая прокрутка</target>
+</phrase>
+<phrase>
+ <source>autoscroll</source>
+ <target>авто-прокрутка</target>
+</phrase>
+<phrase>
+ <source>boxed edit</source>
+ <target>окно редактирования</target>
+</phrase>
+<phrase>
+ <source>Browse</source>
+ <target>Обзор</target>
+</phrase>
+<phrase>
+ <source>Cancel</source>
+ <target>Отмена</target>
+</phrase>
+<phrase>
+ <source>cascading menu</source>
+ <target>ниспадающее меню</target>
+</phrase>
+<phrase>
+ <source>check box</source>
+ <target>флажок</target>
+</phrase>
+<phrase>
+ <source>check mark</source>
+ <target>флажок</target>
+</phrase>
+<phrase>
+ <source>child window</source>
+ <target>дочернее окно</target>
+</phrase>
+<phrase>
+ <source>choose</source>
+ <target>выбрать</target>
+</phrase>
+<phrase>
+ <source>click</source>
+ <target>щелчок</target>
+</phrase>
+<phrase>
+ <source>Clipboard</source>
+ <target>Буфер обмена</target>
+</phrase>
+<phrase>
+ <source>Close</source>
+ <target>Закрыть</target>
+</phrase>
+<phrase>
+ <source>Close button</source>
+ <target>Кнопка закрытия</target>
+</phrase>
+<phrase>
+ <source>collapse</source>
+ <target>свернуть</target>
+</phrase>
+<phrase>
+ <source>column heading</source>
+ <target>заголовок колонки</target>
+</phrase>
+<phrase>
+ <source>combo box</source>
+ <target>поле со списком</target>
+</phrase>
+<phrase>
+ <source>command button</source>
+ <target>кнопка</target>
+</phrase>
+<phrase>
+ <source>container</source>
+ <target>контейнер</target>
+</phrase>
+<phrase>
+ <source>context-sensitive Help</source>
+ <target>контекстная помощь</target>
+</phrase>
+<phrase>
+ <source>contextual</source>
+ <target>контекстный</target>
+</phrase>
+<phrase>
+ <source>control</source>
+ <target>элемент управления</target>
+</phrase>
+<phrase>
+ <source>Copy</source>
+ <target>Копировать</target>
+</phrase>
+<phrase>
+ <source>Copy here</source>
+ <target>Копировать сюда</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut</source>
+ <target>Создать ярлык</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut Here</source>
+ <target>Создать ярлык здесь</target>
+</phrase>
+<phrase>
+ <source>Cut</source>
+ <target>Вырезать</target>
+</phrase>
+<phrase>
+ <source>default</source>
+ <target>по умолчанию</target>
+</phrase>
+<phrase>
+ <source>default button</source>
+ <target>кнопка по умолчанию</target>
+</phrase>
+<phrase>
+ <source>Delete</source>
+ <target>Удалить</target>
+</phrase>
+<phrase>
+ <source>desktop</source>
+ <target>рабочий стол</target>
+</phrase>
+<phrase>
+ <source>destination</source>
+ <target>цель</target>
+</phrase>
+<phrase>
+ <source>dialog box</source>
+ <target>окно диалога</target>
+</phrase>
+<phrase>
+ <source>disability</source>
+ <target>неспособность</target>
+</phrase>
+<phrase>
+ <source>disjoint selection</source>
+ <target>разделить выбранные элементы</target>
+</phrase>
+<phrase>
+ <source>document</source>
+ <target>документ</target>
+</phrase>
+<phrase>
+ <source>double-click</source>
+ <target>двойной щелчок</target>
+</phrase>
+<phrase>
+ <source>drag</source>
+ <target>перетащить</target>
+</phrase>
+<phrase>
+ <source>drag-and-drop</source>
+ <target>drag-and-drop</target>
+</phrase>
+<phrase>
+ <source>drop-down combo box</source>
+ <target>раскрыть поле со списком</target>
+</phrase>
+<phrase>
+ <source>drop-down list box</source>
+ <target>прокрутить список</target>
+</phrase>
+<phrase>
+ <source>drop-down menu</source>
+ <target>открыть меню</target>
+</phrase>
+<phrase>
+ <source>Edit</source>
+ <target>Правка</target>
+</phrase>
+<phrase>
+ <source>Edit menu</source>
+ <target>меню Правка</target>
+</phrase>
+<phrase>
+ <source>ellipsis</source>
+ <target>эллипс</target>
+</phrase>
+<phrase>
+ <source>embedded object</source>
+ <target>встроенный объект</target>
+</phrase>
+<phrase>
+ <source>Exit</source>
+ <target>Выход</target>
+</phrase>
+<phrase>
+ <source>expand</source>
+ <target>развернуть</target>
+</phrase>
+<phrase>
+ <source>Explore</source>
+ <target>Исследовать</target>
+</phrase>
+<phrase>
+ <source>extended selection</source>
+ <target>расширенный выбор</target>
+</phrase>
+<phrase>
+ <source>extended selection list box</source>
+ <target>список с расширенным выбором</target>
+</phrase>
+<phrase>
+ <source>file</source>
+ <target>файл</target>
+</phrase>
+<phrase>
+ <source>File menu</source>
+ <target>меню Файл</target>
+</phrase>
+<phrase>
+ <source>Find</source>
+ <target>Поиск</target>
+</phrase>
+<phrase>
+ <source>Find Next</source>
+ <target>Найти далее</target>
+</phrase>
+<phrase>
+ <source>Find What</source>
+ <target>Искать</target>
+</phrase>
+<phrase>
+ <source>folder</source>
+ <target>папка</target>
+</phrase>
+<phrase>
+ <source>font</source>
+ <target>шрифт</target>
+</phrase>
+<phrase>
+ <source>font size</source>
+ <target>размер шрифта</target>
+</phrase>
+<phrase>
+ <source>font style</source>
+ <target>начертание</target>
+</phrase>
+<phrase>
+ <source>function key</source>
+ <target>функциональная клавиша</target>
+</phrase>
+<phrase>
+ <source>glyph</source>
+ <target>глиф</target>
+</phrase>
+<phrase>
+ <source>group box</source>
+ <target>контейнер элементов</target>
+</phrase>
+<phrase>
+ <source>handle</source>
+ <target>управление</target>
+</phrase>
+<phrase>
+ <source>Help</source>
+ <target>Справка</target>
+</phrase>
+<phrase>
+ <source>Hide</source>
+ <target>Скрыть</target>
+</phrase>
+<phrase>
+ <source>hierarchical selection</source>
+ <target>иерархический выбор</target>
+</phrase>
+<phrase>
+ <source>hold</source>
+ <target>удерживать</target>
+</phrase>
+<phrase>
+ <source>hot spot</source>
+ <target>фокус</target>
+</phrase>
+<phrase>
+ <source>hot zone</source>
+ <target>активная зона</target>
+</phrase>
+<phrase>
+ <source>Icon</source>
+ <target>Значок</target>
+</phrase>
+<phrase>
+ <source>inactive</source>
+ <target>неактивный</target>
+</phrase>
+<phrase>
+ <source>inactive window</source>
+ <target>неактивное окно</target>
+</phrase>
+<phrase>
+ <source>input focus</source>
+ <target>фокус ввода</target>
+</phrase>
+<phrase>
+ <source>Insert</source>
+ <target>Вставить</target>
+</phrase>
+<phrase>
+ <source>Insert Object</source>
+ <target>Вставить объект</target>
+</phrase>
+<phrase>
+ <source>insertion point</source>
+ <target>место вставки</target>
+</phrase>
+<phrase>
+ <source>italic</source>
+ <target>курсив</target>
+</phrase>
+<phrase>
+ <source>label</source>
+ <target>надпись</target>
+</phrase>
+<phrase>
+ <source>landscape</source>
+ <target>альбомная</target>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>ссылка</target>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>связать</target>
+</phrase>
+<phrase>
+ <source>Link Here</source>
+ <target>Создать ссылку здесь</target>
+</phrase>
+<phrase>
+ <source>list box</source>
+ <target>список</target>
+</phrase>
+<phrase>
+ <source>list view</source>
+ <target>список</target>
+</phrase>
+<phrase>
+ <source>manual link</source>
+ <target>ручное связывание</target>
+</phrase>
+<phrase>
+ <source>Maximize</source>
+ <target>Распахнуть</target>
+</phrase>
+<phrase>
+ <source>maximize button</source>
+ <target>кнопка максимизации</target>
+</phrase>
+<phrase>
+ <source>MDI</source>
+ <target>MDI</target>
+</phrase>
+<phrase>
+ <source>menu</source>
+ <target>меню</target>
+</phrase>
+<phrase>
+ <source>menu bar</source>
+ <target>строка меню</target>
+</phrase>
+<phrase>
+ <source>menu button</source>
+ <target>кнопка меню</target>
+</phrase>
+<phrase>
+ <source>menu item</source>
+ <target>элемент меню</target>
+</phrase>
+<phrase>
+ <source>menu title</source>
+ <target>заголовок меню</target>
+</phrase>
+<phrase>
+ <source>message box</source>
+ <target>окно сообщений</target>
+</phrase>
+<phrase>
+ <source>Minimize</source>
+ <target>Свернуть</target>
+</phrase>
+<phrase>
+ <source>minimize button</source>
+ <target>кнопка минимизации</target>
+</phrase>
+<phrase>
+ <source>modal</source>
+ <target>модальный</target>
+</phrase>
+<phrase>
+ <source>mode</source>
+ <target>режим</target>
+</phrase>
+<phrase>
+ <source>modeless</source>
+ <target>немодальный</target>
+</phrase>
+<phrase>
+ <source>modifier key</source>
+ <target>клавиша-модификатор</target>
+</phrase>
+<phrase>
+ <source>mouse</source>
+ <target>мышь</target>
+</phrase>
+<phrase>
+ <source>Move</source>
+ <target>Переместить</target>
+</phrase>
+<phrase>
+ <source>Move Here</source>
+ <target>Переместить сюда</target>
+</phrase>
+<phrase>
+ <source>Multiple Document Interface</source>
+ <target>Multiple Document Interface</target>
+</phrase>
+<phrase>
+ <source>multiple selection list box</source>
+ <target>список с множественным выбором</target>
+</phrase>
+<phrase>
+ <source>My Computer</source>
+ <target>Мой компьютер</target>
+</phrase>
+<phrase>
+ <source>Network Neighborhood</source>
+ <target>Сетевое окружение</target>
+</phrase>
+<phrase>
+ <source>New</source>
+ <target>Новый</target>
+</phrase>
+<phrase>
+ <source>Next</source>
+ <target>Далее</target>
+</phrase>
+<phrase>
+ <source>object</source>
+ <target>объект</target>
+</phrase>
+<phrase>
+ <source>OK</source>
+ <target>ОК</target>
+</phrase>
+<phrase>
+ <source>OLE</source>
+ <target>OLE</target>
+</phrase>
+<phrase>
+ <source>OLE drag and drop</source>
+ <target>OLE-механизм</target>
+</phrase>
+<phrase>
+ <source>OLE embedded object</source>
+ <target>внедрённый OLE-объект</target>
+</phrase>
+<phrase>
+ <source>OLE linked object</source>
+ <target>связанный OLE-объект</target>
+</phrase>
+<phrase>
+ <source>OLE nondefault drag and drop</source>
+ <target>предопределённый OLE-механизм</target>
+</phrase>
+<phrase>
+ <source>Open</source>
+ <target>Открыть</target>
+</phrase>
+<phrase>
+ <source>Open With</source>
+ <target>Открыть с помощью</target>
+</phrase>
+<phrase>
+ <source>option button</source>
+ <target>переключатель</target>
+</phrase>
+<phrase>
+ <source>option-set</source>
+ <target>набор параметров</target>
+</phrase>
+<phrase>
+ <source>package</source>
+ <target>пакет</target>
+</phrase>
+<phrase>
+ <source>Page Setup</source>
+ <target>Параметры страницы</target>
+</phrase>
+<phrase>
+ <source>palette window</source>
+ <target>окно выбора палитры</target>
+</phrase>
+<phrase>
+ <source>pane</source>
+ <target>панель</target>
+</phrase>
+<phrase>
+ <source>parent window</source>
+ <target>родительское окно</target>
+</phrase>
+<phrase>
+ <source>password</source>
+ <target>пароль</target>
+</phrase>
+<phrase>
+ <source>Paste</source>
+ <target>Вставить</target>
+</phrase>
+<phrase>
+ <source>Paste Link</source>
+ <target>Вставить ссылку</target>
+</phrase>
+<phrase>
+ <source>Paste Shortcut</source>
+ <target>Вставить ярлык</target>
+</phrase>
+<phrase>
+ <source>Paste Special</source>
+ <target>Специальная вставка</target>
+</phrase>
+<phrase>
+ <source>path</source>
+ <target>путь</target>
+</phrase>
+<phrase>
+ <source>Pause</source>
+ <target>Пауза</target>
+</phrase>
+<phrase>
+ <source>Play</source>
+ <target>Воспроизведение</target>
+</phrase>
+<phrase>
+ <source>Plug and Play</source>
+ <target>Plug and Play</target>
+</phrase>
+<phrase>
+ <source>point</source>
+ <target>пункт</target>
+</phrase>
+<phrase>
+ <source>pointer</source>
+ <target>указатель</target>
+</phrase>
+<phrase>
+ <source>pop-up menu</source>
+ <target>всплывающее меню</target>
+</phrase>
+<phrase>
+ <source>pop-up window</source>
+ <target>всплывающее окно</target>
+</phrase>
+<phrase>
+ <source>portrait</source>
+ <target>книжная</target>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>нажать</target>
+</phrase>
+<phrase>
+ <source>primary container</source>
+ <target>корневой контейнер</target>
+</phrase>
+<phrase>
+ <source>primary window</source>
+ <target>главное окно</target>
+</phrase>
+<phrase>
+ <source>Print</source>
+ <target>Печать</target>
+</phrase>
+<phrase>
+ <source>printer</source>
+ <target>принтер</target>
+</phrase>
+<phrase>
+ <source>progress indicator</source>
+ <target>индикатор хода процесса</target>
+</phrase>
+<phrase>
+ <source>project</source>
+ <target>проект</target>
+</phrase>
+<phrase>
+ <source>Properties</source>
+ <target>Свойства</target>
+</phrase>
+<phrase>
+ <source>property inspector</source>
+ <target>инспектор свойств</target>
+</phrase>
+<phrase>
+ <source>property page</source>
+ <target>страница свойств</target>
+</phrase>
+<phrase>
+ <source>property sheet</source>
+ <target>группа свойств</target>
+</phrase>
+<phrase>
+ <source>property sheet control</source>
+ <target>управление группой свойств</target>
+</phrase>
+<phrase>
+ <source>Quick View</source>
+ <target>Быстрый просмотр</target>
+</phrase>
+<phrase>
+ <source>read-only</source>
+ <target>только чтение</target>
+</phrase>
+<phrase>
+ <source>Recycle Bin</source>
+ <target>Корзина</target>
+</phrase>
+<phrase>
+ <source>Redo</source>
+ <target>Повторить</target>
+</phrase>
+<phrase>
+ <source>region selection</source>
+ <target>область выделения</target>
+</phrase>
+<phrase>
+ <source>registry</source>
+ <target>реестр</target>
+</phrase>
+<phrase>
+ <source>Repeat</source>
+ <target>Повторить</target>
+</phrase>
+<phrase>
+ <source>Replace</source>
+ <target>Заменить</target>
+</phrase>
+<phrase>
+ <source>Restore</source>
+ <target>Восстановить</target>
+</phrase>
+<phrase>
+ <source>Restore button</source>
+ <target>Кнопка восстановления</target>
+</phrase>
+<phrase>
+ <source>Resume</source>
+ <target>Продолжить</target>
+</phrase>
+<phrase>
+ <source>Retry</source>
+ <target>Повторить</target>
+</phrase>
+<phrase>
+ <source>rich-text box</source>
+ <target>окно RTF-редактора</target>
+</phrase>
+<phrase>
+ <source>Run</source>
+ <target>Выполнить</target>
+</phrase>
+<phrase>
+ <source>Save</source>
+ <target>Сохранить</target>
+</phrase>
+<phrase>
+ <source>Save as</source>
+ <target>Сохранить как</target>
+</phrase>
+<phrase>
+ <source>scroll</source>
+ <target>прокрутить</target>
+</phrase>
+<phrase>
+ <source>scroll arrow</source>
+ <target>кнопка прокрутки</target>
+</phrase>
+<phrase>
+ <source>scroll bar</source>
+ <target>полоса прокрутки</target>
+</phrase>
+<phrase>
+ <source>scroll box</source>
+ <target>окно с полосами прокрутки</target>
+</phrase>
+<phrase>
+ <source>secondary window</source>
+ <target>подчинённое окно</target>
+</phrase>
+<phrase>
+ <source>select</source>
+ <target>выбрать</target>
+</phrase>
+<phrase>
+ <source>Select All</source>
+ <target>Выделить всё</target>
+</phrase>
+<phrase>
+ <source>selection</source>
+ <target>выбор</target>
+</phrase>
+<phrase>
+ <source>selection handle</source>
+ <target>осуществление выбора</target>
+</phrase>
+<phrase>
+ <source>Send To</source>
+ <target>Отправить</target>
+</phrase>
+<phrase>
+ <source>separator</source>
+ <target>разделитель</target>
+</phrase>
+<phrase>
+ <source>Settings</source>
+ <target>Настройки</target>
+</phrase>
+<phrase>
+ <source>Setup</source>
+ <target>Установить</target>
+</phrase>
+<phrase>
+ <source>shortcut</source>
+ <target>ярлык</target>
+</phrase>
+<phrase>
+ <source>shortcut button</source>
+ <target>кнопка</target>
+</phrase>
+<phrase>
+ <source>shortcut icon</source>
+ <target>пиктограмма</target>
+</phrase>
+<phrase>
+ <source>shortcut key</source>
+ <target>комбинация клавиш</target>
+</phrase>
+<phrase>
+ <source>shortcut key control</source>
+ <target>настройка комбинации клавиш</target>
+</phrase>
+<phrase>
+ <source>Show</source>
+ <target>Показать</target>
+</phrase>
+<phrase>
+ <source>Shutdown</source>
+ <target>Выключение</target>
+</phrase>
+<phrase>
+ <source>single selection list box</source>
+ <target>список с одиночным выбором</target>
+</phrase>
+<phrase>
+ <source>Size</source>
+ <target>Размер</target>
+</phrase>
+<phrase>
+ <source>size grip</source>
+ <target>уголок для изменения размера</target>
+</phrase>
+<phrase>
+ <source>slider</source>
+ <target>регулятор</target>
+</phrase>
+<phrase>
+ <source>spin box</source>
+ <target>поле со счетчиком</target>
+</phrase>
+<phrase>
+ <source>Split</source>
+ <target>Разделить</target>
+</phrase>
+<phrase>
+ <source>split bar</source>
+ <target>разделительная черта</target>
+</phrase>
+<phrase>
+ <source>split box</source>
+ <target>разделительная рамка</target>
+</phrase>
+<phrase>
+ <source>Start button</source>
+ <target>кнопка Пуск</target>
+</phrase>
+<phrase>
+ <source>StartUp folder</source>
+ <target>каталог Автозапуска</target>
+</phrase>
+<phrase>
+ <source>status bar</source>
+ <target>строка состояния</target>
+</phrase>
+<phrase>
+ <source>Stop</source>
+ <target>Остановить</target>
+</phrase>
+<phrase>
+ <source>tab control</source>
+ <target>вкладка</target>
+</phrase>
+<phrase>
+ <source>task bar</source>
+ <target>панель задач</target>
+</phrase>
+<phrase>
+ <source>task-oriented Help</source>
+ <target>контекстная справка</target>
+</phrase>
+<phrase>
+ <source>template</source>
+ <target>шаблон</target>
+</phrase>
+<phrase>
+ <source>text box</source>
+ <target>текстовое поле</target>
+</phrase>
+<phrase>
+ <source>title bar</source>
+ <target>заголовок</target>
+</phrase>
+<phrase>
+ <source>title text</source>
+ <target>текст заголовка</target>
+</phrase>
+<phrase>
+ <source>toggle key</source>
+ <target>кнопка-переключатель</target>
+</phrase>
+<phrase>
+ <source>toolbar</source>
+ <target>панель инструментов</target>
+</phrase>
+<phrase>
+ <source>tree view control</source>
+ <target>древовидный список</target>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>тип</target>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>набирать</target>
+</phrase>
+<phrase>
+ <source>unavailable</source>
+ <target>недоступный</target>
+</phrase>
+<phrase>
+ <source>Undo</source>
+ <target>Отменить</target>
+</phrase>
+<phrase>
+ <source>Uninstall</source>
+ <target>Удалить</target>
+</phrase>
+<phrase>
+ <source>View</source>
+ <target>Вид</target>
+</phrase>
+<phrase>
+ <source>visual editing</source>
+ <target>визуальное редактирование</target>
+</phrase>
+<phrase>
+ <source>well control</source>
+ <target>элемент графического интерфейса</target>
+</phrase>
+<phrase>
+ <source>What&apos;s This?</source>
+ <target>Что это?</target>
+</phrase>
+<phrase>
+ <source>Window</source>
+ <target>Окно</target>
+</phrase>
+<phrase>
+ <source>window</source>
+ <target>окно</target>
+</phrase>
+<phrase>
+ <source>Windows Explorer</source>
+ <target>Проводник Windows</target>
+</phrase>
+<phrase>
+ <source>wizard</source>
+ <target>мастер</target>
+</phrase>
+<phrase>
+ <source>workbook</source>
+ <target>учебник</target>
+</phrase>
+<phrase>
+ <source>workgroup</source>
+ <target>рабочая группа</target>
+</phrase>
+<phrase>
+ <source>workspace</source>
+ <target>рабочая область</target>
+</phrase>
+<phrase>
+ <source>Yes</source>
+ <target>Да</target>
+</phrase>
+<phrase>
+ <source>No</source>
+ <target>Нет</target>
+</phrase>
+<phrase>
+ <source>Options</source>
+ <target>Параметры</target>
+</phrase>
+<phrase>
+ <source>directory</source>
+ <target>каталог</target>
+</phrase>
+<phrase>
+ <source>Finish</source>
+ <target>Завершить</target>
+</phrase>
+<phrase>
+ <source>Continue</source>
+ <target>Продолжить</target>
+</phrase>
+<phrase>
+ <source>advanced</source>
+ <target>расширенный</target>
+</phrase>
+<phrase>
+ <source>layout</source>
+ <target>компоновка</target>
+</phrase>
+<phrase>
+ <source>layout</source>
+ <target>компоновщик</target>
+</phrase>
+<phrase>
+ <source>plugin</source>
+ <target>модуль</target>
+</phrase>
+<phrase>
+ <source>script</source>
+ <target>сценарий</target>
+</phrase>
+<phrase>
+ <source>spacer</source>
+ <target>разделитель</target>
+</phrase>
+<phrase>
+ <source>tabbar</source>
+ <target>панель вкладок</target>
+</phrase>
+<phrase>
+ <source>whitespace</source>
+ <target>символ пробела</target>
+</phrase>
+<phrase>
+ <source>Forward</source>
+ <target>Вперёд</target>
+</phrase>
+<phrase>
+ <source>Back</source>
+ <target>Назад</target>
+</phrase>
+<phrase>
+ <source>Search wrapped</source>
+ <target>Поиск с начала</target>
+</phrase>
+<phrase>
+ <source>OK</source>
+ <target>Закрыть</target>
+</phrase>
+<phrase>
+ <source>Match case</source>
+ <target>С учётом регистра</target>
+</phrase>
+<phrase>
+ <source>Case Sensitive</source>
+ <target>Учитывать регистр</target>
+</phrase>
+<phrase>
+ <source>Whole words</source>
+ <target>Слова целиком</target>
+</phrase>
+<phrase>
+ <source>Find Next</source>
+ <target>Найти следующее</target>
+</phrase>
+<phrase>
+ <source>Find Previous</source>
+ <target>Найти предыдущее</target>
+</phrase>
+<phrase>
+ <source>Case Sensitive</source>
+ <target>Учитывать регистр символов</target>
+</phrase>
+<phrase>
+ <source>Whole words only</source>
+ <target>Только слова целиком</target>
+</phrase>
+<phrase>
+ <source>Subwindow</source>
+ <target>Дочернее окно</target>
+</phrase>
+<phrase>
+ <source>Next</source>
+ <target>Следующий</target>
+</phrase>
+<phrase>
+ <source>tree view</source>
+ <target>древовидный список</target>
+</phrase>
+<phrase>
+ <source>ToolTip</source>
+ <target>Подсказка</target>
+</phrase>
+<phrase>
+ <source>Checkable</source>
+ <target>Переключаемое</target>
+</phrase>
+<phrase>
+ <source>Style</source>
+ <target>Начертание</target>
+</phrase>
+<phrase>
+ <source>Point size</source>
+ <target>Размер</target>
+</phrase>
+<phrase>
+ <source>Family</source>
+ <target>Шрифт</target>
+</phrase>
+<phrase>
+ <source>Writing system</source>
+ <target>Система письма</target>
+</phrase>
+<phrase>
+ <source>OK</source>
+ <target>Удалить</target>
+</phrase>
+<phrase>
+ <source>Style</source>
+ <target>Стиль</target>
+</phrase>
+<phrase>
+ <source>Edit</source>
+ <target>Изменить</target>
+</phrase>
+<phrase>
+ <source>Invalid</source>
+ <target>Некорректный</target>
+</phrase>
+<phrase>
+ <source>Invalid</source>
+ <target>Некорректная</target>
+</phrase>
+<phrase>
+ <source>Invalid</source>
+ <target>Некорректное</target>
+</phrase>
+<phrase>
+ <source>tag</source>
+ <target>тэг</target>
+</phrase>
+<phrase>
+ <source>tab order</source>
+ <target>порядок обхода</target>
+</phrase>
+<phrase>
+ <source>Ascending</source>
+ <target>По возрастанию</target>
+</phrase>
+<phrase>
+ <source>Descending</source>
+ <target>По убыванию</target>
+</phrase>
+<phrase>
+ <source>Reset</source>
+ <target>Сбросить</target>
+</phrase>
+<phrase>
+ <source>Sort order</source>
+ <target>Порядок сортировки</target>
+</phrase>
+<phrase>
+ <source>Appearance</source>
+ <target>Внешний вид</target>
+</phrase>
+<phrase>
+ <source>View</source>
+ <target>Просмотр</target>
+</phrase>
+<phrase>
+ <source>Actions</source>
+ <target>Действия</target>
+</phrase>
+<phrase>
+ <source>Table of Contents</source>
+ <target>Оглавление</target>
+</phrase>
+<phrase>
+ <source>parse</source>
+ <target>разобрать</target>
+</phrase>
+<phrase>
+ <source>parsing</source>
+ <target>разбор</target>
+</phrase>
+<phrase>
+ <source>phrasebook</source>
+ <target>глоссарий</target>
+</phrase>
+<phrase>
+ <source>phrase book</source>
+ <target>глоссарий</target>
+</phrase>
+<phrase>
+ <source>In use</source>
+ <target>Используется</target>
+</phrase>
+<phrase>
+ <source>Access denied</source>
+ <target>Доступ запрещён</target>
+</phrase>
+<phrase>
+ <source>No error</source>
+ <target>Нет ошибки</target>
+</phrase>
+<phrase>
+ <source>Not supported</source>
+ <target>Не поддерживается</target>
+</phrase>
+<phrase>
+ <source>Already exists</source>
+ <target>Уже существует</target>
+</phrase>
+<phrase>
+ <source>Permission denied</source>
+ <target>Доступ запрещён</target>
+</phrase>
+<phrase>
+ <source>Previous</source>
+ <target>Предыдущий</target>
+</phrase>
+<phrase>
+ <source>Next</source>
+ <target>Следующее</target>
+</phrase>
+<phrase>
+ <source>Previous</source>
+ <target>Предыдущее</target>
+</phrase>
+</QPH>
diff --git a/src/linguist/phrasebooks/spanish.qph b/src/linguist/phrasebooks/spanish.qph
new file mode 100644
index 000000000..dacb6b5d2
--- /dev/null
+++ b/src/linguist/phrasebooks/spanish.qph
@@ -0,0 +1,1086 @@
+<!DOCTYPE QPH><QPH language="es">
+<phrase>
+ <source>About</source>
+ <target>Acerca de</target>
+</phrase>
+<phrase>
+ <source>access key</source>
+ <target>tecla de acceso</target>
+</phrase>
+<phrase>
+ <source>accessibility</source>
+ <target>accesibilidad</target>
+</phrase>
+<phrase>
+ <source>action handle</source>
+ <target>controlador de acciones</target>
+</phrase>
+<phrase>
+ <source>active</source>
+ <target>activo</target>
+</phrase>
+<phrase>
+ <source>active end</source>
+ <target>fin de la selección activa</target>
+</phrase>
+<phrase>
+ <source>active object</source>
+ <target>objeto activo</target>
+</phrase>
+<phrase>
+ <source>active window</source>
+ <target>ventana activa</target>
+</phrase>
+<phrase>
+ <source>adornment</source>
+ <target>opción gráfica</target>
+</phrase>
+<phrase>
+ <source>Always on Top</source>
+ <target>Siempre visible</target>
+</phrase>
+<phrase>
+ <source>anchor point</source>
+ <target>inicio de la selección activa</target>
+</phrase>
+<phrase>
+ <source>Apply</source>
+ <target>Aplicar</target>
+</phrase>
+<phrase>
+ <source>auto-exit</source>
+ <target>salida automática</target>
+</phrase>
+<phrase>
+ <source>auto-repeat</source>
+ <target>repetición automática</target>
+</phrase>
+<phrase>
+ <source>automatic link</source>
+ <target>vínculo automático</target>
+</phrase>
+<phrase>
+ <source>automatic scrolling</source>
+ <target>desplazamiento automático</target>
+</phrase>
+<phrase>
+ <source>autoscroll</source>
+ <target>desplazamiento automático</target>
+</phrase>
+<phrase>
+ <source>Back</source>
+ <target>Atrás</target>
+</phrase>
+<phrase>
+ <source>barrel button</source>
+ <target>botón del lápiz</target>
+ <definition>pen</definition>
+</phrase>
+<phrase>
+ <source>barrel-tap</source>
+ <target>puntenar con el botón presionado</target>
+</phrase>
+<phrase>
+ <source>boxed edit</source>
+ <target>edición en casilla</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>Browse</source>
+ <target>Examinar</target>
+</phrase>
+<phrase>
+ <source>Cancel</source>
+ <target>Cancelar</target>
+</phrase>
+<phrase>
+ <source>cascading menu</source>
+ <target>menú en cascada</target>
+</phrase>
+<phrase>
+ <source>check box</source>
+ <target>casilla de verificación</target>
+</phrase>
+<phrase>
+ <source>check mark</source>
+ <target>marca de verificación</target>
+</phrase>
+<phrase>
+ <source>child window</source>
+ <target>ventana secundaria</target>
+</phrase>
+<phrase>
+ <source>choose</source>
+ <target>elegir</target>
+</phrase>
+<phrase>
+ <source>click</source>
+ <target>hacer clic</target>
+</phrase>
+<phrase>
+ <source>Clipboard</source>
+ <target>Portapapeles</target>
+</phrase>
+<phrase>
+ <source>Close</source>
+ <target>Cerrar</target>
+</phrase>
+<phrase>
+ <source>Close button</source>
+ <target>botón &amp;quot;Cerrar&amp;quot;</target>
+</phrase>
+<phrase>
+ <source>collapse</source>
+ <target>contraer</target>
+ <definition>outline/esquerna</definition>
+</phrase>
+<phrase>
+ <source>column heading</source>
+ <target>encabezado de columna</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>combo box</source>
+ <target>cuadro combinado</target>
+</phrase>
+<phrase>
+ <source>command button</source>
+ <target>botón de comando</target>
+</phrase>
+<phrase>
+ <source>container</source>
+ <target>contenedor</target>
+</phrase>
+<phrase>
+ <source>context-sensitive Help</source>
+ <target>ayuda interactiva</target>
+</phrase>
+<phrase>
+ <source>contextual</source>
+ <target>contextual</target>
+</phrase>
+<phrase>
+ <source>control</source>
+ <target>control</target>
+</phrase>
+<phrase>
+ <source>Copy</source>
+ <target>Copiar</target>
+</phrase>
+<phrase>
+ <source>Copy here</source>
+ <target>Copiar aquí</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut</source>
+ <target>Crear acceso directo</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut Here</source>
+ <target>Crear acceso directo aquí</target>
+</phrase>
+<phrase>
+ <source>Cut</source>
+ <target>Cortar</target>
+</phrase>
+<phrase>
+ <source>default</source>
+ <target>predeterminado</target>
+</phrase>
+<phrase>
+ <source>default button</source>
+ <target>botón predeterminado</target>
+</phrase>
+<phrase>
+ <source>Delete</source>
+ <target>Eliminar</target>
+</phrase>
+<phrase>
+ <source>desktop</source>
+ <target>escritorio</target>
+</phrase>
+<phrase>
+ <source>destination</source>
+ <target>destino</target>
+</phrase>
+<phrase>
+ <source>dialog box</source>
+ <target>cuadro de diálogo</target>
+</phrase>
+<phrase>
+ <source>disability</source>
+ <target>Discapacidades</target>
+</phrase>
+<phrase>
+ <source>disjoint selection</source>
+ <target>selección disjunta</target>
+</phrase>
+<phrase>
+ <source>dock</source>
+ <target>acoplar</target>
+</phrase>
+<phrase>
+ <source>document</source>
+ <target>documento</target>
+</phrase>
+<phrase>
+ <source>double-click</source>
+ <target>hacer doble clic</target>
+</phrase>
+<phrase>
+ <source>double-tap</source>
+ <target>puntear dos veces</target>
+</phrase>
+<phrase>
+ <source>drag</source>
+ <target>arrastrar</target>
+</phrase>
+<phrase>
+ <source>drag-and-drop</source>
+ <target>arrastrar y colocar</target>
+</phrase>
+<phrase>
+ <source>drop-down combo box</source>
+ <target>cuadro combinado desplegable</target>
+</phrase>
+<phrase>
+ <source>drop-down list box</source>
+ <target>cuadro de lista desplegable</target>
+</phrase>
+<phrase>
+ <source>drop-down menu</source>
+ <target>menú desplegable</target>
+</phrase>
+<phrase>
+ <source>Edit</source>
+ <target>Edición</target>
+</phrase>
+<phrase>
+ <source>Edit menu</source>
+ <target>menú Edición</target>
+</phrase>
+<phrase>
+ <source>ellipsis</source>
+ <target>puntos suspensivos</target>
+</phrase>
+<phrase>
+ <source>embedded object</source>
+ <target>objeto incrustado</target>
+</phrase>
+<phrase>
+ <source>Exit</source>
+ <target>Salir</target>
+</phrase>
+<phrase>
+ <source>expand</source>
+ <target>expandir</target>
+ <definition>an outline/un esquema</definition>
+</phrase>
+<phrase>
+ <source>Explore</source>
+ <target>Explorar</target>
+</phrase>
+<phrase>
+ <source>extended selection</source>
+ <target>selección extendida</target>
+</phrase>
+<phrase>
+ <source>extended selection list box</source>
+ <target>cuadro de lista de selección extendida</target>
+</phrase>
+<phrase>
+ <source>file</source>
+ <target>archivo</target>
+</phrase>
+<phrase>
+ <source>File menu</source>
+ <target>menú Archivo</target>
+</phrase>
+<phrase>
+ <source>Find</source>
+ <target>Buscar</target>
+</phrase>
+<phrase>
+ <source>Find Next</source>
+ <target>Buscar siguiente</target>
+</phrase>
+<phrase>
+ <source>Find What</source>
+ <target>Buscar</target>
+</phrase>
+<phrase>
+ <source>folder</source>
+ <target>carpeta</target>
+</phrase>
+<phrase>
+ <source>font</source>
+ <target>fuente</target>
+</phrase>
+<phrase>
+ <source>font size</source>
+ <target>tamaño de fuente</target>
+</phrase>
+<phrase>
+ <source>font style</source>
+ <target>estilo de fuente</target>
+</phrase>
+<phrase>
+ <source>function key</source>
+ <target>tecla de función</target>
+</phrase>
+<phrase>
+ <source>gesture</source>
+ <target>signo</target>
+</phrase>
+<phrase>
+ <source>glyph</source>
+ <target>símbolo gráfico</target>
+</phrase>
+<phrase>
+ <source>group box</source>
+ <target>cuadro de grupo</target>
+</phrase>
+<phrase>
+ <source>handle</source>
+ <target>controlar</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>handle</source>
+ <target>controlador</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>Help</source>
+ <target>Ayuda</target>
+</phrase>
+<phrase>
+ <source>Help menu</source>
+ <target>menú Ayuda</target>
+</phrase>
+<phrase>
+ <source>Hide</source>
+ <target>Ocultar</target>
+</phrase>
+<phrase>
+ <source>hierarchical selection</source>
+ <target>selección jerárquica</target>
+</phrase>
+<phrase>
+ <source>hold</source>
+ <target>mantener presionado</target>
+</phrase>
+<phrase>
+ <source>hot spot</source>
+ <target>punto interactivo</target>
+</phrase>
+<phrase>
+ <source>hot zone</source>
+ <target>zona interactiva</target>
+</phrase>
+<phrase>
+ <source>icon</source>
+ <target>icono</target>
+</phrase>
+<phrase>
+ <source>inactive</source>
+ <target>inactivo</target>
+</phrase>
+<phrase>
+ <source>inactive window</source>
+ <target>ventana inactiva</target>
+</phrase>
+<phrase>
+ <source>ink</source>
+ <target>trazo</target>
+</phrase>
+<phrase>
+ <source>ink edit</source>
+ <target>editor de trazos</target>
+</phrase>
+<phrase>
+ <source>input focus</source>
+ <target>zona de entrada</target>
+</phrase>
+<phrase>
+ <source>input focus</source>
+ <target>zona de entrada de datos</target>
+</phrase>
+<phrase>
+ <source>Insert</source>
+ <target>menú Insertar</target>
+</phrase>
+<phrase>
+ <source>Insert Object</source>
+ <target>Insertar objeto</target>
+</phrase>
+<phrase>
+ <source>insertion point</source>
+ <target>punto de inserción</target>
+</phrase>
+<phrase>
+ <source>italic</source>
+ <target>cursiva</target>
+</phrase>
+<phrase>
+ <source>label</source>
+ <target>etiqueta</target>
+</phrase>
+<phrase>
+ <source>landscape</source>
+ <target>horizontal</target>
+</phrase>
+<phrase>
+ <source>lasso-tap</source>
+ <target>punteo en la selección</target>
+</phrase>
+<phrase>
+ <source>lens</source>
+ <target>lente</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>vincular</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>vínculo</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>Link Here</source>
+ <target>Vincular aquí</target>
+</phrase>
+<phrase>
+ <source>list box</source>
+ <target>cuadro de lista</target>
+</phrase>
+<phrase>
+ <source>list view</source>
+ <target>ver lista</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>list view</source>
+ <target>presentación de iconos </target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>manual link</source>
+ <target>vínculo manual</target>
+</phrase>
+<phrase>
+ <source>Maximize</source>
+ <target>Maximizar</target>
+</phrase>
+<phrase>
+ <source>maximize button</source>
+ <target>botón de maximizar</target>
+</phrase>
+<phrase>
+ <source>MDI</source>
+ <target>MDI</target>
+</phrase>
+<phrase>
+ <source>menu</source>
+ <target>menú</target>
+</phrase>
+<phrase>
+ <source>menu bar</source>
+ <target>barra de menús</target>
+</phrase>
+<phrase>
+ <source>menu button</source>
+ <target>botón de menú</target>
+</phrase>
+<phrase>
+ <source>menu item</source>
+ <target>elemento de menú</target>
+</phrase>
+<phrase>
+ <source>menu title</source>
+ <target>título de menú</target>
+</phrase>
+<phrase>
+ <source>message box</source>
+ <target>cuadro de mensaje</target>
+</phrase>
+<phrase>
+ <source>Minimize</source>
+ <target>Minimizar</target>
+</phrase>
+<phrase>
+ <source>minimize button</source>
+ <target>botón de minimizar</target>
+</phrase>
+<phrase>
+ <source>mixed-value</source>
+ <target>valores mezclados</target>
+</phrase>
+<phrase>
+ <source>modal</source>
+ <target>modal</target>
+</phrase>
+<phrase>
+ <source>mode</source>
+ <target>modo</target>
+</phrase>
+<phrase>
+ <source>modeless</source>
+ <target>sin modo</target>
+</phrase>
+<phrase>
+ <source>modifier key</source>
+ <target>tecla modificadora</target>
+</phrase>
+<phrase>
+ <source>mouse</source>
+ <target>mouse</target>
+</phrase>
+<phrase>
+ <source>Move</source>
+ <target>Mover</target>
+</phrase>
+<phrase>
+ <source>Move Here</source>
+ <target>Mover aquí</target>
+</phrase>
+<phrase>
+ <source>Multiple Document Interface</source>
+ <target>interfaz de documentos múltiples</target>
+</phrase>
+<phrase>
+ <source>multiple selection list box</source>
+ <target>cuadro de lista de selección múltiple</target>
+</phrase>
+<phrase>
+ <source>My Computer</source>
+ <target>Mi PC</target>
+ <definition>icon/icono</definition>
+</phrase>
+<phrase>
+ <source>Network Neighborhood</source>
+ <target>Entorno de red</target>
+ <definition>icon/icono</definition>
+</phrase>
+<phrase>
+ <source>New</source>
+ <target>Nuevo</target>
+</phrase>
+<phrase>
+ <source>Next</source>
+ <target>Sigulente</target>
+</phrase>
+<phrase>
+ <source>object</source>
+ <target>objeto</target>
+</phrase>
+<phrase>
+ <source>OK</source>
+ <target>Aceptar</target>
+</phrase>
+<phrase>
+ <source>OLE</source>
+ <target>OLE</target>
+</phrase>
+<phrase>
+ <source>OLE drag and drop</source>
+ <target>Arrastrar y colocar de OLE</target>
+</phrase>
+<phrase>
+ <source>OLE embedded object</source>
+ <target>Objeto incrustado de OLE</target>
+</phrase>
+<phrase>
+ <source>OLE linked object</source>
+ <target>Objeto vinculado de OLE</target>
+</phrase>
+<phrase>
+ <source>OLE nondefault drag and drop</source>
+ <target>Arrastrar y colocar no predeterminado de OLE</target>
+</phrase>
+<phrase>
+ <source>Open</source>
+ <target>Abrir</target>
+</phrase>
+<phrase>
+ <source>Open With</source>
+ <target>Abrir con</target>
+</phrase>
+<phrase>
+ <source>option button</source>
+ <target>botón de opción</target>
+</phrase>
+<phrase>
+ <source>option-set</source>
+ <target>opción establecida</target>
+</phrase>
+<phrase>
+ <source>package</source>
+ <target>paquete</target>
+</phrase>
+<phrase>
+ <source>Page Setup</source>
+ <target>Preparar página</target>
+</phrase>
+<phrase>
+ <source>palette window</source>
+ <target>ventana de paleta</target>
+</phrase>
+<phrase>
+ <source>pane</source>
+ <target>panel</target>
+</phrase>
+<phrase>
+ <source>parent window</source>
+ <target>ventana principal</target>
+</phrase>
+<phrase>
+ <source>password</source>
+ <target>contraseña</target>
+</phrase>
+<phrase>
+ <source>Paste</source>
+ <target>Pegar</target>
+</phrase>
+<phrase>
+ <source>Paste Link</source>
+ <target>Pegar vínculo</target>
+</phrase>
+<phrase>
+ <source>Paste Shortcut</source>
+ <target>Pegar acceso directo</target>
+</phrase>
+<phrase>
+ <source>Paste Special</source>
+ <target>Pegado especial</target>
+</phrase>
+<phrase>
+ <source>path</source>
+ <target>ruta de acceso</target>
+</phrase>
+<phrase>
+ <source>Pause</source>
+ <target>Pausa</target>
+</phrase>
+<phrase>
+ <source>pen</source>
+ <target>lápiz</target>
+</phrase>
+<phrase>
+ <source>Play</source>
+ <target>Reproducir</target>
+</phrase>
+<phrase>
+ <source>Plug and Play</source>
+ <target>Plug and Play</target>
+</phrase>
+<phrase>
+ <source>point</source>
+ <target>señalar</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>point</source>
+ <target>punto</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>pointer</source>
+ <target>puntero</target>
+</phrase>
+<phrase>
+ <source>pop-up menu</source>
+ <target>menú emergente</target>
+</phrase>
+<phrase>
+ <source>pop-up window</source>
+ <target>ventana emergente</target>
+</phrase>
+<phrase>
+ <source>portrait</source>
+ <target>vertical</target>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>presionar</target>
+ <definition>and hold a mouse button/y mantener presionado un botón del mouse</definition>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>presionar</target>
+ <definition>a key/una tecla</definition>
+</phrase>
+<phrase>
+ <source>primary container</source>
+ <target>contenedor primario</target>
+</phrase>
+<phrase>
+ <source>primary window</source>
+ <target>ventana principal</target>
+</phrase>
+<phrase>
+ <source>Print</source>
+ <target>Imprimir</target>
+</phrase>
+<phrase>
+ <source>printer</source>
+ <target>impresora</target>
+</phrase>
+<phrase>
+ <source>progress indicator</source>
+ <target>indicador de progreso</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>project</source>
+ <target>proyecto</target>
+</phrase>
+<phrase>
+ <source>Properties</source>
+ <target>Propiedades</target>
+</phrase>
+<phrase>
+ <source>property inspector</source>
+ <target>monitor de propiedades</target>
+</phrase>
+<phrase>
+ <source>property page</source>
+ <target>página de propiedades</target>
+</phrase>
+<phrase>
+ <source>property sheet</source>
+ <target>hoja de propiedades</target>
+</phrase>
+<phrase>
+ <source>property sheet control</source>
+ <target>control de la hoja de propiedades</target>
+</phrase>
+<phrase>
+ <source>Quick View</source>
+ <target>Vista rápida</target>
+</phrase>
+<phrase>
+ <source>read-only</source>
+ <target>sólo lectura</target>
+</phrase>
+<phrase>
+ <source>recognition</source>
+ <target>reconocimiento</target>
+</phrase>
+<phrase>
+ <source>Recycle Bin</source>
+ <target>Papelera de reciclaje</target>
+ <definition>Icon/icono</definition>
+</phrase>
+<phrase>
+ <source>Redo</source>
+ <target>Rehacer</target>
+</phrase>
+<phrase>
+ <source>region selection</source>
+ <target>selección de área</target>
+</phrase>
+<phrase>
+ <source>registry</source>
+ <target>Registro de configuraciones</target>
+</phrase>
+<phrase>
+ <source>Repeat</source>
+ <target>Repetir</target>
+</phrase>
+<phrase>
+ <source>Replace</source>
+ <target>Reemplazar</target>
+</phrase>
+<phrase>
+ <source>Restore</source>
+ <target>Restaurar</target>
+</phrase>
+<phrase>
+ <source>Restore button</source>
+ <target>botón &amp;quot;Restaurar&amp;quot;</target>
+</phrase>
+<phrase>
+ <source>Resume</source>
+ <target>Reanudar</target>
+</phrase>
+<phrase>
+ <source>Retry</source>
+ <target>Reintentar</target>
+</phrase>
+<phrase>
+ <source>rich-text box</source>
+ <target>cuadro de texto enriquecido</target>
+</phrase>
+<phrase>
+ <source>Run</source>
+ <target>Ejecutar</target>
+</phrase>
+<phrase>
+ <source>Save</source>
+ <target>Guardar</target>
+</phrase>
+<phrase>
+ <source>Save as</source>
+ <target>Guardar como</target>
+</phrase>
+<phrase>
+ <source>scroll</source>
+ <target>desplazar</target>
+</phrase>
+<phrase>
+ <source>scroll arrow</source>
+ <target>flecha de desplazamiento</target>
+</phrase>
+<phrase>
+ <source>scroll bar</source>
+ <target>barra de desplazamiento</target>
+</phrase>
+<phrase>
+ <source>scroll box</source>
+ <target>cuadro de desplazamiento</target>
+</phrase>
+<phrase>
+ <source>secondary window</source>
+ <target>ventana secundaria</target>
+</phrase>
+<phrase>
+ <source>select</source>
+ <target>seleccionar</target>
+</phrase>
+<phrase>
+ <source>Select All</source>
+ <target>Seleccionar todo</target>
+</phrase>
+<phrase>
+ <source>selection</source>
+ <target>selección</target>
+</phrase>
+<phrase>
+ <source>selection handle</source>
+ <target>controlador de selección</target>
+</phrase>
+<phrase>
+ <source>Send To</source>
+ <target>Enviar a</target>
+</phrase>
+<phrase>
+ <source>separator</source>
+ <target>separador</target>
+</phrase>
+<phrase>
+ <source>Settings</source>
+ <target>Configuración</target>
+</phrase>
+<phrase>
+ <source>Setup</source>
+ <target>Configurar</target>
+ <definition>for a device already installed</definition>
+</phrase>
+<phrase>
+ <source>Setup</source>
+ <target>Instalar</target>
+ <definition>for an application</definition>
+</phrase>
+<phrase>
+ <source>shortcut</source>
+ <target>acceso directo</target>
+</phrase>
+<phrase>
+ <source>shortcut button</source>
+ <target>botón de acceso directo</target>
+</phrase>
+<phrase>
+ <source>shortcut icon</source>
+ <target>icono de acceso directo</target>
+</phrase>
+<phrase>
+ <source>shortcut key</source>
+ <target>tecla de método abreviado</target>
+</phrase>
+<phrase>
+ <source>shortcut key control</source>
+ <target>control de la tecla de método abreviado</target>
+</phrase>
+<phrase>
+ <source>Show</source>
+ <target>Mostrar</target>
+</phrase>
+<phrase>
+ <source>Shutdown</source>
+ <target>Apagar el sistema</target>
+</phrase>
+<phrase>
+ <source>single selection list box</source>
+ <target>cuadro de lista de selección simple</target>
+</phrase>
+<phrase>
+ <source>Size</source>
+ <target>Tamaño</target>
+</phrase>
+<phrase>
+ <source>size grip</source>
+ <target>ajuste de tamaño</target>
+</phrase>
+<phrase>
+ <source>slider</source>
+ <target>control deslizante</target>
+</phrase>
+<phrase>
+ <source>spin box</source>
+ <target>cuadro selector</target>
+</phrase>
+<phrase>
+ <source>Split</source>
+ <target>Dividir</target>
+</phrase>
+<phrase>
+ <source>split bar</source>
+ <target>barra de división</target>
+</phrase>
+<phrase>
+ <source>split box</source>
+ <target>cuadro de división</target>
+</phrase>
+<phrase>
+ <source>Start button</source>
+ <target>botón &amp;quot;Inicio&amp;quot;</target>
+</phrase>
+<phrase>
+ <source>StartUp folder</source>
+ <target>carpeta Inicio</target>
+</phrase>
+<phrase>
+ <source>status bar</source>
+ <target>barra de estado</target>
+</phrase>
+<phrase>
+ <source>Stop</source>
+ <target>Detener</target>
+</phrase>
+<phrase>
+ <source>tab control</source>
+ <target>control de fichas</target>
+</phrase>
+<phrase>
+ <source>tap</source>
+ <target>puntear</target>
+</phrase>
+<phrase>
+ <source>task bar</source>
+ <target>barra de tareas</target>
+</phrase>
+<phrase>
+ <source>task-oriented Help</source>
+ <target>Ayuda relativa a la tarea</target>
+</phrase>
+<phrase>
+ <source>template</source>
+ <target>plantilla</target>
+</phrase>
+<phrase>
+ <source>text box</source>
+ <target>cuadro de texto</target>
+</phrase>
+<phrase>
+ <source>title bar</source>
+ <target>barra de título</target>
+</phrase>
+<phrase>
+ <source>title text</source>
+ <target>texto de título</target>
+</phrase>
+<phrase>
+ <source>toggle key</source>
+ <target>tecla para alternar</target>
+</phrase>
+<phrase>
+ <source>toolbar</source>
+ <target>barra de herramientas</target>
+</phrase>
+<phrase>
+ <source>tooltip</source>
+ <target>sugerencias</target>
+</phrase>
+<phrase>
+ <source>tree view control</source>
+ <target>control de visión en árbol</target>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>escribir</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>tipo</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>unavailable</source>
+ <target>no disponible</target>
+</phrase>
+<phrase>
+ <source>Undo</source>
+ <target>Deshacer</target>
+</phrase>
+<phrase>
+ <source>Uninstall</source>
+ <target>Desinstalar</target>
+</phrase>
+<phrase>
+ <source>View</source>
+ <target>menú Ver</target>
+</phrase>
+<phrase>
+ <source>visual editing</source>
+ <target>edición visual</target>
+</phrase>
+<phrase>
+ <source>well control</source>
+ <target>control de opciones gráficas</target>
+</phrase>
+<phrase>
+ <source>What&apos;s This?</source>
+ <target>¿Qué es esto?</target>
+</phrase>
+<phrase>
+ <source>Window</source>
+ <target>menú Ventana</target>
+</phrase>
+<phrase>
+ <source>window</source>
+ <target>ventana</target>
+</phrase>
+<phrase>
+ <source>Windows Explorer</source>
+ <target>Explorador de Windows</target>
+</phrase>
+<phrase>
+ <source>wizard</source>
+ <target>asistente</target>
+</phrase>
+<phrase>
+ <source>workbook</source>
+ <target>libro</target>
+</phrase>
+<phrase>
+ <source>workbook</source>
+ <target>libro de trabajo</target>
+</phrase>
+<phrase>
+ <source>workgroup</source>
+ <target>grupo de trabajo</target>
+</phrase>
+<phrase>
+ <source>workspace</source>
+ <target>área de trabajo</target>
+</phrase>
+<phrase>
+ <source>Yes</source>
+ <target>Sí</target>
+</phrase>
+</QPH>
diff --git a/src/linguist/phrasebooks/swedish.qph b/src/linguist/phrasebooks/swedish.qph
new file mode 100644
index 000000000..a88735ec7
--- /dev/null
+++ b/src/linguist/phrasebooks/swedish.qph
@@ -0,0 +1,1010 @@
+<!DOCTYPE QPH><QPH language="sv">
+<phrase>
+ <source>About</source>
+ <target>Om</target>
+</phrase>
+<phrase>
+ <source>access key</source>
+ <target>snabbtangent</target>
+</phrase>
+<phrase>
+ <source>accessibility</source>
+ <target>Hjälpmedel</target>
+ <definition>ikonen Accessibility</definition>
+</phrase>
+<phrase>
+ <source>accessibility</source>
+ <target>tillgänglighet</target>
+</phrase>
+<phrase>
+ <source>action handle</source>
+ <target>funktionshandtag</target>
+</phrase>
+<phrase>
+ <source>active</source>
+ <target>aktiv</target>
+</phrase>
+<phrase>
+ <source>active end</source>
+ <target>aktiv slutpunkt</target>
+</phrase>
+<phrase>
+ <source>active object</source>
+ <target>aktivt objekt</target>
+</phrase>
+<phrase>
+ <source>active window</source>
+ <target>aktivt fönster</target>
+</phrase>
+<phrase>
+ <source>adornment</source>
+ <target>fönsterfält</target>
+</phrase>
+<phrase>
+ <source>Always on Top</source>
+ <target>Alltid överst</target>
+</phrase>
+<phrase>
+ <source>anchor point</source>
+ <target>startpunkt</target>
+</phrase>
+<phrase>
+ <source>Apply</source>
+ <target>Verkställ</target>
+</phrase>
+<phrase>
+ <source>auto-exit</source>
+ <target>flytta automatiskt</target>
+</phrase>
+<phrase>
+ <source>auto-repeat</source>
+ <target>upprepa automatiskt</target>
+</phrase>
+<phrase>
+ <source>automatic link</source>
+ <target>automatisk länk</target>
+</phrase>
+<phrase>
+ <source>automatic scrolling</source>
+ <target>automatisk rullning</target>
+</phrase>
+<phrase>
+ <source>autoscroll</source>
+ <target>automatisk rullning</target>
+</phrase>
+<phrase>
+ <source>Back</source>
+ <target>Föregående</target>
+</phrase>
+<phrase>
+ <source>Browse</source>
+ <target>Bläddra</target>
+</phrase>
+<phrase>
+ <source>Cancel</source>
+ <target>Avbryt</target>
+</phrase>
+<phrase>
+ <source>cascading menu</source>
+ <target>undermeny</target>
+</phrase>
+<phrase>
+ <source>check box</source>
+ <target>kryssruta</target>
+</phrase>
+<phrase>
+ <source>check mark</source>
+ <target>markering</target>
+</phrase>
+<phrase>
+ <source>child window</source>
+ <target>underfönster</target>
+</phrase>
+<phrase>
+ <source>choose</source>
+ <target>välj</target>
+</phrase>
+<phrase>
+ <source>click</source>
+ <target>klicka</target>
+</phrase>
+<phrase>
+ <source>Clipboard</source>
+ <target>Urklipp</target>
+</phrase>
+<phrase>
+ <source>Close</source>
+ <target>Stäng</target>
+</phrase>
+<phrase>
+ <source>Close button</source>
+ <target>stängningsknapp</target>
+</phrase>
+<phrase>
+ <source>collapse</source>
+ <target>komprimera</target>
+ <definition>outline</definition>
+</phrase>
+<phrase>
+ <source>column heading</source>
+ <target>kolumnrubrik</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>combo box</source>
+ <target>kombinationsruta</target>
+</phrase>
+<phrase>
+ <source>command button</source>
+ <target>kommandoknapp</target>
+</phrase>
+<phrase>
+ <source>container</source>
+ <target>behållare</target>
+</phrase>
+<phrase>
+ <source>context-sensitive Help</source>
+ <target>sammanhangsberoende hjälp</target>
+</phrase>
+<phrase>
+ <source>contextual</source>
+ <target>sammanhangsberoende</target>
+</phrase>
+<phrase>
+ <source>control</source>
+ <target>kontroll</target>
+</phrase>
+<phrase>
+ <source>Copy</source>
+ <target>Kopiera</target>
+</phrase>
+<phrase>
+ <source>Copy here</source>
+ <target>Kopiera hit</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut</source>
+ <target>Skapa genväg</target>
+</phrase>
+<phrase>
+ <source>Create Shortcut Here</source>
+ <target>Skapa genväg här</target>
+</phrase>
+<phrase>
+ <source>Cut</source>
+ <target>Klipp ut</target>
+</phrase>
+<phrase>
+ <source>default</source>
+ <target>standard</target>
+</phrase>
+<phrase>
+ <source>default button</source>
+ <target>standardknapp</target>
+</phrase>
+<phrase>
+ <source>Delete</source>
+ <target>Ta bort</target>
+</phrase>
+<phrase>
+ <source>desktop</source>
+ <target>skrivbord</target>
+</phrase>
+<phrase>
+ <source>destination</source>
+ <target>mål</target>
+</phrase>
+<phrase>
+ <source>dialog box</source>
+ <target>dialogruta</target>
+</phrase>
+<phrase>
+ <source>disability</source>
+ <target>oförmåga</target>
+</phrase>
+<phrase>
+ <source>disjoint selection</source>
+ <target>osammanhängande markering</target>
+</phrase>
+<phrase>
+ <source>dock</source>
+ <target>docka</target>
+</phrase>
+<phrase>
+ <source>document</source>
+ <target>dokument</target>
+</phrase>
+<phrase>
+ <source>double-click</source>
+ <target>dubbelklicka på</target>
+</phrase>
+<phrase>
+ <source>drag</source>
+ <target>dra</target>
+</phrase>
+<phrase>
+ <source>drag-and-drop</source>
+ <target>dra och släpp</target>
+</phrase>
+<phrase>
+ <source>drop-down combo box</source>
+ <target>nedrullningsbar kombinationsruta</target>
+</phrase>
+<phrase>
+ <source>drop-down list box</source>
+ <target>nedrullningsbar listruta</target>
+</phrase>
+<phrase>
+ <source>drop-down menu</source>
+ <target>nedrullningsbar meny</target>
+</phrase>
+<phrase>
+ <source>Edit</source>
+ <target>Redigera</target>
+</phrase>
+<phrase>
+ <source>ellipsis</source>
+ <target>punkter</target>
+</phrase>
+<phrase>
+ <source>embedded object</source>
+ <target>inbäddat objekt</target>
+</phrase>
+<phrase>
+ <source>Exit</source>
+ <target>Avsluta</target>
+</phrase>
+<phrase>
+ <source>expand</source>
+ <target>expandera</target>
+ <definition>an outline</definition>
+</phrase>
+<phrase>
+ <source>Explore</source>
+ <target>Utforska</target>
+</phrase>
+<phrase>
+ <source>extended selection</source>
+ <target>utökad markering</target>
+</phrase>
+<phrase>
+ <source>extended selection list box</source>
+ <target>listruta för utökad markering</target>
+</phrase>
+<phrase>
+ <source>File</source>
+ <target>Arkiv</target>
+ <definition>menu/meny</definition>
+</phrase>
+<phrase>
+ <source>file</source>
+ <target>fil</target>
+</phrase>
+<phrase>
+ <source>Find</source>
+ <target>Sök</target>
+</phrase>
+<phrase>
+ <source>Find Next</source>
+ <target>Sök nästa</target>
+</phrase>
+<phrase>
+ <source>Find What</source>
+ <target>Sök efter</target>
+</phrase>
+<phrase>
+ <source>folder</source>
+ <target>mapp</target>
+</phrase>
+<phrase>
+ <source>font</source>
+ <target>teckensnitt</target>
+</phrase>
+<phrase>
+ <source>font size</source>
+ <target>teckenstorlek</target>
+</phrase>
+<phrase>
+ <source>font style</source>
+ <target>teckenstil</target>
+</phrase>
+<phrase>
+ <source>function key</source>
+ <target>funktionstangent</target>
+</phrase>
+<phrase>
+ <source>group box</source>
+ <target>gruppruta</target>
+</phrase>
+<phrase>
+ <source>handle</source>
+ <target>handtag</target>
+</phrase>
+<phrase>
+ <source>Help</source>
+ <target>Hjälp</target>
+</phrase>
+<phrase>
+ <source>Hide</source>
+ <target>Dölj</target>
+</phrase>
+<phrase>
+ <source>hierarchical selection</source>
+ <target>hierarkisk markering</target>
+</phrase>
+<phrase>
+ <source>hold</source>
+ <target>hålla ned</target>
+</phrase>
+<phrase>
+ <source>hot spot</source>
+ <target>aktiv punkt</target>
+</phrase>
+<phrase>
+ <source>hot zone</source>
+ <target>aktiveringszon</target>
+</phrase>
+<phrase>
+ <source>icon</source>
+ <target>ikon</target>
+</phrase>
+<phrase>
+ <source>inactive</source>
+ <target>inaktiv</target>
+</phrase>
+<phrase>
+ <source>inactive window</source>
+ <target>inaktivt fönster</target>
+</phrase>
+<phrase>
+ <source>input focus</source>
+ <target>inmatningsfokus</target>
+</phrase>
+<phrase>
+ <source>Insert</source>
+ <target>Infoga-meny</target>
+</phrase>
+<phrase>
+ <source>Insert Object</source>
+ <target>Infoga objekt</target>
+</phrase>
+<phrase>
+ <source>insertion point</source>
+ <target>insättningspunkt</target>
+</phrase>
+<phrase>
+ <source>italic</source>
+ <target>kursiv</target>
+</phrase>
+<phrase>
+ <source>label</source>
+ <target>titel</target>
+</phrase>
+<phrase>
+ <source>landscape</source>
+ <target>liggande</target>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>länk</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>link</source>
+ <target>länka</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>Link Here</source>
+ <target>Länka hit</target>
+</phrase>
+<phrase>
+ <source>list box</source>
+ <target>listruta</target>
+</phrase>
+<phrase>
+ <source>list view</source>
+ <target>listvy</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>manual link</source>
+ <target>manuell länk</target>
+</phrase>
+<phrase>
+ <source>Maximize</source>
+ <target>Maximera</target>
+</phrase>
+<phrase>
+ <source>maximize button</source>
+ <target>maximeringsknapp</target>
+</phrase>
+<phrase>
+ <source>MDI</source>
+ <target>MDI</target>
+</phrase>
+<phrase>
+ <source>menu</source>
+ <target>meny</target>
+</phrase>
+<phrase>
+ <source>menu bar</source>
+ <target>menyrad</target>
+</phrase>
+<phrase>
+ <source>menu button</source>
+ <target>menyknapp</target>
+</phrase>
+<phrase>
+ <source>menu item</source>
+ <target>menyobjekt</target>
+</phrase>
+<phrase>
+ <source>menu title</source>
+ <target>menytitel</target>
+</phrase>
+<phrase>
+ <source>message box</source>
+ <target>meddelanderuta</target>
+</phrase>
+<phrase>
+ <source>Minimize</source>
+ <target>Minimera</target>
+</phrase>
+<phrase>
+ <source>minimize button</source>
+ <target>minimeringsknapp</target>
+</phrase>
+<phrase>
+ <source>mixed-value</source>
+ <target>blandvärde</target>
+</phrase>
+<phrase>
+ <source>modal</source>
+ <target>modal</target>
+</phrase>
+<phrase>
+ <source>mode</source>
+ <target>läge</target>
+</phrase>
+<phrase>
+ <source>modeless</source>
+ <target>icke-modal</target>
+</phrase>
+<phrase>
+ <source>modifier key</source>
+ <target>ändringstangent</target>
+</phrase>
+<phrase>
+ <source>mouse</source>
+ <target>mus</target>
+</phrase>
+<phrase>
+ <source>Move</source>
+ <target>Flytta</target>
+</phrase>
+<phrase>
+ <source>Move Here</source>
+ <target>Flytta hit</target>
+</phrase>
+<phrase>
+ <source>Multiple Document Interface</source>
+ <target>multiple document interface</target>
+</phrase>
+<phrase>
+ <source>multiple selection list box</source>
+ <target>listruta för multipel markering</target>
+</phrase>
+<phrase>
+ <source>My Computer</source>
+ <target>Den här datorn</target>
+ <definition>icon</definition>
+</phrase>
+<phrase>
+ <source>Network Neighborhood</source>
+ <target>Nätverket</target>
+ <definition>icon</definition>
+</phrase>
+<phrase>
+ <source>New</source>
+ <target>Ny</target>
+</phrase>
+<phrase>
+ <source>New</source>
+ <target>Nytt</target>
+</phrase>
+<phrase>
+ <source>Next</source>
+ <target>Nästa</target>
+</phrase>
+<phrase>
+ <source>object</source>
+ <target>objekt</target>
+</phrase>
+<phrase>
+ <source>OK</source>
+ <target>OK</target>
+</phrase>
+<phrase>
+ <source>OLE</source>
+ <target>OLE</target>
+</phrase>
+<phrase>
+ <source>OLE drag and drop</source>
+ <target>dra och släpp (OLE)</target>
+</phrase>
+<phrase>
+ <source>OLE embedded object</source>
+ <target>inbäddat objekt (OLE)</target>
+</phrase>
+<phrase>
+ <source>OLE linked object</source>
+ <target>länkat objekt (OLE)</target>
+</phrase>
+<phrase>
+ <source>OLE nondefault drag and drop</source>
+ <target>utökat drag och släpp (OLE)</target>
+</phrase>
+<phrase>
+ <source>Open</source>
+ <target>Öppna</target>
+</phrase>
+<phrase>
+ <source>Open With</source>
+ <target>Öppna med</target>
+</phrase>
+<phrase>
+ <source>option button</source>
+ <target>alternativknapp</target>
+</phrase>
+<phrase>
+ <source>option-set</source>
+ <target>valt alternativ</target>
+</phrase>
+<phrase>
+ <source>package</source>
+ <target>paket</target>
+</phrase>
+<phrase>
+ <source>Page Setup</source>
+ <target>Utskriftsformat</target>
+</phrase>
+<phrase>
+ <source>palette window</source>
+ <target>palettfönster</target>
+</phrase>
+<phrase>
+ <source>pane</source>
+ <target>fönsterruta</target>
+</phrase>
+<phrase>
+ <source>parent window</source>
+ <target>moderfönster</target>
+</phrase>
+<phrase>
+ <source>password</source>
+ <target>lösenord</target>
+</phrase>
+<phrase>
+ <source>Paste</source>
+ <target>Klistra in</target>
+</phrase>
+<phrase>
+ <source>Paste Link</source>
+ <target>Klistra in länk</target>
+</phrase>
+<phrase>
+ <source>Paste Shortcut</source>
+ <target>Klistra in genväg</target>
+</phrase>
+<phrase>
+ <source>Paste Special</source>
+ <target>Klistra in special</target>
+</phrase>
+<phrase>
+ <source>path</source>
+ <target>sökväg</target>
+</phrase>
+<phrase>
+ <source>Pause</source>
+ <target>Paus</target>
+</phrase>
+<phrase>
+ <source>Play</source>
+ <target>Spela upp</target>
+</phrase>
+<phrase>
+ <source>Plug and Play</source>
+ <target>Plug and Play</target>
+</phrase>
+<phrase>
+ <source>point</source>
+ <target>peka</target>
+</phrase>
+<phrase>
+ <source>pointer</source>
+ <target>pekare</target>
+</phrase>
+<phrase>
+ <source>pop-up menu</source>
+ <target>pop up-meny</target>
+</phrase>
+<phrase>
+ <source>pop-up window</source>
+ <target>pop up-fönster</target>
+</phrase>
+<phrase>
+ <source>portrait</source>
+ <target>stående</target>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>håll ned</target>
+ <definition>and hold a mouse button</definition>
+</phrase>
+<phrase>
+ <source>press</source>
+ <target>tryck på</target>
+ <definition>a key</definition>
+</phrase>
+<phrase>
+ <source>primary container</source>
+ <target>primär behållare</target>
+</phrase>
+<phrase>
+ <source>primary window</source>
+ <target>primärt fönster</target>
+</phrase>
+<phrase>
+ <source>Print</source>
+ <target>Skriv ut</target>
+</phrase>
+<phrase>
+ <source>printer</source>
+ <target>skrivare</target>
+</phrase>
+<phrase>
+ <source>progress indicator</source>
+ <target>förloppsindikator</target>
+ <definition>control</definition>
+</phrase>
+<phrase>
+ <source>project</source>
+ <target>projekt</target>
+</phrase>
+<phrase>
+ <source>Properties</source>
+ <target>Egenskaper</target>
+</phrase>
+<phrase>
+ <source>property inspector</source>
+ <target>egenskapsgranskare</target>
+</phrase>
+<phrase>
+ <source>property page</source>
+ <target>egenskapssida</target>
+</phrase>
+<phrase>
+ <source>property sheet</source>
+ <target>egenskapsförteckning</target>
+</phrase>
+<phrase>
+ <source>property sheet control</source>
+ <target>kontroll i egenskapsförteckning</target>
+</phrase>
+<phrase>
+ <source>Quick View</source>
+ <target>Snabbgranskning</target>
+</phrase>
+<phrase>
+ <source>read-only</source>
+ <target>skrivskydd</target>
+</phrase>
+<phrase>
+ <source>Recycle Bin</source>
+ <target>Papperskorgen</target>
+ <definition>Icon</definition>
+</phrase>
+<phrase>
+ <source>Redo</source>
+ <target>Gör om</target>
+</phrase>
+<phrase>
+ <source>region selection</source>
+ <target>områdesmarkering</target>
+</phrase>
+<phrase>
+ <source>registry</source>
+ <target>Registret</target>
+</phrase>
+<phrase>
+ <source>Repeat</source>
+ <target>Upprepa</target>
+</phrase>
+<phrase>
+ <source>Replace</source>
+ <target>Ersätt</target>
+</phrase>
+<phrase>
+ <source>Restore</source>
+ <target>Återställ</target>
+</phrase>
+<phrase>
+ <source>Restore button</source>
+ <target>knappen Återställ</target>
+</phrase>
+<phrase>
+ <source>Resume</source>
+ <target>Fortsätt</target>
+</phrase>
+<phrase>
+ <source>Retry</source>
+ <target>Försök igen</target>
+</phrase>
+<phrase>
+ <source>rich-text box</source>
+ <target>rich text-ruta</target>
+</phrase>
+<phrase>
+ <source>Run</source>
+ <target>Kör</target>
+</phrase>
+<phrase>
+ <source>Save</source>
+ <target>Spara</target>
+</phrase>
+<phrase>
+ <source>Save as</source>
+ <target>Spara som</target>
+</phrase>
+<phrase>
+ <source>scroll</source>
+ <target>rulla</target>
+</phrase>
+<phrase>
+ <source>scroll arrow</source>
+ <target>rullningspil</target>
+</phrase>
+<phrase>
+ <source>scroll bar</source>
+ <target>rullningslist</target>
+</phrase>
+<phrase>
+ <source>scroll box</source>
+ <target>rullningsruta</target>
+</phrase>
+<phrase>
+ <source>secondary window</source>
+ <target>sekundärt fönster</target>
+</phrase>
+<phrase>
+ <source>select</source>
+ <target>markera</target>
+</phrase>
+<phrase>
+ <source>Select All</source>
+ <target>Markera allt</target>
+</phrase>
+<phrase>
+ <source>selection</source>
+ <target>markering</target>
+</phrase>
+<phrase>
+ <source>selection handle</source>
+ <target>markeringshandtag</target>
+</phrase>
+<phrase>
+ <source>Send To</source>
+ <target>Skicka till</target>
+</phrase>
+<phrase>
+ <source>separator</source>
+ <target>avgränsare</target>
+</phrase>
+<phrase>
+ <source>Settings</source>
+ <target>Inställningar</target>
+</phrase>
+<phrase>
+ <source>Setup</source>
+ <target>installationsprogram</target>
+</phrase>
+<phrase>
+ <source>Setup</source>
+ <target>Inställningar</target>
+</phrase>
+<phrase>
+ <source>shortcut</source>
+ <target>genväg</target>
+</phrase>
+<phrase>
+ <source>shortcut button</source>
+ <target>genvägsknapp</target>
+</phrase>
+<phrase>
+ <source>shortcut icon</source>
+ <target>genvägsikon</target>
+</phrase>
+<phrase>
+ <source>shortcut key</source>
+ <target>kortkommando</target>
+</phrase>
+<phrase>
+ <source>shortcut key control</source>
+ <target>kortkommandokontroll</target>
+</phrase>
+<phrase>
+ <source>Show</source>
+ <target>Visa</target>
+</phrase>
+<phrase>
+ <source>Shutdown</source>
+ <target>Avsluta</target>
+</phrase>
+<phrase>
+ <source>single selection list box</source>
+ <target>listruta för enkel markering</target>
+</phrase>
+<phrase>
+ <source>Size</source>
+ <target>Ändra storlek</target>
+</phrase>
+<phrase>
+ <source>Size</source>
+ <target>Storlek</target>
+</phrase>
+<phrase>
+ <source>size grip</source>
+ <target>storleksgrepp</target>
+</phrase>
+<phrase>
+ <source>slider</source>
+ <target>skjutreglage</target>
+</phrase>
+<phrase>
+ <source>spin box</source>
+ <target>rotationsruta</target>
+</phrase>
+<phrase>
+ <source>Split</source>
+ <target>Dela</target>
+</phrase>
+<phrase>
+ <source>split bar</source>
+ <target>delningslist</target>
+</phrase>
+<phrase>
+ <source>split box</source>
+ <target>delningsruta</target>
+</phrase>
+<phrase>
+ <source>Start button</source>
+ <target>startknappen</target>
+</phrase>
+<phrase>
+ <source>StartUp folder</source>
+ <target>mappen Autostart</target>
+</phrase>
+<phrase>
+ <source>status bar</source>
+ <target>statusfält</target>
+</phrase>
+<phrase>
+ <source>Stop</source>
+ <target>Stanna</target>
+</phrase>
+<phrase>
+ <source>tab control</source>
+ <target>flik</target>
+</phrase>
+<phrase>
+ <source>task bar</source>
+ <target>Aktivitetsfältet</target>
+</phrase>
+<phrase>
+ <source>task-oriented Help</source>
+ <target>aktivitetsberoende hjälp</target>
+</phrase>
+<phrase>
+ <source>template</source>
+ <target>mall</target>
+</phrase>
+<phrase>
+ <source>text box</source>
+ <target>textruta</target>
+</phrase>
+<phrase>
+ <source>title bar</source>
+ <target>namnlist</target>
+</phrase>
+<phrase>
+ <source>title text</source>
+ <target>text i namnlist</target>
+</phrase>
+<phrase>
+ <source>toggle key</source>
+ <target>växlingstangent</target>
+</phrase>
+<phrase>
+ <source>toolbar</source>
+ <target>verktygsfält</target>
+</phrase>
+<phrase>
+ <source>tooltip</source>
+ <target>funktionsbeskrivning</target>
+</phrase>
+<phrase>
+ <source>tree view control</source>
+ <target>trädkontroll</target>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>typ</target>
+ <definition>noun</definition>
+</phrase>
+<phrase>
+ <source>type</source>
+ <target>skriva</target>
+ <definition>verb</definition>
+</phrase>
+<phrase>
+ <source>unavailable</source>
+ <target>ej tillgänglig</target>
+</phrase>
+<phrase>
+ <source>Undo</source>
+ <target>Ångra</target>
+</phrase>
+<phrase>
+ <source>Uninstall</source>
+ <target>Avinstallera</target>
+</phrase>
+<phrase>
+ <source>View</source>
+ <target>Visa-meny</target>
+</phrase>
+<phrase>
+ <source>visual editing</source>
+ <target>direktredigering</target>
+</phrase>
+<phrase>
+ <source>well control</source>
+ <target>grafikkontroll</target>
+</phrase>
+<phrase>
+ <source>What&apos;s This?</source>
+ <target>Förklaring</target>
+</phrase>
+<phrase>
+ <source>Window</source>
+ <target>Fönster-meny</target>
+</phrase>
+<phrase>
+ <source>window</source>
+ <target>fönster</target>
+</phrase>
+<phrase>
+ <source>Windows Explorer</source>
+ <target>Utforskaren</target>
+</phrase>
+<phrase>
+ <source>wizard</source>
+ <target>guide</target>
+</phrase>
+<phrase>
+ <source>workbook</source>
+ <target>arbetsbok</target>
+</phrase>
+<phrase>
+ <source>workgroup</source>
+ <target>arbetsgrupp</target>
+</phrase>
+<phrase>
+ <source>workspace</source>
+ <target>arbetsyta</target>
+</phrase>
+<phrase>
+ <source>Yes</source>
+ <target>Ja</target>
+</phrase>
+</QPH>
diff --git a/src/linguist/qdoc.conf b/src/linguist/qdoc.conf
new file mode 100644
index 000000000..31d44d5a5
--- /dev/null
+++ b/src/linguist/qdoc.conf
@@ -0,0 +1,15 @@
+SOURCEDIRS = $QTDIR/tools/linguist/doc
+DOCDIRS = $QTDIR/tools/linguist/doc
+EXAMPLEDIRS = $QTDIR/tools/linguist/tutorial
+OUTPUTDIR = $QTDIR/tools/linguist/doc/html
+BASE = file:$QTDIR/tools/linguist/doc/html/
+COMPANY = Nokia Corporation and/or its subsidiary(-ies)
+PRODUCT = Qt Linguist
+VERSIONSYM = QT_VERSION_STR
+DEFINE = QT_QDOC QT_.*_SUPPORT _WS_.*_
+FALSE = 0 1
+INTERNAL = no
+STYLE = "h3.fn,span.fn { margin-left: 1cm; text-indent: -1cm; }
+a:link { color: #af4f00; text-decoration: none }
+a:visited { color: #8f2f00; text-decoration: none }
+body { background: #ffffff; color: black; }"
diff --git a/src/linguist/shared/abstractproitemvisitor.h b/src/linguist/shared/abstractproitemvisitor.h
new file mode 100644
index 000000000..259aba788
--- /dev/null
+++ b/src/linguist/shared/abstractproitemvisitor.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 ABSTRACTPROITEMVISITOR
+#define ABSTRACTPROITEMVISITOR
+
+#include "proitems.h"
+
+QT_BEGIN_NAMESPACE
+
+struct AbstractProItemVisitor
+{
+ virtual ~AbstractProItemVisitor() {}
+
+ virtual ProItem::ProItemReturn visitBeginProBlock(ProBlock *block) = 0;
+ virtual void visitEndProBlock(ProBlock *block) = 0;
+
+ virtual ProItem::ProItemReturn visitProLoopIteration() = 0;
+ virtual void visitProLoopCleanup() = 0;
+
+ virtual void visitBeginProVariable(ProVariable *variable) = 0;
+ virtual void visitEndProVariable(ProVariable *variable) = 0;
+
+ virtual ProItem::ProItemReturn visitBeginProFile(ProFile *value) = 0;
+ virtual ProItem::ProItemReturn visitEndProFile(ProFile *value) = 0;
+
+ virtual void visitProValue(ProValue *value) = 0;
+ virtual ProItem::ProItemReturn visitProFunction(ProFunction *function) = 0;
+ virtual void visitProOperator(ProOperator *function) = 0;
+ virtual void visitProCondition(ProCondition *function) = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // ABSTRACTPROITEMVISITOR
+
diff --git a/src/linguist/shared/formats.pri b/src/linguist/shared/formats.pri
new file mode 100644
index 000000000..985f6dbb8
--- /dev/null
+++ b/src/linguist/shared/formats.pri
@@ -0,0 +1,22 @@
+
+# infrastructure
+QT *= xml
+
+INCLUDEPATH *= $$PWD
+
+SOURCES += \
+ $$PWD/numerus.cpp \
+ $$PWD/translator.cpp \
+ $$PWD/translatormessage.cpp
+
+HEADERS += \
+ $$PWD/translator.h \
+ $$PWD/translatormessage.h
+
+# "real" formats readers and writers
+SOURCES += \
+ $$PWD/qm.cpp \
+ $$PWD/qph.cpp \
+ $$PWD/po.cpp \
+ $$PWD/ts.cpp \
+ $$PWD/xliff.cpp
diff --git a/src/linguist/shared/numerus.cpp b/src/linguist/shared/numerus.cpp
new file mode 100644
index 000000000..8fe263baf
--- /dev/null
+++ b/src/linguist/shared/numerus.cpp
@@ -0,0 +1,404 @@
+/****************************************************************************
+**
+** 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 "translator.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QMap>
+
+#include <private/qtranslator_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static const uchar englishStyleRules[] =
+ { Q_EQ, 1 };
+static const uchar frenchStyleRules[] =
+ { Q_LEQ, 1 };
+static const uchar latvianRules[] =
+ { Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11, Q_NEWRULE,
+ Q_NEQ, 0 };
+static const uchar icelandicRules[] =
+ { Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11 };
+static const uchar irishStyleRules[] =
+ { Q_EQ, 1, Q_NEWRULE,
+ Q_EQ, 2 };
+static const uchar slovakStyleRules[] =
+ { Q_EQ, 1, Q_NEWRULE,
+ Q_BETWEEN, 2, 4 };
+static const uchar macedonianRules[] =
+ { Q_MOD_10 | Q_EQ, 1, Q_NEWRULE,
+ Q_MOD_10 | Q_EQ, 2 };
+static const uchar lithuanianRules[] =
+ { Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11, Q_NEWRULE,
+ Q_MOD_10 | Q_NEQ, 0, Q_AND, Q_MOD_100 | Q_NOT_BETWEEN, 10, 19 };
+static const uchar russianStyleRules[] =
+ { Q_MOD_10 | Q_EQ, 1, Q_AND, Q_MOD_100 | Q_NEQ, 11, Q_NEWRULE,
+ Q_MOD_10 | Q_BETWEEN, 2, 4, Q_AND, Q_MOD_100 | Q_NOT_BETWEEN, 10, 19 };
+static const uchar polishRules[] =
+ { Q_EQ, 1, Q_NEWRULE,
+ Q_MOD_10 | Q_BETWEEN, 2, 4, Q_AND, Q_MOD_100 | Q_NOT_BETWEEN, 10, 19 };
+static const uchar romanianRules[] =
+ { Q_EQ, 1, Q_NEWRULE,
+ Q_EQ, 0, Q_OR, Q_MOD_100 | Q_BETWEEN, 1, 19 };
+static const uchar slovenianRules[] =
+ { Q_MOD_100 | Q_EQ, 1, Q_NEWRULE,
+ Q_MOD_100 | Q_EQ, 2, Q_NEWRULE,
+ Q_MOD_100 | Q_BETWEEN, 3, 4 };
+static const uchar malteseRules[] =
+ { Q_EQ, 1, Q_NEWRULE,
+ Q_EQ, 0, Q_OR, Q_MOD_100 | Q_BETWEEN, 1, 10, Q_NEWRULE,
+ Q_MOD_100 | Q_BETWEEN, 11, 19 };
+static const uchar welshRules[] =
+ { Q_EQ, 0, Q_NEWRULE,
+ Q_EQ, 1, Q_NEWRULE,
+ Q_BETWEEN, 2, 5, Q_NEWRULE,
+ Q_EQ, 6 };
+static const uchar arabicRules[] =
+ { Q_EQ, 0, Q_NEWRULE,
+ Q_EQ, 1, Q_NEWRULE,
+ Q_EQ, 2, Q_NEWRULE,
+ Q_MOD_100 | Q_BETWEEN, 3, 10, Q_NEWRULE,
+ Q_MOD_100 | Q_GEQ, 11 };
+static const uchar tagalogRules[] =
+ { Q_LEQ, 1, Q_NEWRULE,
+ Q_MOD_10 | Q_EQ, 4, Q_OR, Q_MOD_10 | Q_EQ, 6, Q_OR, Q_MOD_10 | Q_EQ, 9 };
+static const uchar catalanRules[] =
+ { Q_EQ, 1, Q_NEWRULE,
+ Q_LEAD_1000 | Q_EQ, 11 };
+
+static const char * const japaneseStyleForms[] = { "Universal Form", 0 };
+static const char * const englishStyleForms[] = { "Singular", "Plural", 0 };
+static const char * const frenchStyleForms[] = { "Singular", "Plural", 0 };
+static const char * const icelandicForms[] = { "Singular", "Plural", 0 };
+static const char * const latvianForms[] = { "Singular", "Plural", "Nullar", 0 };
+static const char * const irishStyleForms[] = { "Singular", "Dual", "Plural", 0 };
+static const char * const slovakStyleForms[] = { "Singular", "Paucal", "Plural", 0 };
+static const char * const macedonianForms[] = { "Singular", "Dual", "Plural", 0 };
+static const char * const lithuanianForms[] = { "Singular", "Paucal", "Plural", 0 };
+static const char * const russianStyleForms[] = { "Singular", "Dual", "Plural", 0 };
+static const char * const polishForms[] = { "Singular", "Paucal", "Plural", 0 };
+static const char * const romanianForms[] = { "Singular", "Paucal", "Plural", 0 };
+static const char * const slovenianForms[] = { "Singular", "Dual", "Trial", "Plural", 0 };
+static const char * const malteseForms[] =
+ { "Singular", "Paucal", "Greater Paucal", "Plural", 0 };
+static const char * const welshForms[] =
+ { "Nullar", "Singular", "Dual", "Sexal", "Plural", 0 };
+static const char * const arabicForms[] =
+ { "Nullar", "Singular", "Dual", "Minority Plural", "Plural", "Plural (100-102, ...)", 0 };
+static const char * const tagalogForms[] =
+ { "Singular", "Plural (consonant-ended)", "Plural (vowel-ended)", 0 };
+static const char * const catalanForms[] = { "Singular", "Undecal (11)", "Plural", 0 };
+
+#define EOL QLocale::C
+
+static const QLocale::Language japaneseStyleLanguages[] = {
+ QLocale::Afan,
+ QLocale::Armenian,
+ QLocale::Bhutani,
+ QLocale::Bislama,
+ QLocale::Burmese,
+ QLocale::Chinese,
+ QLocale::FijiLanguage,
+ QLocale::Guarani,
+ QLocale::Hungarian,
+ QLocale::Indonesian,
+ QLocale::Japanese,
+ QLocale::Javanese,
+ QLocale::Korean,
+ QLocale::Malay,
+ QLocale::NauruLanguage,
+ QLocale::Persian,
+ QLocale::Sundanese,
+ QLocale::Thai,
+ QLocale::Tibetan,
+ QLocale::Turkish,
+ QLocale::Vietnamese,
+ QLocale::Yoruba,
+ QLocale::Zhuang,
+ EOL
+};
+
+static const QLocale::Language englishStyleLanguages[] = {
+ QLocale::Abkhazian,
+ QLocale::Afar,
+ QLocale::Afrikaans,
+ QLocale::Albanian,
+ QLocale::Amharic,
+ QLocale::Assamese,
+ QLocale::Aymara,
+ QLocale::Azerbaijani,
+ QLocale::Bashkir,
+ QLocale::Basque,
+ QLocale::Bengali,
+ QLocale::Bihari,
+ // Missing: Bokmal,
+ QLocale::Bulgarian,
+ QLocale::Cambodian,
+ QLocale::Cornish,
+ QLocale::Corsican,
+ QLocale::Danish,
+ QLocale::Dutch,
+ QLocale::English,
+ QLocale::Esperanto,
+ QLocale::Estonian,
+ QLocale::Faroese,
+ QLocale::Finnish,
+ // Missing: Friulian,
+ QLocale::Frisian,
+ QLocale::Galician,
+ QLocale::Georgian,
+ QLocale::German,
+ QLocale::Greek,
+ QLocale::Greenlandic,
+ QLocale::Gujarati,
+ QLocale::Hausa,
+ QLocale::Hebrew,
+ QLocale::Hindi,
+ QLocale::Interlingua,
+ QLocale::Interlingue,
+ QLocale::Italian,
+ QLocale::Kannada,
+ QLocale::Kashmiri,
+ QLocale::Kazakh,
+ QLocale::Kinyarwanda,
+ QLocale::Kirghiz,
+ QLocale::Kurdish,
+ QLocale::Kurundi,
+ QLocale::Laothian,
+ QLocale::Latin,
+ // Missing: Letzeburgesch,
+ QLocale::Lingala,
+ QLocale::Malagasy,
+ QLocale::Malayalam,
+ QLocale::Marathi,
+ QLocale::Mongolian,
+ // Missing: Nahuatl,
+ QLocale::Nepali,
+ // Missing: Northern Sotho,
+ QLocale::Norwegian,
+ QLocale::Nynorsk,
+ QLocale::Occitan,
+ QLocale::Oriya,
+ QLocale::Pashto,
+ QLocale::Portuguese,
+ QLocale::Punjabi,
+ QLocale::Quechua,
+ QLocale::RhaetoRomance,
+ QLocale::Sesotho,
+ QLocale::Setswana,
+ QLocale::Shona,
+ QLocale::Sindhi,
+ QLocale::Singhalese,
+ QLocale::Siswati,
+ QLocale::Somali,
+ QLocale::Spanish,
+ QLocale::Swahili,
+ QLocale::Swedish,
+ QLocale::Tajik,
+ QLocale::Tamil,
+ QLocale::Tatar,
+ QLocale::Telugu,
+ QLocale::TongaLanguage,
+ QLocale::Tsonga,
+ QLocale::Turkmen,
+ QLocale::Twi,
+ QLocale::Uigur,
+ QLocale::Urdu,
+ QLocale::Uzbek,
+ QLocale::Volapuk,
+ QLocale::Wolof,
+ QLocale::Xhosa,
+ QLocale::Yiddish,
+ QLocale::Zulu,
+ EOL
+};
+static const QLocale::Language frenchStyleLanguages[] = {
+ // keep synchronized with frenchStyleCountries
+ QLocale::Breton,
+ QLocale::French,
+ QLocale::Portuguese,
+ // Missing: Filipino,
+ QLocale::Tigrinya,
+ // Missing: Walloon
+ EOL
+};
+static const QLocale::Language latvianLanguage[] = { QLocale::Latvian, EOL };
+static const QLocale::Language icelandicLanguage[] = { QLocale::Icelandic, EOL };
+static const QLocale::Language irishStyleLanguages[] = {
+ QLocale::Divehi,
+ QLocale::Gaelic,
+ QLocale::Inuktitut,
+ QLocale::Inupiak,
+ QLocale::Irish,
+ QLocale::Manx,
+ QLocale::Maori,
+ // Missing: Sami,
+ QLocale::Samoan,
+ QLocale::Sanskrit,
+ EOL
+};
+static const QLocale::Language slovakStyleLanguages[] = { QLocale::Slovak, QLocale::Czech, EOL };
+static const QLocale::Language macedonianLanguage[] = { QLocale::Macedonian, EOL };
+static const QLocale::Language lithuanianLanguage[] = { QLocale::Lithuanian, EOL };
+static const QLocale::Language russianStyleLanguages[] = {
+ QLocale::Bosnian,
+ QLocale::Byelorussian,
+ QLocale::Croatian,
+ QLocale::Russian,
+ QLocale::Serbian,
+ QLocale::SerboCroatian,
+ QLocale::Ukrainian,
+ EOL
+};
+static const QLocale::Language polishLanguage[] = { QLocale::Polish, EOL };
+static const QLocale::Language romanianLanguages[] = {
+ QLocale::Moldavian,
+ QLocale::Romanian,
+ EOL
+};
+static const QLocale::Language slovenianLanguage[] = { QLocale::Slovenian, EOL };
+static const QLocale::Language malteseLanguage[] = { QLocale::Maltese, EOL };
+static const QLocale::Language welshLanguage[] = { QLocale::Welsh, EOL };
+static const QLocale::Language arabicLanguage[] = { QLocale::Arabic, EOL };
+static const QLocale::Language tagalogLanguage[] = { QLocale::Tagalog, EOL };
+static const QLocale::Language catalanLanguage[] = { QLocale::Catalan, EOL };
+
+static const QLocale::Country frenchStyleCountries[] = {
+ // keep synchronized with frenchStyleLanguages
+ QLocale::AnyCountry,
+ QLocale::AnyCountry,
+ QLocale::Brazil,
+ QLocale::AnyCountry
+};
+struct NumerusTableEntry {
+ const uchar *rules;
+ int rulesSize;
+ const char * const *forms;
+ const QLocale::Language *languages;
+ const QLocale::Country *countries;
+ const char * const gettextRules;
+};
+
+static const NumerusTableEntry numerusTable[] = {
+ { 0, 0, japaneseStyleForms, japaneseStyleLanguages, 0, "nplurals=1; plural=0;" },
+ { englishStyleRules, sizeof(englishStyleRules), englishStyleForms, englishStyleLanguages, 0,
+ "nplurals=2; plural=(n != 1);" },
+ { frenchStyleRules, sizeof(frenchStyleRules), frenchStyleForms, frenchStyleLanguages,
+ frenchStyleCountries, "nplurals=2; plural=(n > 1);" },
+ { latvianRules, sizeof(latvianRules), latvianForms, latvianLanguage, 0,
+ "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);" },
+ { icelandicRules, sizeof(icelandicRules), icelandicForms, icelandicLanguage, 0,
+ "nplurals=2; plural=(n%10==1 && n%100!=11 ? 0 : 1);" },
+ { irishStyleRules, sizeof(irishStyleRules), irishStyleForms, irishStyleLanguages, 0,
+ "nplurals=3; plural=(n==1 ? 0 : n==2 ? 1 : 2);" },
+ { slovakStyleRules, sizeof(slovakStyleRules), slovakStyleForms, slovakStyleLanguages, 0,
+ "nplurals=3; plural=((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);" },
+ { macedonianRules, sizeof(macedonianRules), macedonianForms, macedonianLanguage, 0,
+ "nplurals=3; plural=(n%100==1 ? 0 : n%100==2 ? 1 : 2);" },
+ { lithuanianRules, sizeof(lithuanianRules), lithuanianForms, lithuanianLanguage, 0,
+ "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+ { russianStyleRules, sizeof(russianStyleRules), russianStyleForms, russianStyleLanguages, 0,
+ "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+ { polishRules, sizeof(polishRules), polishForms, polishLanguage, 0,
+ "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+ { romanianRules, sizeof(romanianRules), romanianForms, romanianLanguages, 0,
+ "nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2);" },
+ { slovenianRules, sizeof(slovenianRules), slovenianForms, slovenianLanguage, 0,
+ "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);" },
+ { malteseRules, sizeof(malteseRules), malteseForms, malteseLanguage, 0,
+ "nplurals=4; plural=(n==1 ? 0 : (n==0 || (n%100>=1 && n%100<=10)) ? 1 : (n%100>=11 && n%100<=19) ? 2 : 3);" },
+ { welshRules, sizeof(welshRules), welshForms, welshLanguage, 0,
+ "nplurals=5; plural=(n==0 ? 0 : n==1 ? 1 : (n>=2 && n<=5) ? 2 : n==6 ? 3 : 4);" },
+ { arabicRules, sizeof(arabicRules), arabicForms, arabicLanguage, 0,
+ "nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : (n%100>=3 && n%100<=10) ? 3 : n%100>=11 ? 4 : 5);" },
+ { tagalogRules, sizeof(tagalogRules), tagalogForms, tagalogLanguage, 0,
+ "nplurals=3; plural=(n==1 ? 0 : (n%10==4 || n%10==6 || n%10== 9) ? 1 : 2);" },
+ { catalanRules, sizeof(catalanRules), catalanForms, catalanLanguage, 0,
+ "nplurals=3; plural=(n==1 ? 0 : (n==11 || n/1000==11 || n/1000000==11 || n/1000000000==11) ? 1 : 2);" },
+};
+
+static const int NumerusTableSize = sizeof(numerusTable) / sizeof(numerusTable[0]);
+
+// magic number for the file
+static const int MagicLength = 16;
+static const uchar magic[MagicLength] = {
+ 0x3c, 0xb8, 0x64, 0x18, 0xca, 0xef, 0x9c, 0x95,
+ 0xcd, 0x21, 0x1c, 0xbf, 0x60, 0xa1, 0xbd, 0xdd
+};
+
+bool getNumerusInfo(QLocale::Language language, QLocale::Country country,
+ QByteArray *rules, QStringList *forms, const char **gettextRules)
+{
+ while (true) {
+ for (int i = 0; i < NumerusTableSize; ++i) {
+ const NumerusTableEntry &entry = numerusTable[i];
+ for (int j = 0; entry.languages[j] != EOL; ++j) {
+ if (entry.languages[j] == language
+ && ((!entry.countries && country == QLocale::AnyCountry)
+ || (entry.countries && entry.countries[j] == country))) {
+ if (rules) {
+ *rules = QByteArray::fromRawData(reinterpret_cast<const char *>(entry.rules),
+ entry.rulesSize);
+ }
+ if (gettextRules)
+ *gettextRules = entry.gettextRules;
+ if (forms) {
+ forms->clear();
+ for (int k = 0; entry.forms[k]; ++k)
+ forms->append(QLatin1String(entry.forms[k]));
+ }
+ return true;
+ }
+ }
+ }
+
+ if (country == QLocale::AnyCountry)
+ break;
+ country = QLocale::AnyCountry;
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/shared/po.cpp b/src/linguist/shared/po.cpp
new file mode 100644
index 000000000..c8127b529
--- /dev/null
+++ b/src/linguist/shared/po.cpp
@@ -0,0 +1,902 @@
+/****************************************************************************
+**
+** 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 "translator.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QIODevice>
+#include <QtCore/QHash>
+#include <QtCore/QString>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextStream>
+
+#include <ctype.h>
+
+// Uncomment if you wish to hard wrap long lines in .po files. Note that this
+// affects only msg strings, not comments.
+//#define HARD_WRAP_LONG_WORDS
+
+QT_BEGIN_NAMESPACE
+
+static const int MAX_LEN = 79;
+
+static QString poEscapedString(const QString &prefix, const QString &keyword,
+ bool noWrap, const QString &ba)
+{
+ QStringList lines;
+ int off = 0;
+ QString res;
+ while (off < ba.length()) {
+ ushort c = ba[off++].unicode();
+ switch (c) {
+ case '\n':
+ res += QLatin1String("\\n");
+ lines.append(res);
+ res.clear();
+ break;
+ case '\r':
+ res += QLatin1String("\\r");
+ break;
+ case '\t':
+ res += QLatin1String("\\t");
+ break;
+ case '\v':
+ res += QLatin1String("\\v");
+ break;
+ case '\a':
+ res += QLatin1String("\\a");
+ break;
+ case '\b':
+ res += QLatin1String("\\b");
+ break;
+ case '\f':
+ res += QLatin1String("\\f");
+ break;
+ case '"':
+ res += QLatin1String("\\\"");
+ break;
+ case '\\':
+ res += QLatin1String("\\\\");
+ break;
+ default:
+ if (c < 32) {
+ res += QLatin1String("\\x");
+ res += QString::number(c, 16);
+ if (off < ba.length() && isxdigit(ba[off].unicode()))
+ res += QLatin1String("\"\"");
+ } else {
+ res += QChar(c);
+ }
+ break;
+ }
+ }
+ if (!res.isEmpty())
+ lines.append(res);
+ if (!lines.isEmpty()) {
+ if (!noWrap) {
+ if (lines.count() != 1 ||
+ lines.first().length() > MAX_LEN - keyword.length() - prefix.length() - 3)
+ {
+ QStringList olines = lines;
+ lines = QStringList(QString());
+ const int maxlen = MAX_LEN - prefix.length() - 2;
+ foreach (const QString &line, olines) {
+ int off = 0;
+ while (off + maxlen < line.length()) {
+ int idx = line.lastIndexOf(QLatin1Char(' '), off + maxlen - 1) + 1;
+ if (idx == off) {
+#ifdef HARD_WRAP_LONG_WORDS
+ // This doesn't seem too nice, but who knows ...
+ idx = off + maxlen;
+#else
+ idx = line.indexOf(QLatin1Char(' '), off + maxlen) + 1;
+ if (!idx)
+ break;
+#endif
+ }
+ lines.append(line.mid(off, idx - off));
+ off = idx;
+ }
+ lines.append(line.mid(off));
+ }
+ }
+ } else if (lines.count() > 1) {
+ lines.prepend(QString());
+ }
+ }
+ return prefix + keyword + QLatin1String(" \"") +
+ lines.join(QLatin1String("\"\n") + prefix + QLatin1Char('"')) +
+ QLatin1String("\"\n");
+}
+
+static QString poEscapedLines(const QString &prefix, bool addSpace, const QStringList &lines)
+{
+ QString out;
+ foreach (const QString &line, lines) {
+ out += prefix;
+ if (addSpace && !line.isEmpty())
+ out += QLatin1Char(' ' );
+ out += line;
+ out += QLatin1Char('\n');
+ }
+ return out;
+}
+
+static QString poEscapedLines(const QString &prefix, bool addSpace, const QString &in0)
+{
+ QString in = in0;
+ if (in.endsWith(QLatin1Char('\n')))
+ in.chop(1);
+ return poEscapedLines(prefix, addSpace, in.split(QLatin1Char('\n')));
+}
+
+static QString poWrappedEscapedLines(const QString &prefix, bool addSpace, const QString &line)
+{
+ const int maxlen = MAX_LEN - prefix.length();
+ QStringList lines;
+ int off = 0;
+ while (off + maxlen < line.length()) {
+ int idx = line.lastIndexOf(QLatin1Char(' '), off + maxlen - 1);
+ if (idx < off) {
+#if 0 //def HARD_WRAP_LONG_WORDS
+ // This cannot work without messing up semantics, so do not even try.
+#else
+ idx = line.indexOf(QLatin1Char(' '), off + maxlen);
+ if (idx < 0)
+ break;
+#endif
+ }
+ lines.append(line.mid(off, idx - off));
+ off = idx + 1;
+ }
+ lines.append(line.mid(off));
+ return poEscapedLines(prefix, addSpace, lines);
+}
+
+struct PoItem
+{
+public:
+ PoItem()
+ : isPlural(false), isFuzzy(false)
+ {}
+
+
+public:
+ QByteArray id;
+ QByteArray context;
+ QByteArray tscomment;
+ QByteArray oldTscomment;
+ QByteArray lineNumber;
+ QByteArray fileName;
+ QByteArray references;
+ QByteArray translatorComments;
+ QByteArray automaticComments;
+ QByteArray msgId;
+ QByteArray oldMsgId;
+ QList<QByteArray> msgStr;
+ bool isPlural;
+ bool isFuzzy;
+ QHash<QString, QString> extra;
+};
+
+
+static bool isTranslationLine(const QByteArray &line)
+{
+ return line.startsWith("#~ msgstr") || line.startsWith("msgstr");
+}
+
+static QByteArray slurpEscapedString(const QList<QByteArray> &lines, int &l,
+ int offset, const QByteArray &prefix, ConversionData &cd)
+{
+ QByteArray msg;
+ int stoff;
+
+ for (; l < lines.size(); ++l) {
+ const QByteArray &line = lines.at(l);
+ if (line.isEmpty() || !line.startsWith(prefix))
+ break;
+ while (isspace(line[offset])) // No length check, as string has no trailing spaces.
+ offset++;
+ if (line[offset] != '"')
+ break;
+ offset++;
+ forever {
+ if (offset == line.length())
+ goto premature_eol;
+ uchar c = line[offset++];
+ if (c == '"') {
+ if (offset == line.length())
+ break;
+ while (isspace(line[offset]))
+ offset++;
+ if (line[offset++] != '"') {
+ cd.appendError(QString::fromLatin1(
+ "PO parsing error: extra characters on line %1.")
+ .arg(l + 1));
+ break;
+ }
+ continue;
+ }
+ if (c == '\\') {
+ if (offset == line.length())
+ goto premature_eol;
+ c = line[offset++];
+ switch (c) {
+ case 'r':
+ msg += '\r'; // Maybe just throw it away?
+ break;
+ case 'n':
+ msg += '\n';
+ break;
+ case 't':
+ msg += '\t';
+ break;
+ case 'v':
+ msg += '\v';
+ break;
+ case 'a':
+ msg += '\a';
+ break;
+ case 'b':
+ msg += '\b';
+ break;
+ case 'f':
+ msg += '\f';
+ break;
+ case '"':
+ msg += '"';
+ break;
+ case '\\':
+ msg += '\\';
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ stoff = offset - 1;
+ while ((c = line[offset]) >= '0' && c <= '7')
+ if (++offset == line.length())
+ goto premature_eol;
+ msg += line.mid(stoff, offset - stoff).toUInt(0, 8);
+ break;
+ case 'x':
+ stoff = offset;
+ while (isxdigit(line[offset]))
+ if (++offset == line.length())
+ goto premature_eol;
+ msg += line.mid(stoff, offset - stoff).toUInt(0, 16);
+ break;
+ default:
+ cd.appendError(QString::fromLatin1(
+ "PO parsing error: invalid escape '\\%1' (line %2).")
+ .arg(QChar((uint)c)).arg(l + 1));
+ msg += '\\';
+ msg += c;
+ break;
+ }
+ } else {
+ msg += c;
+ }
+ }
+ offset = prefix.size();
+ }
+ --l;
+ return msg;
+
+premature_eol:
+ cd.appendError(QString::fromLatin1(
+ "PO parsing error: premature end of line %1.").arg(l + 1));
+ return QByteArray();
+}
+
+static void slurpComment(QByteArray &msg, const QList<QByteArray> &lines, int & l)
+{
+ QByteArray prefix = lines.at(l);
+ for (int i = 1; ; i++) {
+ if (prefix.at(i) != ' ') {
+ prefix.truncate(i);
+ break;
+ }
+ }
+ for (; l < lines.size(); ++l) {
+ const QByteArray &line = lines.at(l);
+ if (line.startsWith(prefix))
+ msg += line.mid(prefix.size());
+ else if (line != "#")
+ break;
+ msg += '\n';
+ }
+ --l;
+}
+
+static void splitContext(QByteArray *comment, QByteArray *context)
+{
+ char *data = comment->data();
+ int len = comment->size();
+ int sep = -1, j = 0;
+
+ for (int i = 0; i < len; i++, j++) {
+ if (data[i] == '~' && i + 1 < len)
+ i++;
+ else if (data[i] == '|')
+ sep = j;
+ data[j] = data[i];
+ }
+ if (sep >= 0) {
+ QByteArray tmp = comment->mid(sep + 1, j - sep - 1);
+ comment->truncate(sep);
+ *context = *comment;
+ *comment = tmp;
+ } else {
+ comment->truncate(j);
+ }
+}
+
+static QString makePoHeader(const QString &str)
+{
+ return QLatin1String("po-header-") + str.toLower().replace(QLatin1Char('-'), QLatin1Char('_'));
+}
+
+static QByteArray QByteArrayList_join(const QList<QByteArray> &that, char sep)
+{
+ int totalLength = 0;
+ const int size = that.size();
+
+ for (int i = 0; i < size; ++i)
+ totalLength += that.at(i).size();
+
+ if (size > 0)
+ totalLength += size - 1;
+
+ QByteArray res;
+ if (totalLength == 0)
+ return res;
+ res.reserve(totalLength);
+ for (int i = 0; i < that.size(); ++i) {
+ if (i)
+ res += sep;
+ res += that.at(i);
+ }
+ return res;
+}
+
+bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ QTextCodec *codec = QTextCodec::codecForName(
+ cd.m_codecForSource.isEmpty() ? QByteArray("UTF-8") : cd.m_codecForSource);
+ bool error = false;
+
+ // format of a .po file entry:
+ // white-space
+ // # translator-comments
+ // #. automatic-comments
+ // #: reference...
+ // #, flag...
+ // #~ msgctxt, msgid*, msgstr - used for obsoleted messages
+ // #| msgctxt, msgid* previous untranslated-string - for fuzzy message
+ // msgctx string-context
+ // msgid untranslated-string
+ // -- For singular:
+ // msgstr translated-string
+ // -- For plural:
+ // msgid_plural untranslated-string-plural
+ // msgstr[0] translated-string
+ // ...
+
+ // we need line based lookahead below.
+ QList<QByteArray> lines;
+ while (!dev.atEnd())
+ lines.append(dev.readLine().trimmed());
+ lines.append(QByteArray());
+
+ int l = 0, lastCmtLine = -1;
+ bool qtContexts = false;
+ PoItem item;
+ for (; l != lines.size(); ++l) {
+ QByteArray line = lines.at(l);
+ if (line.isEmpty())
+ continue;
+ if (isTranslationLine(line)) {
+ bool isObsolete = line.startsWith("#~ msgstr");
+ const QByteArray prefix = isObsolete ? "#~ " : "";
+ while (true) {
+ int idx = line.indexOf(' ', prefix.length());
+ QByteArray str = slurpEscapedString(lines, l, idx, prefix, cd);
+ item.msgStr.append(str);
+ if (l + 1 >= lines.size() || !isTranslationLine(lines.at(l + 1)))
+ break;
+ ++l;
+ line = lines.at(l);
+ }
+ if (item.msgId.isEmpty()) {
+ QHash<QString, QByteArray> extras;
+ QList<QByteArray> hdrOrder;
+ QByteArray pluralForms;
+ foreach (const QByteArray &hdr, item.msgStr.first().split('\n')) {
+ if (hdr.isEmpty())
+ continue;
+ int idx = hdr.indexOf(':');
+ if (idx < 0) {
+ cd.appendError(QString::fromLatin1("Unexpected PO header format '%1'")
+ .arg(QString::fromLatin1(hdr)));
+ error = true;
+ break;
+ }
+ QByteArray hdrName = hdr.left(idx).trimmed();
+ QByteArray hdrValue = hdr.mid(idx + 1).trimmed();
+ hdrOrder << hdrName;
+ if (hdrName == "X-Language") {
+ translator.setLanguageCode(QString::fromLatin1(hdrValue));
+ } else if (hdrName == "X-Source-Language") {
+ translator.setSourceLanguageCode(QString::fromLatin1(hdrValue));
+ } else if (hdrName == "X-Qt-Contexts") {
+ qtContexts = (hdrValue == "true");
+ } else if (hdrName == "Plural-Forms") {
+ pluralForms = hdrValue;
+ } else if (hdrName == "MIME-Version") {
+ // just assume it is 1.0
+ } else if (hdrName == "Content-Type") {
+ if (cd.m_codecForSource.isEmpty()) {
+ if (!hdrValue.startsWith("text/plain; charset=")) {
+ cd.appendError(QString::fromLatin1("Unexpected Content-Type header '%1'")
+ .arg(QString::fromLatin1(hdrValue)));
+ error = true;
+ // This will avoid a flood of conversion errors.
+ codec = QTextCodec::codecForName("latin1");
+ } else {
+ QByteArray cod = hdrValue.mid(20);
+ QTextCodec *cdc = QTextCodec::codecForName(cod);
+ if (!cdc) {
+ cd.appendError(QString::fromLatin1("Unsupported codec '%1'")
+ .arg(QString::fromLatin1(cod)));
+ error = true;
+ // This will avoid a flood of conversion errors.
+ codec = QTextCodec::codecForName("latin1");
+ } else {
+ codec = cdc;
+ }
+ }
+ }
+ } else if (hdrName == "Content-Transfer-Encoding") {
+ if (hdrValue != "8bit") {
+ cd.appendError(QString::fromLatin1("Unexpected Content-Transfer-Encoding '%1'")
+ .arg(QString::fromLatin1(hdrValue)));
+ return false;
+ }
+ } else if (hdrName == "X-Virgin-Header") {
+ // legacy
+ } else {
+ extras[makePoHeader(QString::fromLatin1(hdrName))] = hdrValue;
+ }
+ }
+ if (!pluralForms.isEmpty()) {
+ if (translator.languageCode().isEmpty()) {
+ extras[makePoHeader(QLatin1String("Plural-Forms"))] = pluralForms;
+ } else {
+ // FIXME: have fun with making a consistency check ...
+ }
+ }
+ // Eliminate the field if only headers we added are present in standard order.
+ // Keep in sync with savePO
+ static const char * const dfltHdrs[] = {
+ "MIME-Version", "Content-Type", "Content-Transfer-Encoding",
+ "Plural-Forms", "X-Language", "X-Source-Language", "X-Qt-Contexts"
+ };
+ uint cdh = 0;
+ for (int cho = 0; cho < hdrOrder.length(); cho++) {
+ for (;; cdh++) {
+ if (cdh == sizeof(dfltHdrs)/sizeof(dfltHdrs[0])) {
+ extras[QLatin1String("po-headers")] =
+ QByteArrayList_join(hdrOrder, ',');
+ goto doneho;
+ }
+ if (hdrOrder.at(cho) == dfltHdrs[cdh]) {
+ cdh++;
+ break;
+ }
+ }
+ }
+ doneho:
+ if (lastCmtLine != -1)
+ extras[QLatin1String("po-header_comment")] =
+ QByteArrayList_join(lines.mid(0, lastCmtLine + 1), '\n');
+ for (QHash<QString, QByteArray>::ConstIterator it = extras.constBegin(),
+ end = extras.constEnd();
+ it != end; ++it)
+ translator.setExtra(it.key(), codec->toUnicode(it.value()));
+ item = PoItem();
+ continue;
+ }
+ // build translator message
+ TranslatorMessage msg;
+ msg.setContext(codec->toUnicode(item.context));
+ if (!item.references.isEmpty()) {
+ QString xrefs;
+ foreach (const QString &ref,
+ codec->toUnicode(item.references).split(
+ QRegExp(QLatin1String("\\s")), QString::SkipEmptyParts)) {
+ int pos = ref.indexOf(QLatin1Char(':'));
+ int lpos = ref.lastIndexOf(QLatin1Char(':'));
+ if (pos != -1 && pos == lpos) {
+ bool ok;
+ int lno = ref.mid(pos + 1).toInt(&ok);
+ if (ok) {
+ msg.addReference(ref.left(pos), lno);
+ continue;
+ }
+ }
+ if (!xrefs.isEmpty())
+ xrefs += QLatin1Char(' ');
+ xrefs += ref;
+ }
+ if (!xrefs.isEmpty())
+ item.extra[QLatin1String("po-references")] = xrefs;
+ }
+ msg.setId(codec->toUnicode(item.id));
+ msg.setSourceText(codec->toUnicode(item.msgId));
+ msg.setOldSourceText(codec->toUnicode(item.oldMsgId));
+ msg.setComment(codec->toUnicode(item.tscomment));
+ msg.setOldComment(codec->toUnicode(item.oldTscomment));
+ msg.setExtraComment(codec->toUnicode(item.automaticComments));
+ msg.setTranslatorComment(codec->toUnicode(item.translatorComments));
+ msg.setPlural(item.isPlural || item.msgStr.size() > 1);
+ QStringList translations;
+ foreach (const QByteArray &bstr, item.msgStr) {
+ QString str = codec->toUnicode(bstr);
+ str.replace(QChar(Translator::TextVariantSeparator),
+ QChar(Translator::BinaryVariantSeparator));
+ translations << str;
+ }
+ msg.setTranslations(translations);
+ if (isObsolete)
+ msg.setType(TranslatorMessage::Obsolete);
+ else if (item.isFuzzy || (!msg.sourceText().isEmpty() && !msg.isTranslated()))
+ msg.setType(TranslatorMessage::Unfinished);
+ else
+ msg.setType(TranslatorMessage::Finished);
+ msg.setExtras(item.extra);
+
+ //qDebug() << "WRITE: " << context;
+ //qDebug() << "SOURCE: " << msg.sourceText();
+ //qDebug() << flags << msg.m_extra;
+ translator.append(msg);
+ item = PoItem();
+ } else if (line.startsWith('#')) {
+ switch (line.size() < 2 ? 0 : line.at(1)) {
+ case ':':
+ item.references += line.mid(3);
+ item.references += '\n';
+ break;
+ case ',': {
+ QStringList flags =
+ QString::fromLatin1(line.mid(2)).split(
+ QRegExp(QLatin1String("[, ]")), QString::SkipEmptyParts);
+ if (flags.removeOne(QLatin1String("fuzzy")))
+ item.isFuzzy = true;
+ flags.removeOne(QLatin1String("qt-format"));
+ TranslatorMessage::ExtraData::const_iterator it =
+ item.extra.find(QLatin1String("po-flags"));
+ if (it != item.extra.end())
+ flags.prepend(*it);
+ if (!flags.isEmpty())
+ item.extra[QLatin1String("po-flags")] = flags.join(QLatin1String(", "));
+ break;
+ }
+ case 0:
+ item.translatorComments += '\n';
+ break;
+ case ' ':
+ slurpComment(item.translatorComments, lines, l);
+ break;
+ case '.':
+ if (line.startsWith("#. ts-context ")) { // legacy
+ item.context = line.mid(14);
+ } else if (line.startsWith("#. ts-id ")) {
+ item.id = line.mid(9);
+ } else {
+ item.automaticComments += line.mid(3);
+ item.automaticComments += '\n';
+ }
+ break;
+ case '|':
+ if (line.startsWith("#| msgid ")) {
+ item.oldMsgId = slurpEscapedString(lines, l, 9, "#| ", cd);
+ } else if (line.startsWith("#| msgid_plural ")) {
+ QByteArray extra = slurpEscapedString(lines, l, 16, "#| ", cd);
+ if (extra != item.oldMsgId)
+ item.extra[QLatin1String("po-old_msgid_plural")] =
+ codec->toUnicode(extra);
+ } else if (line.startsWith("#| msgctxt ")) {
+ item.oldTscomment = slurpEscapedString(lines, l, 11, "#| ", cd);
+ if (qtContexts)
+ splitContext(&item.oldTscomment, &item.context);
+ } else {
+ cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'"))
+ .arg(l + 1).arg(codec->toUnicode(lines[l])));
+ error = true;
+ }
+ break;
+ case '~':
+ if (line.startsWith("#~ msgid ")) {
+ item.msgId = slurpEscapedString(lines, l, 9, "#~ ", cd);
+ } else if (line.startsWith("#~ msgid_plural ")) {
+ QByteArray extra = slurpEscapedString(lines, l, 16, "#~ ", cd);
+ if (extra != item.msgId)
+ item.extra[QLatin1String("po-msgid_plural")] =
+ codec->toUnicode(extra);
+ item.isPlural = true;
+ } else if (line.startsWith("#~ msgctxt ")) {
+ item.tscomment = slurpEscapedString(lines, l, 11, "#~ ", cd);
+ if (qtContexts)
+ splitContext(&item.tscomment, &item.context);
+ } else {
+ cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'"))
+ .arg(l + 1).arg(codec->toUnicode(lines[l])));
+ error = true;
+ }
+ break;
+ default:
+ cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'"))
+ .arg(l + 1).arg(codec->toUnicode(lines[l])));
+ error = true;
+ break;
+ }
+ lastCmtLine = l;
+ } else if (line.startsWith("msgctxt ")) {
+ item.tscomment = slurpEscapedString(lines, l, 8, QByteArray(), cd);
+ if (qtContexts)
+ splitContext(&item.tscomment, &item.context);
+ } else if (line.startsWith("msgid ")) {
+ item.msgId = slurpEscapedString(lines, l, 6, QByteArray(), cd);
+ } else if (line.startsWith("msgid_plural ")) {
+ QByteArray extra = slurpEscapedString(lines, l, 13, QByteArray(), cd);
+ if (extra != item.msgId)
+ item.extra[QLatin1String("po-msgid_plural")] = codec->toUnicode(extra);
+ item.isPlural = true;
+ } else {
+ cd.appendError(QString(QLatin1String("PO-format error in line %1: '%2'"))
+ .arg(l + 1).arg(codec->toUnicode(lines[l])));
+ error = true;
+ }
+ }
+ return !error && cd.errors().isEmpty();
+}
+
+static void addPoHeader(Translator::ExtraData &headers, QStringList &hdrOrder,
+ const char *name, const QString &value)
+{
+ QString qName = QLatin1String(name);
+ if (!hdrOrder.contains(qName))
+ hdrOrder << qName;
+ headers[makePoHeader(qName)] = value;
+}
+
+static QString escapeComment(const QString &in, bool escape)
+{
+ QString out = in;
+ if (escape) {
+ out.replace(QLatin1Char('~'), QLatin1String("~~"));
+ out.replace(QLatin1Char('|'), QLatin1String("~|"));
+ }
+ return out;
+}
+
+bool savePO(const Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ QString str_format = QLatin1String("-format");
+
+ bool ok = true;
+ QTextStream out(&dev);
+ out.setCodec(cd.m_outputCodec.isEmpty() ? QByteArray("UTF-8") : cd.m_outputCodec);
+
+ bool qtContexts = false;
+ foreach (const TranslatorMessage &msg, translator.messages())
+ if (!msg.context().isEmpty()) {
+ qtContexts = true;
+ break;
+ }
+
+ QString cmt = translator.extra(QLatin1String("po-header_comment"));
+ if (!cmt.isEmpty())
+ out << cmt << '\n';
+ out << "msgid \"\"\n";
+ Translator::ExtraData headers = translator.extras();
+ QStringList hdrOrder = translator.extra(QLatin1String("po-headers")).split(
+ QLatin1Char(','), QString::SkipEmptyParts);
+ // Keep in sync with loadPO
+ addPoHeader(headers, hdrOrder, "MIME-Version", QLatin1String("1.0"));
+ addPoHeader(headers, hdrOrder, "Content-Type",
+ QLatin1String("text/plain; charset=" + out.codec()->name()));
+ addPoHeader(headers, hdrOrder, "Content-Transfer-Encoding", QLatin1String("8bit"));
+ if (!translator.languageCode().isEmpty()) {
+ QLocale::Language l;
+ QLocale::Country c;
+ Translator::languageAndCountry(translator.languageCode(), &l, &c);
+ const char *gettextRules;
+ if (getNumerusInfo(l, c, 0, 0, &gettextRules))
+ addPoHeader(headers, hdrOrder, "Plural-Forms", QLatin1String(gettextRules));
+ addPoHeader(headers, hdrOrder, "X-Language", translator.languageCode());
+ }
+ if (!translator.sourceLanguageCode().isEmpty())
+ addPoHeader(headers, hdrOrder, "X-Source-Language", translator.sourceLanguageCode());
+ if (qtContexts)
+ addPoHeader(headers, hdrOrder, "X-Qt-Contexts", QLatin1String("true"));
+ QString hdrStr;
+ foreach (const QString &hdr, hdrOrder) {
+ hdrStr += hdr;
+ hdrStr += QLatin1String(": ");
+ hdrStr += headers.value(makePoHeader(hdr));
+ hdrStr += QLatin1Char('\n');
+ }
+ out << poEscapedString(QString(), QString::fromLatin1("msgstr"), true, hdrStr);
+
+ foreach (const TranslatorMessage &msg, translator.messages()) {
+ out << endl;
+
+ if (!msg.translatorComment().isEmpty())
+ out << poEscapedLines(QLatin1String("#"), true, msg.translatorComment());
+
+ if (!msg.extraComment().isEmpty())
+ out << poEscapedLines(QLatin1String("#."), true, msg.extraComment());
+
+ if (!msg.id().isEmpty())
+ out << QLatin1String("#. ts-id ") << msg.id() << '\n';
+
+ QString xrefs = msg.extra(QLatin1String("po-references"));
+ if (!msg.fileName().isEmpty() || !xrefs.isEmpty()) {
+ QStringList refs;
+ foreach (const TranslatorMessage::Reference &ref, msg.allReferences())
+ refs.append(QString(QLatin1String("%2:%1"))
+ .arg(ref.lineNumber()).arg(ref.fileName()));
+ if (!xrefs.isEmpty())
+ refs << xrefs;
+ out << poWrappedEscapedLines(QLatin1String("#:"), true, refs.join(QLatin1String(" ")));
+ }
+
+ bool noWrap = false;
+ bool skipFormat = false;
+ QStringList flags;
+ if (msg.type() == TranslatorMessage::Unfinished && msg.isTranslated())
+ flags.append(QLatin1String("fuzzy"));
+ TranslatorMessage::ExtraData::const_iterator itr =
+ msg.extras().find(QLatin1String("po-flags"));
+ if (itr != msg.extras().end()) {
+ QStringList atoms = itr->split(QLatin1String(", "));
+ foreach (const QString &atom, atoms)
+ if (atom.endsWith(str_format)) {
+ skipFormat = true;
+ break;
+ }
+ if (atoms.contains(QLatin1String("no-wrap")))
+ noWrap = true;
+ flags.append(*itr);
+ }
+ if (!skipFormat) {
+ QString source = msg.sourceText();
+ // This is fuzzy logic, as we don't know whether the string is
+ // actually used with QString::arg().
+ for (int off = 0; (off = source.indexOf(QLatin1Char('%'), off)) >= 0; ) {
+ if (++off >= source.length())
+ break;
+ if (source.at(off) == QLatin1Char('n') || source.at(off).isDigit()) {
+ flags.append(QLatin1String("qt-format"));
+ break;
+ }
+ }
+ }
+ if (!flags.isEmpty())
+ out << "#, " << flags.join(QLatin1String(", ")) << '\n';
+
+ QString prefix = QLatin1String("#| ");
+ if (!msg.oldComment().isEmpty())
+ out << poEscapedString(prefix, QLatin1String("msgctxt"), noWrap,
+ escapeComment(msg.oldComment(), qtContexts));
+ if (!msg.oldSourceText().isEmpty())
+ out << poEscapedString(prefix, QLatin1String("msgid"), noWrap, msg.oldSourceText());
+ QString plural = msg.extra(QLatin1String("po-old_msgid_plural"));
+ if (!plural.isEmpty())
+ out << poEscapedString(prefix, QLatin1String("msgid_plural"), noWrap, plural);
+ prefix = QLatin1String((msg.type() == TranslatorMessage::Obsolete) ? "#~ " : "");
+ if (!msg.context().isEmpty())
+ out << poEscapedString(prefix, QLatin1String("msgctxt"), noWrap,
+ escapeComment(msg.context(), true) + QLatin1Char('|')
+ + escapeComment(msg.comment(), true));
+ else if (!msg.comment().isEmpty())
+ out << poEscapedString(prefix, QLatin1String("msgctxt"), noWrap,
+ escapeComment(msg.comment(), qtContexts));
+ out << poEscapedString(prefix, QLatin1String("msgid"), noWrap, msg.sourceText());
+ if (!msg.isPlural()) {
+ QString transl = msg.translation();
+ transl.replace(QChar(Translator::BinaryVariantSeparator),
+ QChar(Translator::TextVariantSeparator));
+ out << poEscapedString(prefix, QLatin1String("msgstr"), noWrap, transl);
+ } else {
+ QString plural = msg.extra(QLatin1String("po-msgid_plural"));
+ if (plural.isEmpty())
+ plural = msg.sourceText();
+ out << poEscapedString(prefix, QLatin1String("msgid_plural"), noWrap, plural);
+ const QStringList &translations = msg.translations();
+ for (int i = 0; i != translations.size(); ++i) {
+ QString str = translations.at(i);
+ str.replace(QChar(Translator::BinaryVariantSeparator),
+ QChar(Translator::TextVariantSeparator));
+ out << poEscapedString(prefix, QString::fromLatin1("msgstr[%1]").arg(i), noWrap,
+ str);
+ }
+ }
+ }
+ return ok;
+}
+
+static bool savePOT(const Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ Translator ttor = translator;
+ ttor.dropTranslations();
+ return savePO(ttor, dev, cd);
+}
+
+int initPO()
+{
+ Translator::FileFormat format;
+ format.extension = QLatin1String("po");
+ format.description = QObject::tr("GNU Gettext localization files");
+ format.loader = &loadPO;
+ format.saver = &savePO;
+ format.fileType = Translator::FileFormat::TranslationSource;
+ format.priority = 1;
+ Translator::registerFileFormat(format);
+ format.extension = QLatin1String("pot");
+ format.description = QObject::tr("GNU Gettext localization template files");
+ format.loader = &loadPO;
+ format.saver = &savePOT;
+ format.fileType = Translator::FileFormat::TranslationSource;
+ format.priority = -1;
+ Translator::registerFileFormat(format);
+ return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(initPO)
+
+QT_END_NAMESPACE
diff --git a/src/linguist/shared/profileevaluator.cpp b/src/linguist/shared/profileevaluator.cpp
new file mode 100644
index 000000000..94e66745b
--- /dev/null
+++ b/src/linguist/shared/profileevaluator.cpp
@@ -0,0 +1,2627 @@
+/****************************************************************************
+**
+** 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 "profileevaluator.h"
+#include "proparserutils.h"
+#include "proitems.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QDateTime>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QList>
+#include <QtCore/QRegExp>
+#include <QtCore/QSet>
+#include <QtCore/QStack>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QTextStream>
+
+#ifdef Q_OS_UNIX
+#include <unistd.h>
+#include <sys/utsname.h>
+#else
+#include <Windows.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef Q_OS_WIN32
+#define QT_POPEN _popen
+#define QT_PCLOSE _pclose
+#else
+#define QT_POPEN popen
+#define QT_PCLOSE pclose
+#endif
+
+QT_BEGIN_NAMESPACE
+
+///////////////////////////////////////////////////////////////////////
+//
+// Option
+//
+///////////////////////////////////////////////////////////////////////
+
+QString
+Option::fixString(QString string, uchar flags)
+{
+ // XXX Ripped out caching, so this will be slow. Should not matter for current uses.
+
+ //fix the environment variables
+ if (flags & Option::FixEnvVars) {
+ int rep;
+ QRegExp reg_variableName(QLatin1String("\\$\\(.*\\)"));
+ reg_variableName.setMinimal(true);
+ while ((rep = reg_variableName.indexIn(string)) != -1)
+ string.replace(rep, reg_variableName.matchedLength(),
+ QString::fromLocal8Bit(qgetenv(string.mid(rep + 2, reg_variableName.matchedLength() - 3).toLatin1().constData()).constData()));
+ }
+
+ //canonicalize it (and treat as a path)
+ if (flags & Option::FixPathCanonicalize) {
+#if 0
+ string = QFileInfo(string).canonicalFilePath();
+#endif
+ string = QDir::cleanPath(string);
+ }
+
+ if (string.length() > 2 && string[0].isLetter() && string[1] == QLatin1Char(':'))
+ string[0] = string[0].toLower();
+
+ //fix separators
+ Q_ASSERT(!((flags & Option::FixPathToLocalSeparators) && (flags & Option::FixPathToTargetSeparators)));
+ if (flags & Option::FixPathToLocalSeparators) {
+#if defined(Q_OS_WIN32)
+ string = string.replace(QLatin1Char('/'), QLatin1Char('\\'));
+#else
+ string = string.replace(QLatin1Char('\\'), QLatin1Char('/'));
+#endif
+ } else if (flags & Option::FixPathToTargetSeparators) {
+ string = string.replace(QLatin1Char('/'), Option::dir_sep)
+ .replace(QLatin1Char('\\'), Option::dir_sep);
+ }
+
+ if ((string.startsWith(QLatin1Char('"')) && string.endsWith(QLatin1Char('"'))) ||
+ (string.startsWith(QLatin1Char('\'')) && string.endsWith(QLatin1Char('\''))))
+ string = string.mid(1, string.length() - 2);
+
+ return string;
+}
+
+///////////////////////////////////////////////////////////////////////
+//
+// ProFileEvaluator::Private
+//
+///////////////////////////////////////////////////////////////////////
+
+class ProFileEvaluator::Private : public AbstractProItemVisitor
+{
+public:
+ Private(ProFileEvaluator *q_);
+
+ ProFileEvaluator *q;
+ int m_lineNo; // Error reporting
+ bool m_verbose;
+
+ /////////////// Reading pro file
+
+ bool read(ProFile *pro);
+
+ ProBlock *currentBlock();
+ void updateItem();
+ bool parseLine(const QString &line);
+ void insertVariable(const QString &line, int *i);
+ void insertOperator(const char op);
+ void insertComment(const QString &comment);
+ void enterScope(bool multiLine);
+ void leaveScope();
+ void finalizeBlock();
+
+ QStack<ProBlock *> m_blockstack;
+ ProBlock *m_block;
+
+ ProItem *m_commentItem;
+ QString m_proitem;
+ QString m_pendingComment;
+ bool m_syntaxError;
+ bool m_contNextLine;
+ bool m_inQuote;
+ int m_parens;
+
+ /////////////// Evaluating pro file contents
+
+ // implementation of AbstractProItemVisitor
+ ProItem::ProItemReturn visitBeginProBlock(ProBlock *block);
+ void visitEndProBlock(ProBlock *block);
+ ProItem::ProItemReturn visitProLoopIteration();
+ void visitProLoopCleanup();
+ void visitBeginProVariable(ProVariable *variable);
+ void visitEndProVariable(ProVariable *variable);
+ ProItem::ProItemReturn visitBeginProFile(ProFile *value);
+ ProItem::ProItemReturn visitEndProFile(ProFile *value);
+ void visitProValue(ProValue *value);
+ ProItem::ProItemReturn visitProFunction(ProFunction *function);
+ void visitProOperator(ProOperator *oper);
+ void visitProCondition(ProCondition *condition);
+
+ QStringList valuesDirect(const QString &variableName) const { return m_valuemap[variableName]; }
+ QStringList values(const QString &variableName) const;
+ QStringList values(const QString &variableName, const ProFile *pro) const;
+ QStringList values(const QString &variableName, const QHash<QString, QStringList> &place,
+ const ProFile *pro) const;
+ QString propertyValue(const QString &val) const;
+
+ bool isActiveConfig(const QString &config, bool regex = false);
+ QStringList expandVariableReferences(const QString &value);
+ void doVariableReplace(QString *str);
+ QStringList evaluateExpandFunction(const QString &function, const QString &arguments);
+ QString format(const char *format) const;
+
+ QString currentFileName() const;
+ QString currentDirectory() const;
+ ProFile *currentProFile() const;
+
+ ProItem::ProItemReturn evaluateConditionalFunction(const QString &function, const QString &arguments);
+ bool evaluateFile(const QString &fileName);
+ bool evaluateFeatureFile(const QString &fileName);
+
+ static inline ProItem::ProItemReturn returnBool(bool b)
+ { return b ? ProItem::ReturnTrue : ProItem::ReturnFalse; }
+
+ QStringList evaluateFunction(ProBlock *funcPtr, const QStringList &argumentsList, bool *ok);
+
+ QStringList qmakeFeaturePaths();
+
+ struct State {
+ bool condition;
+ bool prevCondition;
+ } m_sts;
+ bool m_invertNext; // Short-lived, so not in State
+ int m_skipLevel;
+ bool m_cumulative;
+ bool m_isFirstVariableValue;
+ QString m_lastVarName;
+ ProVariable::VariableOperator m_variableOperator;
+ QString m_origfile;
+ QString m_oldPath; // To restore the current path to the path
+ QStack<ProFile*> m_profileStack; // To handle 'include(a.pri), so we can track back to 'a.pro' when finished with 'a.pri'
+ struct ProLoop {
+ QString variable;
+ QStringList oldVarVal;
+ QStringList list;
+ int index;
+ bool infinite;
+ };
+ QStack<ProLoop> m_loopStack;
+
+ // we need the following two variables for handling
+ // CONFIG = foo bar $$CONFIG
+ QHash<QString, QStringList> m_tempValuemap; // used while evaluating (variable operator value1 value2 ...)
+ QHash<const ProFile*, QHash<QString, QStringList> > m_tempFilevaluemap; // used while evaluating (variable operator value1 value2 ...)
+
+ QHash<QString, QStringList> m_valuemap; // VariableName must be us-ascii, the content however can be non-us-ascii.
+ QHash<const ProFile*, QHash<QString, QStringList> > m_filevaluemap; // Variables per include file
+ QHash<QString, QString> m_properties;
+ QString m_outputDir;
+
+ bool m_definingTest;
+ QString m_definingFunc;
+ QHash<QString, ProBlock *> m_testFunctions;
+ QHash<QString, ProBlock *> m_replaceFunctions;
+ QStringList m_returnValue;
+ QStack<QHash<QString, QStringList> > m_valuemapStack;
+ QStack<QHash<const ProFile*, QHash<QString, QStringList> > > m_filevaluemapStack;
+
+ int m_prevLineNo; // Checking whether we're assigning the same TARGET
+ ProFile *m_prevProFile; // See m_prevLineNo
+};
+
+Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::State, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::ProLoop, Q_MOVABLE_TYPE);
+
+ProFileEvaluator::Private::Private(ProFileEvaluator *q_)
+ : q(q_)
+{
+ // Global parser state
+ m_prevLineNo = 0;
+ m_prevProFile = 0;
+
+ // Configuration, more or less
+ m_verbose = true;
+ m_cumulative = true;
+
+ // Evaluator state
+ m_sts.condition = false;
+ m_sts.prevCondition = false;
+ m_invertNext = false;
+ m_skipLevel = 0;
+ m_isFirstVariableValue = true;
+ m_definingFunc.clear();
+}
+
+bool ProFileEvaluator::Private::read(ProFile *pro)
+{
+ QFile file(pro->fileName());
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ q->errorMessage(format("%1 not readable.").arg(pro->fileName()));
+ return false;
+ }
+
+ // Parser state
+ m_block = 0;
+ m_commentItem = 0;
+ m_inQuote = false;
+ m_parens = 0;
+ m_contNextLine = false;
+ m_syntaxError = false;
+ m_lineNo = 1;
+ m_blockstack.clear();
+ m_blockstack.push(pro);
+
+ QTextStream ts(&file);
+ while (!ts.atEnd()) {
+ QString line = ts.readLine();
+ if (!parseLine(line)) {
+ q->errorMessage(format(".pro parse failure."));
+ return false;
+ }
+ ++m_lineNo;
+ }
+ return true;
+}
+
+bool ProFileEvaluator::Private::parseLine(const QString &line0)
+{
+ if (m_blockstack.isEmpty())
+ return false;
+
+ int parens = m_parens;
+ bool inQuote = m_inQuote;
+ bool escaped = false;
+ QString line = line0.simplified();
+
+ for (int i = 0; !m_syntaxError && i < line.length(); ++i) {
+ ushort c = line.at(i).unicode();
+ if (c == '#') { // Yep - no escaping possible
+ insertComment(line.mid(i + 1));
+ escaped = m_contNextLine;
+ break;
+ }
+ if (!escaped) {
+ if (c == '\\') {
+ escaped = true;
+ m_proitem += c;
+ continue;
+ } else if (c == '"') {
+ inQuote = !inQuote;
+ m_proitem += c;
+ continue;
+ }
+ } else {
+ escaped = false;
+ }
+ if (!inQuote) {
+ if (c == '(') {
+ ++parens;
+ } else if (c == ')') {
+ --parens;
+ } else if (!parens) {
+ if (m_block && (m_block->blockKind() & ProBlock::VariableKind)) {
+ if (c == ' ')
+ updateItem();
+ else
+ m_proitem += c;
+ continue;
+ }
+ if (c == ':') {
+ enterScope(false);
+ continue;
+ }
+ if (c == '{') {
+ enterScope(true);
+ continue;
+ }
+ if (c == '}') {
+ leaveScope();
+ continue;
+ }
+ if (c == '=') {
+ insertVariable(line, &i);
+ continue;
+ }
+ if (c == '|' || c == '!') {
+ insertOperator(c);
+ continue;
+ }
+ }
+ }
+
+ m_proitem += c;
+ }
+ m_inQuote = inQuote;
+ m_parens = parens;
+ m_contNextLine = escaped;
+ if (escaped) {
+ m_proitem.chop(1);
+ updateItem();
+ return true;
+ } else {
+ if (!m_syntaxError) {
+ updateItem();
+ finalizeBlock();
+ return true;
+ }
+ return false;
+ }
+}
+
+void ProFileEvaluator::Private::finalizeBlock()
+{
+ if (m_blockstack.isEmpty()) {
+ m_syntaxError = true;
+ } else {
+ if (m_blockstack.top()->blockKind() & ProBlock::SingleLine)
+ leaveScope();
+ m_block = 0;
+ m_commentItem = 0;
+ }
+}
+
+void ProFileEvaluator::Private::insertVariable(const QString &line, int *i)
+{
+ ProVariable::VariableOperator opkind;
+
+ if (m_proitem.isEmpty()) // Line starting with '=', like a conflict marker
+ return;
+
+ switch (m_proitem.at(m_proitem.length() - 1).unicode()) {
+ case '+':
+ m_proitem.chop(1);
+ opkind = ProVariable::AddOperator;
+ break;
+ case '-':
+ m_proitem.chop(1);
+ opkind = ProVariable::RemoveOperator;
+ break;
+ case '*':
+ m_proitem.chop(1);
+ opkind = ProVariable::UniqueAddOperator;
+ break;
+ case '~':
+ m_proitem.chop(1);
+ opkind = ProVariable::ReplaceOperator;
+ break;
+ default:
+ opkind = ProVariable::SetOperator;
+ }
+
+ ProBlock *block = m_blockstack.top();
+ m_proitem = m_proitem.trimmed();
+ ProVariable *variable = new ProVariable(m_proitem, block);
+ variable->setLineNumber(m_lineNo);
+ variable->setVariableOperator(opkind);
+ block->appendItem(variable);
+ m_block = variable;
+
+ if (!m_pendingComment.isEmpty()) {
+ m_block->setComment(m_pendingComment);
+ m_pendingComment.clear();
+ }
+ m_commentItem = variable;
+
+ m_proitem.clear();
+
+ if (opkind == ProVariable::ReplaceOperator) {
+ // skip util end of line or comment
+ while (1) {
+ ++(*i);
+
+ // end of line?
+ if (*i >= line.count())
+ break;
+
+ // comment?
+ if (line.at(*i).unicode() == '#') {
+ --(*i);
+ break;
+ }
+
+ m_proitem += line.at(*i);
+ }
+ m_proitem = m_proitem.trimmed();
+ }
+}
+
+void ProFileEvaluator::Private::insertOperator(const char op)
+{
+ updateItem();
+
+ ProOperator::OperatorKind opkind;
+ switch(op) {
+ case '!':
+ opkind = ProOperator::NotOperator;
+ break;
+ case '|':
+ opkind = ProOperator::OrOperator;
+ break;
+ default:
+ opkind = ProOperator::OrOperator;
+ }
+
+ ProBlock * const block = currentBlock();
+ ProOperator * const proOp = new ProOperator(opkind);
+ proOp->setLineNumber(m_lineNo);
+ block->appendItem(proOp);
+ m_commentItem = proOp;
+}
+
+void ProFileEvaluator::Private::insertComment(const QString &comment)
+{
+ updateItem();
+
+ QString strComment;
+ if (!m_commentItem)
+ strComment = m_pendingComment;
+ else
+ strComment = m_commentItem->comment();
+
+ if (strComment.isEmpty())
+ strComment = comment;
+ else {
+ strComment += QLatin1Char('\n');
+ strComment += comment.trimmed();
+ }
+
+ strComment = strComment.trimmed();
+
+ if (!m_commentItem)
+ m_pendingComment = strComment;
+ else
+ m_commentItem->setComment(strComment);
+}
+
+void ProFileEvaluator::Private::enterScope(bool multiLine)
+{
+ updateItem();
+
+ ProBlock *parent = currentBlock();
+ ProBlock *block = new ProBlock(parent);
+ block->setLineNumber(m_lineNo);
+ parent->setBlockKind(ProBlock::ScopeKind);
+
+ parent->appendItem(block);
+
+ if (multiLine)
+ block->setBlockKind(ProBlock::ScopeContentsKind);
+ else
+ block->setBlockKind(ProBlock::ScopeContentsKind|ProBlock::SingleLine);
+
+ m_blockstack.push(block);
+ m_block = 0;
+}
+
+void ProFileEvaluator::Private::leaveScope()
+{
+ updateItem();
+ m_blockstack.pop();
+ finalizeBlock();
+}
+
+ProBlock *ProFileEvaluator::Private::currentBlock()
+{
+ if (m_block)
+ return m_block;
+
+ ProBlock *parent = m_blockstack.top();
+ m_block = new ProBlock(parent);
+ m_block->setLineNumber(m_lineNo);
+ parent->appendItem(m_block);
+
+ if (!m_pendingComment.isEmpty()) {
+ m_block->setComment(m_pendingComment);
+ m_pendingComment.clear();
+ }
+
+ m_commentItem = m_block;
+
+ return m_block;
+}
+
+void ProFileEvaluator::Private::updateItem()
+{
+ m_proitem = m_proitem.trimmed();
+ if (m_proitem.isEmpty())
+ return;
+
+ ProBlock *block = currentBlock();
+ if (block->blockKind() & ProBlock::VariableKind) {
+ m_commentItem = new ProValue(m_proitem, static_cast<ProVariable*>(block));
+ } else if (m_proitem.endsWith(QLatin1Char(')'))) {
+ m_commentItem = new ProFunction(m_proitem);
+ } else {
+ m_commentItem = new ProCondition(m_proitem);
+ }
+ m_commentItem->setLineNumber(m_lineNo);
+ block->appendItem(m_commentItem);
+
+ m_proitem.clear();
+}
+
+
+ProItem::ProItemReturn ProFileEvaluator::Private::visitBeginProBlock(ProBlock *block)
+{
+ if (block->blockKind() & ProBlock::ScopeContentsKind) {
+ if (!m_definingFunc.isEmpty()) {
+ if (!m_skipLevel || m_cumulative) {
+ QHash<QString, ProBlock *> *hash =
+ (m_definingTest ? &m_testFunctions : &m_replaceFunctions);
+ if (ProBlock *def = hash->value(m_definingFunc))
+ def->deref();
+ hash->insert(m_definingFunc, block);
+ block->ref();
+ block->setBlockKind(block->blockKind() | ProBlock::FunctionBodyKind);
+ }
+ m_definingFunc.clear();
+ return ProItem::ReturnSkip;
+ } else if (!(block->blockKind() & ProBlock::FunctionBodyKind)) {
+ if (!m_sts.condition)
+ ++m_skipLevel;
+ else
+ Q_ASSERT(!m_skipLevel);
+ }
+ } else {
+ if (!m_skipLevel) {
+ if (m_sts.condition) {
+ m_sts.prevCondition = true;
+ m_sts.condition = false;
+ }
+ } else {
+ Q_ASSERT(!m_sts.condition);
+ }
+ }
+ return ProItem::ReturnTrue;
+}
+
+void ProFileEvaluator::Private::visitEndProBlock(ProBlock *block)
+{
+ if ((block->blockKind() & ProBlock::ScopeContentsKind)
+ && !(block->blockKind() & ProBlock::FunctionBodyKind)) {
+ if (m_skipLevel) {
+ Q_ASSERT(!m_sts.condition);
+ --m_skipLevel;
+ } else if (!(block->blockKind() & ProBlock::SingleLine)) {
+ // Conditionals contained inside this block may have changed the state.
+ // So we reset it here to make an else following us do the right thing.
+ m_sts.condition = true;
+ }
+ }
+}
+
+ProItem::ProItemReturn ProFileEvaluator::Private::visitProLoopIteration()
+{
+ ProLoop &loop = m_loopStack.top();
+
+ if (loop.infinite) {
+ if (!loop.variable.isEmpty())
+ m_valuemap[loop.variable] = QStringList(QString::number(loop.index++));
+ if (loop.index > 1000) {
+ q->errorMessage(format("ran into infinite loop (> 1000 iterations)."));
+ return ProItem::ReturnFalse;
+ }
+ } else {
+ QString val;
+ do {
+ if (loop.index >= loop.list.count())
+ return ProItem::ReturnFalse;
+ val = loop.list.at(loop.index++);
+ } while (val.isEmpty()); // stupid, but qmake is like that
+ m_valuemap[loop.variable] = QStringList(val);
+ }
+ return ProItem::ReturnTrue;
+}
+
+void ProFileEvaluator::Private::visitProLoopCleanup()
+{
+ ProLoop &loop = m_loopStack.top();
+ m_valuemap[loop.variable] = loop.oldVarVal;
+ m_loopStack.pop_back();
+}
+
+void ProFileEvaluator::Private::visitBeginProVariable(ProVariable *variable)
+{
+ m_lastVarName = variable->variable();
+ m_variableOperator = variable->variableOperator();
+ m_isFirstVariableValue = true;
+ m_tempValuemap = m_valuemap;
+ m_tempFilevaluemap = m_filevaluemap;
+}
+
+void ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable)
+{
+ Q_UNUSED(variable);
+ m_valuemap = m_tempValuemap;
+ m_filevaluemap = m_tempFilevaluemap;
+ m_lastVarName.clear();
+}
+
+void ProFileEvaluator::Private::visitProOperator(ProOperator *oper)
+{
+ m_invertNext = (oper->operatorKind() == ProOperator::NotOperator);
+}
+
+void ProFileEvaluator::Private::visitProCondition(ProCondition *cond)
+{
+ if (!m_skipLevel) {
+ if (!cond->text().compare(QLatin1String("else"), Qt::CaseInsensitive)) {
+ m_sts.condition = !m_sts.prevCondition;
+ } else {
+ m_sts.prevCondition = false;
+ if (!m_sts.condition && isActiveConfig(cond->text(), true) ^ m_invertNext)
+ m_sts.condition = true;
+ }
+ }
+ m_invertNext = false;
+}
+
+ProItem::ProItemReturn ProFileEvaluator::Private::visitBeginProFile(ProFile * pro)
+{
+ PRE(pro);
+ m_lineNo = pro->lineNumber();
+ if (m_origfile.isEmpty())
+ m_origfile = pro->fileName();
+ if (m_oldPath.isEmpty()) {
+ // change the working directory for the initial profile we visit, since
+ // that is *the* profile. All the other times we reach this function will be due to
+ // include(file) or load(file)
+
+ m_oldPath = QDir::currentPath();
+
+ m_profileStack.push(pro);
+
+ const QString mkspecDirectory = propertyValue(QLatin1String("QMAKE_MKSPECS"));
+ if (!mkspecDirectory.isEmpty()) {
+ bool cumulative = m_cumulative;
+ m_cumulative = false;
+ // This is what qmake does, everything set in the mkspec is also set
+ // But this also creates a lot of problems
+ evaluateFile(mkspecDirectory + QLatin1String("/default/qmake.conf"));
+ evaluateFile(mkspecDirectory + QLatin1String("/features/default_pre.prf"));
+ m_cumulative = cumulative;
+ }
+
+ return returnBool(QDir::setCurrent(pro->directoryName()));
+ }
+
+ return ProItem::ReturnTrue;
+}
+
+ProItem::ProItemReturn ProFileEvaluator::Private::visitEndProFile(ProFile * pro)
+{
+ PRE(pro);
+ m_lineNo = pro->lineNumber();
+ if (m_profileStack.count() == 1 && !m_oldPath.isEmpty()) {
+ const QString &mkspecDirectory = propertyValue(QLatin1String("QMAKE_MKSPECS"));
+ if (!mkspecDirectory.isEmpty()) {
+ bool cumulative = m_cumulative;
+ m_cumulative = false;
+
+ evaluateFile(mkspecDirectory + QLatin1String("/features/default_post.prf"));
+
+ QSet<QString> processed;
+ forever {
+ bool finished = true;
+ QStringList configs = valuesDirect(QLatin1String("CONFIG"));
+ for (int i = configs.size() - 1; i >= 0; --i) {
+ const QString config = configs[i].toLower();
+ if (!processed.contains(config)) {
+ processed.insert(config);
+ if (evaluateFile(mkspecDirectory + QLatin1String("/features/")
+ + config + QLatin1String(".prf"))) {
+ finished = false;
+ break;
+ }
+ }
+ }
+ if (finished)
+ break;
+ }
+
+ foreach (ProBlock *itm, m_replaceFunctions)
+ itm->deref();
+ m_replaceFunctions.clear();
+ foreach (ProBlock *itm, m_testFunctions)
+ itm->deref();
+ m_testFunctions.clear();
+
+ m_cumulative = cumulative;
+ }
+
+ m_profileStack.pop();
+ return returnBool(QDir::setCurrent(m_oldPath));
+ }
+
+ return ProItem::ReturnTrue;
+}
+
+static void replaceInList(QStringList *varlist,
+ const QRegExp &regexp, const QString &replace, bool global)
+{
+ for (QStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) {
+ if ((*varit).contains(regexp)) {
+ (*varit).replace(regexp, replace);
+ if ((*varit).isEmpty())
+ varit = varlist->erase(varit);
+ else
+ ++varit;
+ if(!global)
+ break;
+ } else {
+ ++varit;
+ }
+ }
+}
+
+void ProFileEvaluator::Private::visitProValue(ProValue *value)
+{
+ PRE(value);
+ m_lineNo = value->lineNumber();
+ QString val = value->value();
+
+ QString varName = m_lastVarName;
+
+ QStringList v = expandVariableReferences(val);
+
+ // Since qmake combines different values for the TARGET variable, we join
+ // TARGET values that are on the same line. We can't do this later with all
+ // values because this parser isn't scope-aware, so we'd risk joining
+ // scope-specific targets together.
+ if (varName == QLatin1String("TARGET")
+ && m_lineNo == m_prevLineNo
+ && currentProFile() == m_prevProFile) {
+ QStringList targets = m_tempValuemap.value(QLatin1String("TARGET"));
+ m_tempValuemap.remove(QLatin1String("TARGET"));
+ QStringList lastTarget(targets.takeLast());
+ lastTarget << v.join(QLatin1String(" "));
+ targets.push_back(lastTarget.join(QLatin1String(" ")));
+ v = targets;
+ }
+ m_prevLineNo = m_lineNo;
+ m_prevProFile = currentProFile();
+
+ switch (m_variableOperator) {
+ case ProVariable::SetOperator: // =
+ if (!m_cumulative) {
+ if (!m_skipLevel) {
+ if (m_isFirstVariableValue) {
+ m_tempValuemap[varName] = v;
+ m_tempFilevaluemap[currentProFile()][varName] = v;
+ } else { // handle lines "CONFIG = foo bar"
+ m_tempValuemap[varName] += v;
+ m_tempFilevaluemap[currentProFile()][varName] += v;
+ }
+ }
+ } else {
+ // We are greedy for values.
+ m_tempValuemap[varName] += v;
+ m_tempFilevaluemap[currentProFile()][varName] += v;
+ }
+ break;
+ case ProVariable::UniqueAddOperator: // *=
+ if (!m_skipLevel || m_cumulative) {
+ insertUnique(&m_tempValuemap, varName, v);
+ insertUnique(&m_tempFilevaluemap[currentProFile()], varName, v);
+ }
+ break;
+ case ProVariable::AddOperator: // +=
+ if (!m_skipLevel || m_cumulative) {
+ m_tempValuemap[varName] += v;
+ m_tempFilevaluemap[currentProFile()][varName] += v;
+ }
+ break;
+ case ProVariable::RemoveOperator: // -=
+ if (!m_cumulative) {
+ if (!m_skipLevel) {
+ removeEach(&m_tempValuemap, varName, v);
+ removeEach(&m_tempFilevaluemap[currentProFile()], varName, v);
+ }
+ } else {
+ // We are stingy with our values, too.
+ }
+ break;
+ case ProVariable::ReplaceOperator: // ~=
+ {
+ // DEFINES ~= s/a/b/?[gqi]
+
+ doVariableReplace(&val);
+ if (val.length() < 4 || val[0] != QLatin1Char('s')) {
+ q->logMessage(format("the ~= operator can handle only the s/// function."));
+ break;
+ }
+ QChar sep = val.at(1);
+ QStringList func = val.split(sep);
+ if (func.count() < 3 || func.count() > 4) {
+ q->logMessage(format("the s/// function expects 3 or 4 arguments."));
+ break;
+ }
+
+ bool global = false, quote = false, case_sense = false;
+ if (func.count() == 4) {
+ global = func[3].indexOf(QLatin1Char('g')) != -1;
+ case_sense = func[3].indexOf(QLatin1Char('i')) == -1;
+ quote = func[3].indexOf(QLatin1Char('q')) != -1;
+ }
+ QString pattern = func[1];
+ QString replace = func[2];
+ if (quote)
+ pattern = QRegExp::escape(pattern);
+
+ QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive);
+
+ if (!m_skipLevel || m_cumulative) {
+ // We could make a union of modified and unmodified values,
+ // but this will break just as much as it fixes, so leave it as is.
+ replaceInList(&m_tempValuemap[varName], regexp, replace, global);
+ replaceInList(&m_tempFilevaluemap[currentProFile()][varName], regexp, replace, global);
+ }
+ }
+ break;
+
+ }
+ m_isFirstVariableValue = false;
+}
+
+ProItem::ProItemReturn ProFileEvaluator::Private::visitProFunction(ProFunction *func)
+{
+ // Make sure that called subblocks don't inherit & destroy the state
+ bool invertThis = m_invertNext;
+ m_invertNext = false;
+ if (!m_skipLevel)
+ m_sts.prevCondition = false;
+ if (m_cumulative || !m_sts.condition) {
+ QString text = func->text();
+ int lparen = text.indexOf(QLatin1Char('('));
+ int rparen = text.lastIndexOf(QLatin1Char(')'));
+ Q_ASSERT(lparen < rparen);
+ QString arguments = text.mid(lparen + 1, rparen - lparen - 1);
+ QString funcName = text.left(lparen);
+ m_lineNo = func->lineNumber();
+ ProItem::ProItemReturn result = evaluateConditionalFunction(funcName.trimmed(), arguments);
+ if (result != ProItem::ReturnFalse && result != ProItem::ReturnTrue)
+ return result;
+ if (!m_skipLevel && ((result == ProItem::ReturnTrue) ^ invertThis))
+ m_sts.condition = true;
+ }
+ return ProItem::ReturnTrue;
+}
+
+
+QStringList ProFileEvaluator::Private::qmakeFeaturePaths()
+{
+ QStringList concat;
+ {
+ const QString base_concat = QDir::separator() + QLatin1String("features");
+ concat << base_concat + QDir::separator() + QLatin1String("mac");
+ concat << base_concat + QDir::separator() + QLatin1String("macx");
+ concat << base_concat + QDir::separator() + QLatin1String("unix");
+ concat << base_concat + QDir::separator() + QLatin1String("win32");
+ concat << base_concat + QDir::separator() + QLatin1String("mac9");
+ concat << base_concat + QDir::separator() + QLatin1String("qnx6");
+ concat << base_concat;
+ }
+ const QString mkspecs_concat = QDir::separator() + QLatin1String("mkspecs");
+ QStringList feature_roots;
+ QByteArray mkspec_path = qgetenv("QMAKEFEATURES");
+ if (!mkspec_path.isNull())
+ feature_roots += splitPathList(QString::fromLocal8Bit(mkspec_path));
+ /*
+ if (prop)
+ feature_roots += splitPathList(prop->value("QMAKEFEATURES"));
+ if (!Option::mkfile::cachefile.isEmpty()) {
+ QString path;
+ int last_slash = Option::mkfile::cachefile.lastIndexOf(Option::dir_sep);
+ if (last_slash != -1)
+ path = Option::fixPathToLocalOS(Option::mkfile::cachefile.left(last_slash));
+ foreach (const QString &concat_it, concat)
+ feature_roots << (path + concat_it);
+ }
+ */
+
+ QByteArray qmakepath = qgetenv("QMAKEPATH");
+ if (!qmakepath.isNull()) {
+ const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath));
+ foreach (const QString &item, lst) {
+ foreach (const QString &concat_it, concat)
+ feature_roots << (item + mkspecs_concat + concat_it);
+ }
+ }
+ //if (!Option::mkfile::qmakespec.isEmpty())
+ // feature_roots << Option::mkfile::qmakespec + QDir::separator() + "features";
+ //if (!Option::mkfile::qmakespec.isEmpty()) {
+ // QFileInfo specfi(Option::mkfile::qmakespec);
+ // QDir specdir(specfi.absoluteFilePath());
+ // while (!specdir.isRoot()) {
+ // if (!specdir.cdUp() || specdir.isRoot())
+ // break;
+ // if (QFile::exists(specdir.path() + QDir::separator() + "features")) {
+ // foreach (const QString &concat_it, concat)
+ // feature_roots << (specdir.path() + concat_it);
+ // break;
+ // }
+ // }
+ //}
+ foreach (const QString &concat_it, concat)
+ feature_roots << (propertyValue(QLatin1String("QT_INSTALL_PREFIX")) +
+ mkspecs_concat + concat_it);
+ foreach (const QString &concat_it, concat)
+ feature_roots << (propertyValue(QLatin1String("QT_INSTALL_DATA")) +
+ mkspecs_concat + concat_it);
+ return feature_roots;
+}
+
+QString ProFileEvaluator::Private::propertyValue(const QString &name) const
+{
+ if (m_properties.contains(name))
+ return m_properties.value(name);
+ if (name == QLatin1String("QT_INSTALL_PREFIX"))
+ return QLibraryInfo::location(QLibraryInfo::PrefixPath);
+ if (name == QLatin1String("QT_INSTALL_DATA"))
+ return QLibraryInfo::location(QLibraryInfo::DataPath);
+ if (name == QLatin1String("QT_INSTALL_DOCS"))
+ return QLibraryInfo::location(QLibraryInfo::DocumentationPath);
+ if (name == QLatin1String("QT_INSTALL_HEADERS"))
+ return QLibraryInfo::location(QLibraryInfo::HeadersPath);
+ if (name == QLatin1String("QT_INSTALL_LIBS"))
+ return QLibraryInfo::location(QLibraryInfo::LibrariesPath);
+ if (name == QLatin1String("QT_INSTALL_BINS"))
+ return QLibraryInfo::location(QLibraryInfo::BinariesPath);
+ if (name == QLatin1String("QT_INSTALL_PLUGINS"))
+ return QLibraryInfo::location(QLibraryInfo::PluginsPath);
+ if (name == QLatin1String("QT_INSTALL_TRANSLATIONS"))
+ return QLibraryInfo::location(QLibraryInfo::TranslationsPath);
+ if (name == QLatin1String("QT_INSTALL_CONFIGURATION"))
+ return QLibraryInfo::location(QLibraryInfo::SettingsPath);
+ if (name == QLatin1String("QT_INSTALL_EXAMPLES"))
+ return QLibraryInfo::location(QLibraryInfo::ExamplesPath);
+ if (name == QLatin1String("QT_INSTALL_DEMOS"))
+ return QLibraryInfo::location(QLibraryInfo::DemosPath);
+ if (name == QLatin1String("QMAKE_MKSPECS"))
+ return qmake_mkspec_paths().join(Option::dirlist_sep);
+ if (name == QLatin1String("QMAKE_VERSION"))
+ return QLatin1String("1.0"); //### FIXME
+ //return qmake_version();
+#ifdef QT_VERSION_STR
+ if (name == QLatin1String("QT_VERSION"))
+ return QLatin1String(QT_VERSION_STR);
+#endif
+ return QLatin1String("UNKNOWN"); //###
+}
+
+ProFile *ProFileEvaluator::Private::currentProFile() const
+{
+ if (m_profileStack.count() > 0)
+ return m_profileStack.top();
+ return 0;
+}
+
+QString ProFileEvaluator::Private::currentFileName() const
+{
+ ProFile *pro = currentProFile();
+ if (pro)
+ return pro->fileName();
+ return QString();
+}
+
+QString ProFileEvaluator::Private::currentDirectory() const
+{
+ ProFile *cur = m_profileStack.top();
+ return cur->directoryName();
+}
+
+void ProFileEvaluator::Private::doVariableReplace(QString *str)
+{
+ *str = expandVariableReferences(*str).join(QString(Option::field_sep));
+}
+
+QStringList ProFileEvaluator::Private::expandVariableReferences(const QString &str)
+{
+ QStringList ret;
+// if (ok)
+// *ok = true;
+ if (str.isEmpty())
+ return ret;
+
+ const ushort LSQUARE = '[';
+ const ushort RSQUARE = ']';
+ const ushort LCURLY = '{';
+ const ushort RCURLY = '}';
+ const ushort LPAREN = '(';
+ const ushort RPAREN = ')';
+ const ushort DOLLAR = '$';
+ const ushort BACKSLASH = '\\';
+ const ushort UNDERSCORE = '_';
+ const ushort DOT = '.';
+ const ushort SPACE = ' ';
+ const ushort TAB = '\t';
+ const ushort SINGLEQUOTE = '\'';
+ const ushort DOUBLEQUOTE = '"';
+
+ ushort unicode, quote = 0;
+ const QChar *str_data = str.data();
+ const int str_len = str.length();
+
+ ushort term;
+ QString var, args;
+
+ int replaced = 0;
+ QString current;
+ for (int i = 0; i < str_len; ++i) {
+ unicode = str_data[i].unicode();
+ const int start_var = i;
+ if (unicode == DOLLAR && str_len > i+2) {
+ unicode = str_data[++i].unicode();
+ if (unicode == DOLLAR) {
+ term = 0;
+ var.clear();
+ args.clear();
+ enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
+ unicode = str_data[++i].unicode();
+ if (unicode == LSQUARE) {
+ unicode = str_data[++i].unicode();
+ term = RSQUARE;
+ var_type = PROPERTY;
+ } else if (unicode == LCURLY) {
+ unicode = str_data[++i].unicode();
+ var_type = VAR;
+ term = RCURLY;
+ } else if (unicode == LPAREN) {
+ unicode = str_data[++i].unicode();
+ var_type = ENVIRON;
+ term = RPAREN;
+ }
+ forever {
+ if (!(unicode & (0xFF<<8)) &&
+ unicode != DOT && unicode != UNDERSCORE &&
+ //unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE &&
+ (unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
+ (unicode < '0' || unicode > '9'))
+ break;
+ var.append(QChar(unicode));
+ if (++i == str_len)
+ break;
+ unicode = str_data[i].unicode();
+ // at this point, i points to either the 'term' or 'next' character (which is in unicode)
+ }
+ if (var_type == VAR && unicode == LPAREN) {
+ var_type = FUNCTION;
+ int depth = 0;
+ forever {
+ if (++i == str_len)
+ break;
+ unicode = str_data[i].unicode();
+ if (unicode == LPAREN) {
+ depth++;
+ } else if (unicode == RPAREN) {
+ if (!depth)
+ break;
+ --depth;
+ }
+ args.append(QChar(unicode));
+ }
+ if (++i < str_len)
+ unicode = str_data[i].unicode();
+ else
+ unicode = 0;
+ // at this point i is pointing to the 'next' character (which is in unicode)
+ // this might actually be a term character since you can do $${func()}
+ }
+ if (term) {
+ if (unicode != term) {
+ q->logMessage(format("Missing %1 terminator [found %2]")
+ .arg(QChar(term))
+ .arg(unicode ? QString(unicode) : QString::fromLatin1(("end-of-line"))));
+// if (ok)
+// *ok = false;
+ return QStringList();
+ }
+ } else {
+ // move the 'cursor' back to the last char of the thing we were looking at
+ --i;
+ }
+ // since i never points to the 'next' character, there is no reason for this to be set
+ unicode = 0;
+
+ QStringList replacement;
+ if (var_type == ENVIRON) {
+ replacement = split_value_list(QString::fromLocal8Bit(qgetenv(var.toLatin1().constData())));
+ } else if (var_type == PROPERTY) {
+ replacement << propertyValue(var);
+ } else if (var_type == FUNCTION) {
+ replacement << evaluateExpandFunction(var, args);
+ } else if (var_type == VAR) {
+ replacement = values(var);
+ }
+ if (!(replaced++) && start_var)
+ current = str.left(start_var);
+ if (!replacement.isEmpty()) {
+ if (quote) {
+ current += replacement.join(QString(Option::field_sep));
+ } else {
+ current += replacement.takeFirst();
+ if (!replacement.isEmpty()) {
+ if (!current.isEmpty())
+ ret.append(current);
+ current = replacement.takeLast();
+ if (!replacement.isEmpty())
+ ret += replacement;
+ }
+ }
+ }
+ } else {
+ if (replaced)
+ current.append(QLatin1Char('$'));
+ }
+ }
+ if (quote && unicode == quote) {
+ unicode = 0;
+ quote = 0;
+ } else if (unicode == BACKSLASH) {
+ bool escape = false;
+ const char *symbols = "[]{}()$\\'\"";
+ for (const char *s = symbols; *s; ++s) {
+ if (str_data[i+1].unicode() == (ushort)*s) {
+ i++;
+ escape = true;
+ if (!(replaced++))
+ current = str.left(start_var);
+ current.append(str.at(i));
+ break;
+ }
+ }
+ if (escape || !replaced)
+ unicode =0;
+ } else if (!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
+ quote = unicode;
+ unicode = 0;
+ if (!(replaced++) && i)
+ current = str.left(i);
+ } else if (!quote && (unicode == SPACE || unicode == TAB)) {
+ unicode = 0;
+ if (!current.isEmpty()) {
+ ret.append(current);
+ current.clear();
+ }
+ }
+ if (replaced && unicode)
+ current.append(QChar(unicode));
+ }
+ if (!replaced)
+ ret = QStringList(str);
+ else if (!current.isEmpty())
+ ret.append(current);
+ return ret;
+}
+
+bool ProFileEvaluator::Private::isActiveConfig(const QString &config, bool regex)
+{
+ // magic types for easy flipping
+ if (config == QLatin1String("true"))
+ return true;
+ if (config == QLatin1String("false"))
+ return false;
+
+ // mkspecs
+ if ((Option::target_mode == Option::TARG_MACX_MODE
+ || Option::target_mode == Option::TARG_QNX6_MODE
+ || Option::target_mode == Option::TARG_UNIX_MODE)
+ && config == QLatin1String("unix"))
+ return true;
+ if (Option::target_mode == Option::TARG_MACX_MODE && config == QLatin1String("macx"))
+ return true;
+ if (Option::target_mode == Option::TARG_QNX6_MODE && config == QLatin1String("qnx6"))
+ return true;
+ if (Option::target_mode == Option::TARG_MAC9_MODE && config == QLatin1String("mac9"))
+ return true;
+ if ((Option::target_mode == Option::TARG_MAC9_MODE
+ || Option::target_mode == Option::TARG_MACX_MODE)
+ && config == QLatin1String("mac"))
+ return true;
+ if (Option::target_mode == Option::TARG_WIN_MODE && config == QLatin1String("win32"))
+ return true;
+
+ QRegExp re(config, Qt::CaseSensitive, QRegExp::Wildcard);
+ QString spec = Option::qmakespec;
+ if ((regex && re.exactMatch(spec)) || (!regex && spec == config))
+ return true;
+
+ return false;
+}
+
+QStringList ProFileEvaluator::Private::evaluateFunction(
+ ProBlock *funcPtr, const QStringList &argumentsList, bool *ok)
+{
+ bool oki;
+ QStringList ret;
+
+ if (m_valuemapStack.count() >= 100) {
+ q->errorMessage(format("ran into infinite recursion (depth > 100)."));
+ oki = false;
+ } else {
+ State sts = m_sts;
+ m_valuemapStack.push(m_valuemap);
+ m_filevaluemapStack.push(m_filevaluemap);
+
+ QStringList args;
+ for (int i = 0; i < argumentsList.count(); ++i) {
+ QStringList theArgs = expandVariableReferences(argumentsList[i]);
+ args += theArgs;
+ m_valuemap[QString::number(i+1)] = theArgs;
+ }
+ m_valuemap[QLatin1String("ARGS")] = args;
+ oki = (funcPtr->Accept(this) != ProItem::ReturnFalse); // True || Return
+ ret = m_returnValue;
+ m_returnValue.clear();
+
+ m_valuemap = m_valuemapStack.pop();
+ m_filevaluemap = m_filevaluemapStack.pop();
+ m_sts = sts;
+ }
+ if (ok)
+ *ok = oki;
+ if (oki)
+ return ret;
+ return QStringList();
+}
+
+QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &func, const QString &arguments)
+{
+ QStringList argumentsList = split_arg_list(arguments);
+
+ if (ProBlock *funcPtr = m_replaceFunctions.value(func, 0))
+ return evaluateFunction(funcPtr, argumentsList, 0);
+
+ QStringList args;
+ for (int i = 0; i < argumentsList.count(); ++i)
+ args += expandVariableReferences(argumentsList[i]).join(Option::field_sep);
+
+ enum ExpandFunc { E_MEMBER=1, E_FIRST, E_LAST, E_CAT, E_FROMFILE, E_EVAL, E_LIST,
+ E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION,
+ E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_ESCAPE_EXPAND,
+ E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE,
+ E_REPLACE };
+
+ static QHash<QString, int> expands;
+ if (expands.isEmpty()) {
+ expands.insert(QLatin1String("member"), E_MEMBER);
+ expands.insert(QLatin1String("first"), E_FIRST);
+ expands.insert(QLatin1String("last"), E_LAST);
+ expands.insert(QLatin1String("cat"), E_CAT);
+ expands.insert(QLatin1String("fromfile"), E_FROMFILE); // implementation disabled (see comment below)
+ expands.insert(QLatin1String("eval"), E_EVAL);
+ expands.insert(QLatin1String("list"), E_LIST);
+ expands.insert(QLatin1String("sprintf"), E_SPRINTF);
+ expands.insert(QLatin1String("join"), E_JOIN);
+ expands.insert(QLatin1String("split"), E_SPLIT);
+ expands.insert(QLatin1String("basename"), E_BASENAME);
+ expands.insert(QLatin1String("dirname"), E_DIRNAME);
+ expands.insert(QLatin1String("section"), E_SECTION);
+ expands.insert(QLatin1String("find"), E_FIND);
+ expands.insert(QLatin1String("system"), E_SYSTEM);
+ expands.insert(QLatin1String("unique"), E_UNIQUE);
+ expands.insert(QLatin1String("quote"), E_QUOTE);
+ expands.insert(QLatin1String("escape_expand"), E_ESCAPE_EXPAND);
+ expands.insert(QLatin1String("upper"), E_UPPER);
+ expands.insert(QLatin1String("lower"), E_LOWER);
+ expands.insert(QLatin1String("re_escape"), E_RE_ESCAPE);
+ expands.insert(QLatin1String("files"), E_FILES);
+ expands.insert(QLatin1String("prompt"), E_PROMPT); // interactive, so cannot be implemented
+ expands.insert(QLatin1String("replace"), E_REPLACE);
+ }
+ ExpandFunc func_t = ExpandFunc(expands.value(func.toLower()));
+
+ QStringList ret;
+
+ switch (func_t) {
+ case E_BASENAME:
+ case E_DIRNAME:
+ case E_SECTION: {
+ bool regexp = false;
+ QString sep, var;
+ int beg = 0;
+ int end = -1;
+ if (func_t == E_SECTION) {
+ if (args.count() != 3 && args.count() != 4) {
+ q->logMessage(format("%1(var) section(var, sep, begin, end) "
+ "requires three or four arguments.").arg(func));
+ } else {
+ var = args[0];
+ sep = args[1];
+ beg = args[2].toInt();
+ if (args.count() == 4)
+ end = args[3].toInt();
+ }
+ } else {
+ if (args.count() != 1) {
+ q->logMessage(format("%1(var) requires one argument.").arg(func));
+ } else {
+ var = args[0];
+ regexp = true;
+ sep = QLatin1String("[\\\\/]");
+ if (func_t == E_DIRNAME)
+ end = -2;
+ else
+ beg = -1;
+ }
+ }
+ if (!var.isNull()) {
+ foreach (const QString str, values(var)) {
+ if (regexp)
+ ret += str.section(QRegExp(sep), beg, end);
+ else
+ ret += str.section(sep, beg, end);
+ }
+ }
+ break;
+ }
+ case E_SPRINTF:
+ if(args.count() < 1) {
+ q->logMessage(format("sprintf(format, ...) requires at least one argument"));
+ } else {
+ QString tmp = args.at(0);
+ for (int i = 1; i < args.count(); ++i)
+ tmp = tmp.arg(args.at(i));
+ ret = split_value_list(tmp);
+ }
+ break;
+ case E_JOIN: {
+ if (args.count() < 1 || args.count() > 4) {
+ q->logMessage(format("join(var, glue, before, after) requires one to four arguments."));
+ } else {
+ QString glue, before, after;
+ if (args.count() >= 2)
+ glue = args[1];
+ if (args.count() >= 3)
+ before = args[2];
+ if (args.count() == 4)
+ after = args[3];
+ const QStringList &var = values(args.first());
+ if (!var.isEmpty())
+ ret.append(before + var.join(glue) + after);
+ }
+ break;
+ }
+ case E_SPLIT: {
+ if (args.count() != 2) {
+ q->logMessage(format("split(var, sep) requires one or two arguments"));
+ } else {
+ const QString &sep = (args.count() == 2) ? args[1] : QString(Option::field_sep);
+ foreach (const QString &var, values(args.first()))
+ foreach (const QString &splt, var.split(sep))
+ ret.append(splt);
+ }
+ break;
+ }
+ case E_MEMBER: {
+ if (args.count() < 1 || args.count() > 3) {
+ q->logMessage(format("member(var, start, end) requires one to three arguments."));
+ } else {
+ bool ok = true;
+ const QStringList var = values(args.first());
+ int start = 0, end = 0;
+ if (args.count() >= 2) {
+ QString start_str = args[1];
+ start = start_str.toInt(&ok);
+ if (!ok) {
+ if (args.count() == 2) {
+ int dotdot = start_str.indexOf(QLatin1String(".."));
+ if (dotdot != -1) {
+ start = start_str.left(dotdot).toInt(&ok);
+ if (ok)
+ end = start_str.mid(dotdot+2).toInt(&ok);
+ }
+ }
+ if (!ok)
+ q->logMessage(format("member() argument 2 (start) '%2' invalid.")
+ .arg(start_str));
+ } else {
+ end = start;
+ if (args.count() == 3)
+ end = args[2].toInt(&ok);
+ if (!ok)
+ q->logMessage(format("member() argument 3 (end) '%2' invalid.\n")
+ .arg(args[2]));
+ }
+ }
+ if (ok) {
+ if (start < 0)
+ start += var.count();
+ if (end < 0)
+ end += var.count();
+ if (start < 0 || start >= var.count() || end < 0 || end >= var.count()) {
+ //nothing
+ } else if (start < end) {
+ for (int i = start; i <= end && var.count() >= i; i++)
+ ret.append(var[i]);
+ } else {
+ for (int i = start; i >= end && var.count() >= i && i >= 0; i--)
+ ret += var[i];
+ }
+ }
+ }
+ break;
+ }
+ case E_FIRST:
+ case E_LAST: {
+ if (args.count() != 1) {
+ q->logMessage(format("%1(var) requires one argument.").arg(func));
+ } else {
+ const QStringList var = values(args.first());
+ if (!var.isEmpty()) {
+ if (func_t == E_FIRST)
+ ret.append(var[0]);
+ else
+ ret.append(var.last());
+ }
+ }
+ break;
+ }
+ case E_CAT:
+ if (args.count() < 1 || args.count() > 2) {
+ q->logMessage(format("cat(file, singleline=true) requires one or two arguments."));
+ } else {
+ QString file = args[0];
+ file = Option::fixPathToLocalOS(file);
+
+ bool singleLine = true;
+ if (args.count() > 1)
+ singleLine = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive));
+
+ QFile qfile(file);
+ if (qfile.open(QIODevice::ReadOnly)) {
+ QTextStream stream(&qfile);
+ while (!stream.atEnd()) {
+ ret += split_value_list(stream.readLine().trimmed());
+ if (!singleLine)
+ ret += QLatin1String("\n");
+ }
+ qfile.close();
+ }
+ }
+ break;
+#if 0 // Used only by Qt's configure for caching
+ case E_FROMFILE:
+ if (args.count() != 2) {
+ q->logMessage(format("fromfile(file, variable) requires two arguments."));
+ } else {
+ QString file = args[0], seek_variableName = args[1];
+
+ ProFile pro(Option::fixPathToLocalOS(file));
+
+ ProFileEvaluator visitor;
+ visitor.setVerbose(m_verbose);
+ visitor.setCumulative(m_cumulative);
+
+ if (!visitor.queryProFile(&pro))
+ break;
+
+ if (!visitor.accept(&pro))
+ break;
+
+ ret = visitor.values(seek_variableName);
+ }
+ break;
+#endif
+ case E_EVAL: {
+ if (args.count() != 1) {
+ q->logMessage(format("eval(variable) requires one argument"));
+
+ } else {
+ ret += values(args.at(0));
+ }
+ break; }
+ case E_LIST: {
+ static int x = 0;
+ QString tmp;
+ tmp.sprintf(".QMAKE_INTERNAL_TMP_variableName_%d", x++);
+ ret = QStringList(tmp);
+ QStringList lst;
+ foreach (const QString &arg, args)
+ lst += split_value_list(arg);
+ m_valuemap[tmp] = lst;
+ break; }
+ case E_FIND:
+ if (args.count() != 2) {
+ q->logMessage(format("find(var, str) requires two arguments."));
+ } else {
+ QRegExp regx(args[1]);
+ foreach (const QString &val, values(args.first()))
+ if (regx.indexIn(val) != -1)
+ ret += val;
+ }
+ break;
+ case E_SYSTEM:
+ if (!m_skipLevel) {
+ if (args.count() < 1 || args.count() > 2) {
+ q->logMessage(format("system(execute) requires one or two arguments."));
+ } else {
+ char buff[256];
+ FILE *proc = QT_POPEN(args[0].toLatin1(), "r");
+ bool singleLine = true;
+ if (args.count() > 1)
+ singleLine = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive));
+ QString output;
+ while (proc && !feof(proc)) {
+ int read_in = int(fread(buff, 1, 255, proc));
+ if (!read_in)
+ break;
+ for (int i = 0; i < read_in; i++) {
+ if ((singleLine && buff[i] == '\n') || buff[i] == '\t')
+ buff[i] = ' ';
+ }
+ buff[read_in] = '\0';
+ output += QLatin1String(buff);
+ }
+ ret += split_value_list(output);
+ if (proc)
+ QT_PCLOSE(proc);
+ }
+ }
+ break;
+ case E_UNIQUE:
+ if(args.count() != 1) {
+ q->logMessage(format("unique(var) requires one argument."));
+ } else {
+ foreach (const QString &var, values(args.first()))
+ if (!ret.contains(var))
+ ret.append(var);
+ }
+ break;
+ case E_QUOTE:
+ for (int i = 0; i < args.count(); ++i)
+ ret += QStringList(args.at(i));
+ break;
+ case E_ESCAPE_EXPAND:
+ for (int i = 0; i < args.size(); ++i) {
+ QChar *i_data = args[i].data();
+ int i_len = args[i].length();
+ for (int x = 0; x < i_len; ++x) {
+ if (*(i_data+x) == QLatin1Char('\\') && x < i_len-1) {
+ if (*(i_data+x+1) == QLatin1Char('\\')) {
+ ++x;
+ } else {
+ struct {
+ char in, out;
+ } mapped_quotes[] = {
+ { 'n', '\n' },
+ { 't', '\t' },
+ { 'r', '\r' },
+ { 0, 0 }
+ };
+ for (int i = 0; mapped_quotes[i].in; ++i) {
+ if (*(i_data+x+1) == QLatin1Char(mapped_quotes[i].in)) {
+ *(i_data+x) = QLatin1Char(mapped_quotes[i].out);
+ if (x < i_len-2)
+ memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar));
+ --i_len;
+ break;
+ }
+ }
+ }
+ }
+ }
+ ret.append(QString(i_data, i_len));
+ }
+ break;
+ case E_RE_ESCAPE:
+ for (int i = 0; i < args.size(); ++i)
+ ret += QRegExp::escape(args[i]);
+ break;
+ case E_UPPER:
+ case E_LOWER:
+ for (int i = 0; i < args.count(); ++i)
+ if (func_t == E_UPPER)
+ ret += args[i].toUpper();
+ else
+ ret += args[i].toLower();
+ break;
+ case E_FILES:
+ if (args.count() != 1 && args.count() != 2) {
+ q->logMessage(format("files(pattern, recursive=false) requires one or two arguments"));
+ } else {
+ bool recursive = false;
+ if (args.count() == 2)
+ recursive = (!args[1].compare(QLatin1String("true"), Qt::CaseInsensitive) || args[1].toInt());
+ QStringList dirs;
+ QString r = Option::fixPathToLocalOS(args[0]);
+ int slash = r.lastIndexOf(QDir::separator());
+ if (slash != -1) {
+ dirs.append(r.left(slash));
+ r = r.mid(slash+1);
+ } else {
+ dirs.append(QString());
+ }
+
+ const QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard);
+ for (int d = 0; d < dirs.count(); d++) {
+ QString dir = dirs[d];
+ if (!dir.isEmpty() && !dir.endsWith(Option::dir_sep))
+ dir += QLatin1Char('/');
+
+ QDir qdir(dir);
+ for (int i = 0; i < (int)qdir.count(); ++i) {
+ if (qdir[i] == QLatin1String(".") || qdir[i] == QLatin1String(".."))
+ continue;
+ QString fname = dir + qdir[i];
+ if (QFileInfo(fname).isDir()) {
+ if (recursive)
+ dirs.append(fname);
+ }
+ if (regex.exactMatch(qdir[i]))
+ ret += fname;
+ }
+ }
+ }
+ break;
+ case E_REPLACE:
+ if(args.count() != 3 ) {
+ q->logMessage(format("replace(var, before, after) requires three arguments"));
+ } else {
+ const QRegExp before(args[1]);
+ const QString after(args[2]);
+ foreach (QString val, values(args.first()))
+ ret += val.replace(before, after);
+ }
+ break;
+ case 0:
+ q->logMessage(format("'%1' is not a recognized replace function").arg(func));
+ break;
+ default:
+ q->logMessage(format("Function '%1' is not implemented").arg(func));
+ break;
+ }
+
+ return ret;
+}
+
+ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction(
+ const QString &function, const QString &arguments)
+{
+ QStringList argumentsList = split_arg_list(arguments);
+
+ if (ProBlock *funcPtr = m_testFunctions.value(function, 0)) {
+ bool ok;
+ QStringList ret = evaluateFunction(funcPtr, argumentsList, &ok);
+ if (ok) {
+ if (ret.isEmpty()) {
+ return ProItem::ReturnTrue;
+ } else {
+ if (ret.first() != QLatin1String("false")) {
+ if (ret.first() == QLatin1String("true")) {
+ return ProItem::ReturnTrue;
+ } else {
+ bool ok;
+ int val = ret.first().toInt(&ok);
+ if (ok) {
+ if (val)
+ return ProItem::ReturnTrue;
+ } else {
+ q->logMessage(format("Unexpected return value from test '%1': %2")
+ .arg(function).arg(ret.join(QLatin1String(" :: "))));
+ }
+ }
+ }
+ }
+ }
+ return ProItem::ReturnFalse;
+ }
+
+ QString sep;
+ sep.append(Option::field_sep);
+ QStringList args;
+ for (int i = 0; i < argumentsList.count(); ++i)
+ args += expandVariableReferences(argumentsList[i]).join(sep);
+
+ enum TestFunc { T_REQUIRES=1, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
+ T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
+ T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
+ T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_MESSAGE, T_IF,
+ T_FOR, T_DEFINE_TEST, T_DEFINE_REPLACE };
+
+ static QHash<QString, int> functions;
+ if (functions.isEmpty()) {
+ functions.insert(QLatin1String("requires"), T_REQUIRES);
+ functions.insert(QLatin1String("greaterThan"), T_GREATERTHAN);
+ functions.insert(QLatin1String("lessThan"), T_LESSTHAN);
+ functions.insert(QLatin1String("equals"), T_EQUALS);
+ functions.insert(QLatin1String("isEqual"), T_EQUALS);
+ functions.insert(QLatin1String("exists"), T_EXISTS);
+ functions.insert(QLatin1String("export"), T_EXPORT);
+ functions.insert(QLatin1String("clear"), T_CLEAR);
+ functions.insert(QLatin1String("unset"), T_UNSET);
+ functions.insert(QLatin1String("eval"), T_EVAL);
+ functions.insert(QLatin1String("CONFIG"), T_CONFIG);
+ functions.insert(QLatin1String("if"), T_IF);
+ functions.insert(QLatin1String("isActiveConfig"), T_CONFIG);
+ functions.insert(QLatin1String("system"), T_SYSTEM);
+ functions.insert(QLatin1String("return"), T_RETURN);
+ functions.insert(QLatin1String("break"), T_BREAK);
+ functions.insert(QLatin1String("next"), T_NEXT);
+ functions.insert(QLatin1String("defined"), T_DEFINED);
+ functions.insert(QLatin1String("contains"), T_CONTAINS);
+ functions.insert(QLatin1String("infile"), T_INFILE);
+ functions.insert(QLatin1String("count"), T_COUNT);
+ functions.insert(QLatin1String("isEmpty"), T_ISEMPTY);
+ functions.insert(QLatin1String("load"), T_LOAD); //v
+ functions.insert(QLatin1String("include"), T_INCLUDE); //v
+ functions.insert(QLatin1String("debug"), T_DEBUG);
+ functions.insert(QLatin1String("message"), T_MESSAGE); //v
+ functions.insert(QLatin1String("warning"), T_MESSAGE); //v
+ functions.insert(QLatin1String("error"), T_MESSAGE); //v
+ functions.insert(QLatin1String("for"), T_FOR); //v
+ functions.insert(QLatin1String("defineTest"), T_DEFINE_TEST); //v
+ functions.insert(QLatin1String("defineReplace"), T_DEFINE_REPLACE); //v
+ }
+
+ TestFunc func_t = (TestFunc)functions.value(function);
+
+ switch (func_t) {
+ case T_DEFINE_TEST:
+ m_definingTest = true;
+ goto defineFunc;
+ case T_DEFINE_REPLACE:
+ m_definingTest = false;
+ defineFunc:
+ if (args.count() != 1) {
+ q->logMessage(format("%s(function) requires one argument.").arg(function));
+ return ProItem::ReturnFalse;
+ }
+ m_definingFunc = args.first();
+ return ProItem::ReturnTrue;
+ case T_DEFINED:
+ if (args.count() < 1 || args.count() > 2) {
+ q->logMessage(format("defined(function, [\"test\"|\"replace\"])"
+ " requires one or two arguments."));
+ return ProItem::ReturnFalse;
+ }
+ if (args.count() > 1) {
+ if (args[1] == QLatin1String("test"))
+ return returnBool(m_testFunctions.contains(args[0]));
+ else if (args[1] == QLatin1String("replace"))
+ return returnBool(m_replaceFunctions.contains(args[0]));
+ q->logMessage(format("defined(function, type):"
+ " unexpected type [%1].\n").arg(args[1]));
+ return ProItem::ReturnFalse;
+ }
+ return returnBool(m_replaceFunctions.contains(args[0])
+ || m_testFunctions.contains(args[0]));
+ case T_RETURN:
+ m_returnValue = args;
+ // It is "safe" to ignore returns - due to qmake brokeness
+ // they cannot be used to terminate loops anyway.
+ if (m_skipLevel || m_cumulative)
+ return ProItem::ReturnTrue;
+ if (m_valuemapStack.isEmpty()) {
+ q->logMessage(format("unexpected return()."));
+ return ProItem::ReturnFalse;
+ }
+ return ProItem::ReturnReturn;
+ case T_EXPORT:
+ if (m_skipLevel && !m_cumulative)
+ return ProItem::ReturnTrue;
+ if (args.count() != 1) {
+ q->logMessage(format("export(variable) requires one argument."));
+ return ProItem::ReturnFalse;
+ }
+ for (int i = 0; i < m_valuemapStack.size(); ++i) {
+ m_valuemapStack[i][args[0]] = m_valuemap[args[0]];
+ m_filevaluemapStack[i][currentProFile()][args[0]] =
+ m_filevaluemap[currentProFile()][args[0]];
+ }
+ return ProItem::ReturnTrue;
+#if 0
+ case T_INFILE:
+ case T_REQUIRES:
+ case T_EVAL:
+#endif
+ case T_FOR: {
+ if (m_cumulative) // This is a no-win situation, so just pretend it's no loop
+ return ProItem::ReturnTrue;
+ if (m_skipLevel)
+ return ProItem::ReturnFalse;
+ if (args.count() > 2 || args.count() < 1) {
+ q->logMessage(format("for({var, list|var, forever|ever})"
+ " requires one or two arguments."));
+ return ProItem::ReturnFalse;
+ }
+ ProLoop loop;
+ loop.infinite = false;
+ loop.index = 0;
+ QString it_list;
+ if (args.count() == 1) {
+ doVariableReplace(&args[0]);
+ it_list = args[0];
+ if (args[0] != QLatin1String("ever")) {
+ q->logMessage(format("for({var, list|var, forever|ever})"
+ " requires one or two arguments."));
+ return ProItem::ReturnFalse;
+ }
+ it_list = QLatin1String("forever");
+ } else {
+ loop.variable = args[0];
+ loop.oldVarVal = m_valuemap.value(loop.variable);
+ doVariableReplace(&args[1]);
+ it_list = args[1];
+ }
+ loop.list = m_valuemap[it_list];
+ if (loop.list.isEmpty()) {
+ if (it_list == QLatin1String("forever")) {
+ loop.infinite = true;
+ } else {
+ int dotdot = it_list.indexOf(QLatin1String(".."));
+ if (dotdot != -1) {
+ bool ok;
+ int start = it_list.left(dotdot).toInt(&ok);
+ if (ok) {
+ int end = it_list.mid(dotdot+2).toInt(&ok);
+ if (ok) {
+ if (start < end) {
+ for (int i = start; i <= end; i++)
+ loop.list << QString::number(i);
+ } else {
+ for (int i = start; i >= end; i--)
+ loop.list << QString::number(i);
+ }
+ }
+ }
+ }
+ }
+ }
+ m_loopStack.push(loop);
+ m_sts.condition = true;
+ return ProItem::ReturnLoop;
+ }
+ case T_BREAK:
+ if (m_skipLevel)
+ return ProItem::ReturnFalse;
+ if (!m_loopStack.isEmpty())
+ return ProItem::ReturnBreak;
+ // ### missing: breaking out of multiline blocks
+ q->logMessage(format("unexpected break()."));
+ return ProItem::ReturnFalse;
+ case T_NEXT:
+ if (m_skipLevel)
+ return ProItem::ReturnFalse;
+ if (!m_loopStack.isEmpty())
+ return ProItem::ReturnNext;
+ q->logMessage(format("unexpected next()."));
+ return ProItem::ReturnFalse;
+ case T_IF: {
+ if (args.count() != 1) {
+ q->logMessage(format("if(condition) requires one argument."));
+ return ProItem::ReturnFalse;
+ }
+ QString cond = args.first();
+ bool escaped = false; // This is more than qmake does
+ bool quoted = false;
+ bool ret = true;
+ bool orOp = false;
+ bool invert = false;
+ bool isFunc = false;
+ int parens = 0;
+ QString test;
+ test.reserve(20);
+ QString args;
+ args.reserve(50);
+ const QChar *d = cond.unicode();
+ const QChar *ed = d + cond.length();
+ while (d < ed) {
+ ushort c = (d++)->unicode();
+ if (!escaped) {
+ if (c == '\\') {
+ escaped = true;
+ args += c; // Assume no-one quotes the test name
+ continue;
+ } else if (c == '"') {
+ quoted = !quoted;
+ args += c; // Ditto
+ continue;
+ }
+ } else {
+ escaped = false;
+ }
+ if (quoted) {
+ args += c; // Ditto
+ } else {
+ bool isOp = false;
+ if (c == '(') {
+ isFunc = true;
+ if (parens)
+ args += c;
+ ++parens;
+ } else if (c == ')') {
+ --parens;
+ if (parens)
+ args += c;
+ } else if (!parens) {
+ if (c == ':' || c == '|')
+ isOp = true;
+ else if (c == '!')
+ invert = true;
+ else
+ test += c;
+ } else {
+ args += c;
+ }
+ if (!parens && (isOp || d == ed)) {
+ // Yes, qmake doesn't shortcut evaluations here. We can't, either,
+ // as some test functions have side effects.
+ bool success;
+ if (isFunc) {
+ success = evaluateConditionalFunction(test, args);
+ } else {
+ success = isActiveConfig(test, true);
+ }
+ success ^= invert;
+ if (orOp)
+ ret |= success;
+ else
+ ret &= success;
+ orOp = (c == '|');
+ invert = false;
+ isFunc = false;
+ test.clear();
+ args.clear();
+ }
+ }
+ }
+ return returnBool(ret);
+ }
+ case T_CONFIG: {
+ if (args.count() < 1 || args.count() > 2) {
+ q->logMessage(format("CONFIG(config) requires one or two arguments."));
+ return ProItem::ReturnFalse;
+ }
+ if (args.count() == 1) {
+ //cond = isActiveConfig(args.first()); XXX
+ return ProItem::ReturnFalse;
+ }
+ const QStringList mutuals = args[1].split(QLatin1Char('|'));
+ const QStringList &configs = valuesDirect(QLatin1String("CONFIG"));
+ for (int i = configs.size() - 1; i >= 0; i--) {
+ for (int mut = 0; mut < mutuals.count(); mut++) {
+ if (configs[i] == mutuals[mut].trimmed()) {
+ return returnBool(configs[i] == args[0]);
+ }
+ }
+ }
+ return ProItem::ReturnFalse;
+ }
+ case T_CONTAINS: {
+ if (args.count() < 2 || args.count() > 3) {
+ q->logMessage(format("contains(var, val) requires two or three arguments."));
+ return ProItem::ReturnFalse;
+ }
+
+ QRegExp regx(args[1]);
+ const QStringList &l = values(args.first());
+ if (args.count() == 2) {
+ for (int i = 0; i < l.size(); ++i) {
+ const QString val = l[i];
+ if (regx.exactMatch(val) || val == args[1]) {
+ return ProItem::ReturnTrue;
+ }
+ }
+ } else {
+ const QStringList mutuals = args[2].split(QLatin1Char('|'));
+ for (int i = l.size() - 1; i >= 0; i--) {
+ const QString val = l[i];
+ for (int mut = 0; mut < mutuals.count(); mut++) {
+ if (val == mutuals[mut].trimmed()) {
+ return returnBool(regx.exactMatch(val) || val == args[1]);
+ }
+ }
+ }
+ }
+ return ProItem::ReturnFalse;
+ }
+ case T_COUNT: {
+ if (args.count() != 2 && args.count() != 3) {
+ q->logMessage(format("count(var, count, op=\"equals\") requires two or three arguments."));
+ return ProItem::ReturnFalse;
+ }
+ if (args.count() == 3) {
+ QString comp = args[2];
+ if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) {
+ return returnBool(values(args.first()).count() > args[1].toInt());
+ } else if (comp == QLatin1String(">=")) {
+ return returnBool(values(args.first()).count() >= args[1].toInt());
+ } else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) {
+ return returnBool(values(args.first()).count() < args[1].toInt());
+ } else if (comp == QLatin1String("<=")) {
+ return returnBool(values(args.first()).count() <= args[1].toInt());
+ } else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual")
+ || comp == QLatin1String("=") || comp == QLatin1String("==")) {
+ return returnBool(values(args.first()).count() == args[1].toInt());
+ } else {
+ q->logMessage(format("unexpected modifier to count(%2)").arg(comp));
+ return ProItem::ReturnFalse;
+ }
+ }
+ return returnBool(values(args.first()).count() == args[1].toInt());
+ }
+ case T_GREATERTHAN:
+ case T_LESSTHAN: {
+ if (args.count() != 2) {
+ q->logMessage(format("%1(variable, value) requires two arguments.").arg(function));
+ return ProItem::ReturnFalse;
+ }
+ QString rhs(args[1]), lhs(values(args[0]).join(QString(Option::field_sep)));
+ bool ok;
+ int rhs_int = rhs.toInt(&ok);
+ if (ok) { // do integer compare
+ int lhs_int = lhs.toInt(&ok);
+ if (ok) {
+ if (func_t == T_GREATERTHAN)
+ return returnBool(lhs_int > rhs_int);
+ return returnBool(lhs_int < rhs_int);
+ }
+ }
+ if (func_t == T_GREATERTHAN)
+ return returnBool(lhs > rhs);
+ return returnBool(lhs < rhs);
+ }
+ case T_EQUALS:
+ if (args.count() != 2) {
+ q->logMessage(format("%1(variable, value) requires two arguments.").arg(function));
+ return ProItem::ReturnFalse;
+ }
+ return returnBool(values(args[0]).join(QString(Option::field_sep)) == args[1]);
+ case T_CLEAR: {
+ if (m_skipLevel && !m_cumulative)
+ return ProItem::ReturnFalse;
+ if (args.count() != 1) {
+ q->logMessage(format("%1(variable) requires one argument.").arg(function));
+ return ProItem::ReturnFalse;
+ }
+ QHash<QString, QStringList>::Iterator it = m_valuemap.find(args[0]);
+ if (it == m_valuemap.end())
+ return ProItem::ReturnFalse;
+ it->clear();
+ return ProItem::ReturnTrue;
+ }
+ case T_UNSET: {
+ if (m_skipLevel && !m_cumulative)
+ return ProItem::ReturnFalse;
+ if (args.count() != 1) {
+ q->logMessage(format("%1(variable) requires one argument.").arg(function));
+ return ProItem::ReturnFalse;
+ }
+ QHash<QString, QStringList>::Iterator it = m_valuemap.find(args[0]);
+ if (it == m_valuemap.end())
+ return ProItem::ReturnFalse;
+ m_valuemap.erase(it);
+ return ProItem::ReturnTrue;
+ }
+ case T_INCLUDE: {
+ if (m_skipLevel && !m_cumulative)
+ return ProItem::ReturnFalse;
+ QString parseInto;
+ // the third optional argument to include() controls warnings
+ // and is not used here
+ if ((args.count() == 2) || (args.count() == 3)) {
+ parseInto = args[1];
+ } else if (args.count() != 1) {
+ q->logMessage(format("include(file) requires one, two or three arguments."));
+ return ProItem::ReturnFalse;
+ }
+ QString fileName = args.first();
+ // ### this breaks if we have include(c:/reallystupid.pri) but IMHO that's really bad style.
+ QDir currentProPath(currentDirectory());
+ fileName = QDir::cleanPath(currentProPath.absoluteFilePath(fileName));
+ State sts = m_sts;
+ bool ok = evaluateFile(fileName);
+ m_sts = sts;
+ return returnBool(ok);
+ }
+ case T_LOAD: {
+ if (m_skipLevel && !m_cumulative)
+ return ProItem::ReturnFalse;
+ QString parseInto;
+ bool ignore_error = false;
+ if (args.count() == 2) {
+ QString sarg = args[1];
+ ignore_error = (!sarg.compare(QLatin1String("true"), Qt::CaseInsensitive) || sarg.toInt());
+ } else if (args.count() != 1) {
+ q->logMessage(format("load(feature) requires one or two arguments."));
+ return ProItem::ReturnFalse;
+ }
+ // XXX ignore_error unused
+ return returnBool(evaluateFeatureFile(args.first()));
+ }
+ case T_DEBUG:
+ // Yup - do nothing. Nothing is going to enable debug output anyway.
+ return ProItem::ReturnFalse;
+ case T_MESSAGE: {
+ if (args.count() != 1) {
+ q->logMessage(format("%1(message) requires one argument.").arg(function));
+ return ProItem::ReturnFalse;
+ }
+ QString msg = fixEnvVariables(args.first());
+ q->fileMessage(QString::fromLatin1("Project %1: %2").arg(function.toUpper(), msg));
+ // ### Consider real termination in non-cumulative mode
+ return returnBool(function != QLatin1String("error"));
+ }
+#if 0 // Way too dangerous to enable.
+ case T_SYSTEM: {
+ if (args.count() != 1) {
+ q->logMessage(format("system(exec) requires one argument."));
+ ProItem::ReturnFalse;
+ }
+ return returnBool(system(args.first().toLatin1().constData()) == 0);
+ }
+#endif
+ case T_ISEMPTY: {
+ if (args.count() != 1) {
+ q->logMessage(format("isEmpty(var) requires one argument."));
+ return ProItem::ReturnFalse;
+ }
+ QStringList sl = values(args.first());
+ if (sl.count() == 0) {
+ return ProItem::ReturnTrue;
+ } else if (sl.count() > 0) {
+ QString var = sl.first();
+ if (var.isEmpty())
+ return ProItem::ReturnTrue;
+ }
+ return ProItem::ReturnFalse;
+ }
+ case T_EXISTS: {
+ if (args.count() != 1) {
+ q->logMessage(format("exists(file) requires one argument."));
+ return ProItem::ReturnFalse;
+ }
+ QString file = args.first();
+ file = Option::fixPathToLocalOS(file);
+
+ if (QFile::exists(file)) {
+ return ProItem::ReturnTrue;
+ }
+ //regular expression I guess
+ QString dirstr = currentDirectory();
+ int slsh = file.lastIndexOf(Option::dir_sep);
+ if (slsh != -1) {
+ dirstr = file.left(slsh+1);
+ file = file.right(file.length() - slsh - 1);
+ }
+ if (file.contains(QLatin1Char('*')) || file.contains(QLatin1Char('?')))
+ if (!QDir(dirstr).entryList(QStringList(file)).isEmpty())
+ return ProItem::ReturnTrue;
+
+ return ProItem::ReturnFalse;
+ }
+ case 0:
+ q->logMessage(format("'%1' is not a recognized test function").arg(function));
+ return ProItem::ReturnFalse;
+ default:
+ q->logMessage(format("Function '%1' is not implemented").arg(function));
+ return ProItem::ReturnFalse;
+ }
+}
+
+QStringList ProFileEvaluator::Private::values(const QString &variableName,
+ const QHash<QString, QStringList> &place,
+ const ProFile *pro) const
+{
+ if (variableName == QLatin1String("LITERAL_WHITESPACE")) //a real space in a token
+ return QStringList(QLatin1String("\t"));
+ if (variableName == QLatin1String("LITERAL_DOLLAR")) //a real $
+ return QStringList(QLatin1String("$"));
+ if (variableName == QLatin1String("LITERAL_HASH")) //a real #
+ return QStringList(QLatin1String("#"));
+ if (variableName == QLatin1String("OUT_PWD")) //the out going dir
+ return QStringList(m_outputDir);
+ if (variableName == QLatin1String("PWD") || //current working dir (of _FILE_)
+ variableName == QLatin1String("IN_PWD"))
+ return QStringList(currentDirectory());
+ if (variableName == QLatin1String("DIR_SEPARATOR"))
+ return QStringList(Option::dir_sep);
+ if (variableName == QLatin1String("DIRLIST_SEPARATOR"))
+ return QStringList(Option::dirlist_sep);
+ if (variableName == QLatin1String("_LINE_")) //parser line number
+ return QStringList(QString::number(m_lineNo));
+ if (variableName == QLatin1String("_FILE_")) //parser file; qmake is a bit weird here
+ return QStringList(m_profileStack.size() == 1 ? pro->fileName() : QFileInfo(pro->fileName()).fileName());
+ if (variableName == QLatin1String("_DATE_")) //current date/time
+ return QStringList(QDateTime::currentDateTime().toString());
+ if (variableName == QLatin1String("_PRO_FILE_"))
+ return QStringList(m_origfile);
+ if (variableName == QLatin1String("_PRO_FILE_PWD_"))
+ return QStringList(QFileInfo(m_origfile).absolutePath());
+ if (variableName == QLatin1String("_QMAKE_CACHE_"))
+ return QStringList(); // FIXME?
+ if (variableName.startsWith(QLatin1String("QMAKE_HOST."))) {
+ QString ret, type = variableName.mid(11);
+#if defined(Q_OS_WIN32)
+ if (type == QLatin1String("os")) {
+ ret = QLatin1String("Windows");
+ } else if (type == QLatin1String("name")) {
+ DWORD name_length = 1024;
+ wchar_t name[1024];
+ if (GetComputerName(name, &name_length))
+ ret = QString::fromWCharArray(name);
+ } else if (type == QLatin1String("version") || type == QLatin1String("version_string")) {
+ QSysInfo::WinVersion ver = QSysInfo::WindowsVersion;
+ if (type == QLatin1String("version"))
+ ret = QString::number(ver);
+ else if (ver == QSysInfo::WV_Me)
+ ret = QLatin1String("WinMe");
+ else if (ver == QSysInfo::WV_95)
+ ret = QLatin1String("Win95");
+ else if (ver == QSysInfo::WV_98)
+ ret = QLatin1String("Win98");
+ else if (ver == QSysInfo::WV_NT)
+ ret = QLatin1String("WinNT");
+ else if (ver == QSysInfo::WV_2000)
+ ret = QLatin1String("Win2000");
+ else if (ver == QSysInfo::WV_2000)
+ ret = QLatin1String("Win2003");
+ else if (ver == QSysInfo::WV_XP)
+ ret = QLatin1String("WinXP");
+ else if (ver == QSysInfo::WV_VISTA)
+ ret = QLatin1String("WinVista");
+ else
+ ret = QLatin1String("Unknown");
+ } else if (type == QLatin1String("arch")) {
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+ switch(info.wProcessorArchitecture) {
+#ifdef PROCESSOR_ARCHITECTURE_AMD64
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ ret = QLatin1String("x86_64");
+ break;
+#endif
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ ret = QLatin1String("x86");
+ break;
+ case PROCESSOR_ARCHITECTURE_IA64:
+#ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
+ case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
+#endif
+ ret = QLatin1String("IA64");
+ break;
+ default:
+ ret = QLatin1String("Unknown");
+ break;
+ }
+ }
+#elif defined(Q_OS_UNIX)
+ struct utsname name;
+ if (!uname(&name)) {
+ if (type == QLatin1String("os"))
+ ret = QString::fromLatin1(name.sysname);
+ else if (type == QLatin1String("name"))
+ ret = QString::fromLatin1(name.nodename);
+ else if (type == QLatin1String("version"))
+ ret = QString::fromLatin1(name.release);
+ else if (type == QLatin1String("version_string"))
+ ret = QString::fromLatin1(name.version);
+ else if (type == QLatin1String("arch"))
+ ret = QString::fromLatin1(name.machine);
+ }
+#endif
+ return QStringList(ret);
+ }
+
+ QStringList result = place[variableName];
+ if (result.isEmpty()) {
+ if (variableName == QLatin1String("TARGET")) {
+ result.append(QFileInfo(m_origfile).baseName());
+ } else if (variableName == QLatin1String("TEMPLATE")) {
+ result.append(QLatin1String("app"));
+ } else if (variableName == QLatin1String("QMAKE_DIR_SEP")) {
+ result.append(Option::dirlist_sep);
+ }
+ }
+ return result;
+}
+
+QStringList ProFileEvaluator::Private::values(const QString &variableName) const
+{
+ return values(variableName, m_valuemap, currentProFile());
+}
+
+QStringList ProFileEvaluator::Private::values(const QString &variableName, const ProFile *pro) const
+{
+ return values(variableName, m_filevaluemap[pro], pro);
+}
+
+ProFile *ProFileEvaluator::parsedProFile(const QString &fileName)
+{
+ QFileInfo fi(fileName);
+ if (fi.exists()) {
+ QString fn = QDir::cleanPath(fi.absoluteFilePath());
+ foreach (const ProFile *pf, d->m_profileStack)
+ if (pf->fileName() == fn) {
+ errorMessage(d->format("circular inclusion of %1").arg(fn));
+ return 0;
+ }
+ ProFile *pro = new ProFile(fn);
+ if (d->read(pro))
+ return pro;
+ delete pro;
+ }
+ return 0;
+}
+
+void ProFileEvaluator::releaseParsedProFile(ProFile *proFile)
+{
+ delete proFile;
+}
+
+bool ProFileEvaluator::Private::evaluateFile(const QString &fileName)
+{
+ ProFile *pro = q->parsedProFile(fileName);
+ if (pro) {
+ m_profileStack.push(pro);
+ bool ok = (pro->Accept(this) == ProItem::ReturnTrue);
+ m_profileStack.pop();
+ q->releaseParsedProFile(pro);
+ return ok;
+ } else {
+ return false;
+ }
+}
+
+bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName)
+{
+ QString fn;
+ foreach (const QString &path, qmakeFeaturePaths()) {
+ QString fname = path + QLatin1Char('/') + fileName;
+ if (QFileInfo(fname).exists()) {
+ fn = fname;
+ break;
+ }
+ fname += QLatin1String(".prf");
+ if (QFileInfo(fname).exists()) {
+ fn = fname;
+ break;
+ }
+ }
+ if (fn.isEmpty())
+ return false;
+ bool cumulative = m_cumulative;
+ m_cumulative = false;
+ bool ok = evaluateFile(fn);
+ m_cumulative = cumulative;
+ return ok;
+}
+
+QString ProFileEvaluator::Private::format(const char *fmt) const
+{
+ ProFile *pro = currentProFile();
+ QString fileName = pro ? pro->fileName() : QLatin1String("Not a file");
+ int lineNumber = pro ? m_lineNo : 0;
+ return QString::fromLatin1("%1(%2):").arg(fileName).arg(lineNumber) + QString::fromAscii(fmt);
+}
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// ProFileEvaluator
+//
+///////////////////////////////////////////////////////////////////////
+
+ProFileEvaluator::ProFileEvaluator()
+ : d(new Private(this))
+{
+ Option::init();
+}
+
+ProFileEvaluator::~ProFileEvaluator()
+{
+ delete d;
+}
+
+bool ProFileEvaluator::contains(const QString &variableName) const
+{
+ return d->m_valuemap.contains(variableName);
+}
+
+inline QStringList fixEnvVariables(const QStringList &x)
+{
+ QStringList ret;
+ foreach (const QString &str, x)
+ ret << Option::fixString(str, Option::FixEnvVars);
+ return ret;
+}
+
+
+QStringList ProFileEvaluator::values(const QString &variableName) const
+{
+ return fixEnvVariables(d->values(variableName));
+}
+
+QStringList ProFileEvaluator::values(const QString &variableName, const ProFile *pro) const
+{
+ return fixEnvVariables(d->values(variableName, pro));
+}
+
+QStringList ProFileEvaluator::absolutePathValues(
+ const QString &variable, const QString &baseDirectory) const
+{
+ QStringList result;
+ foreach (const QString &el, values(variable)) {
+ const QFileInfo info = QFileInfo(baseDirectory, el);
+ if (info.isDir())
+ result << QDir::cleanPath(info.absoluteFilePath());
+ }
+ return result;
+}
+
+QStringList ProFileEvaluator::absoluteFileValues(
+ const QString &variable, const QString &baseDirectory, const QStringList &searchDirs,
+ const ProFile *pro) const
+{
+ QStringList result;
+ foreach (const QString &el, pro ? values(variable, pro) : values(variable)) {
+ QFileInfo info(el);
+ if (info.isAbsolute()) {
+ if (info.exists()) {
+ result << QDir::cleanPath(el);
+ goto next;
+ }
+ } else {
+ foreach (const QString &dir, searchDirs) {
+ QFileInfo info(dir, el);
+ if (info.isFile()) {
+ result << QDir::cleanPath(info.filePath());
+ goto next;
+ }
+ }
+ if (baseDirectory.isEmpty())
+ goto next;
+ info = QFileInfo(baseDirectory, el);
+ }
+ {
+ QFileInfo baseInfo(info.absolutePath());
+ if (baseInfo.exists()) {
+ QString wildcard = info.fileName();
+ if (wildcard.contains(QLatin1Char('*')) || wildcard.contains(QLatin1Char('?'))) {
+ QDir theDir(QDir::cleanPath(baseInfo.filePath()));
+ foreach (const QString &fn, theDir.entryList(QStringList(wildcard)))
+ if (fn != QLatin1String(".") && fn != QLatin1String(".."))
+ result << theDir.absoluteFilePath(fn);
+ }
+ }
+ }
+ next: ;
+ }
+ return result;
+}
+
+ProFileEvaluator::TemplateType ProFileEvaluator::templateType()
+{
+ QStringList templ = values(QLatin1String("TEMPLATE"));
+ if (templ.count() >= 1) {
+ const QString &t = templ.last();
+ if (!t.compare(QLatin1String("app"), Qt::CaseInsensitive))
+ return TT_Application;
+ if (!t.compare(QLatin1String("lib"), Qt::CaseInsensitive))
+ return TT_Library;
+ if (!t.compare(QLatin1String("script"), Qt::CaseInsensitive))
+ return TT_Script;
+ if (!t.compare(QLatin1String("subdirs"), Qt::CaseInsensitive))
+ return TT_Subdirs;
+ }
+ return TT_Unknown;
+}
+
+bool ProFileEvaluator::queryProFile(ProFile *pro)
+{
+ return d->read(pro);
+}
+
+bool ProFileEvaluator::accept(ProFile *pro)
+{
+ return pro->Accept(d);
+}
+
+QString ProFileEvaluator::propertyValue(const QString &name) const
+{
+ return d->propertyValue(name);
+}
+
+namespace {
+ template<class K, class T> void insert(QHash<K,T> *out, const QHash<K,T> &in)
+ {
+ typename QHash<K,T>::const_iterator i = in.begin();
+ while (i != in.end()) {
+ out->insert(i.key(), i.value());
+ ++i;
+ }
+ }
+} // anon namespace
+
+void ProFileEvaluator::addVariables(const QHash<QString, QStringList> &variables)
+{
+ insert(&(d->m_valuemap), variables);
+}
+
+void ProFileEvaluator::addProperties(const QHash<QString, QString> &properties)
+{
+ insert(&(d->m_properties), properties);
+}
+
+void ProFileEvaluator::logMessage(const QString &message)
+{
+ if (d->m_verbose && !d->m_skipLevel)
+ fprintf(stderr, "%s\n", qPrintable(message));
+}
+
+void ProFileEvaluator::fileMessage(const QString &message)
+{
+ if (!d->m_skipLevel)
+ fprintf(stderr, "%s\n", qPrintable(message));
+}
+
+void ProFileEvaluator::errorMessage(const QString &message)
+{
+ if (!d->m_skipLevel)
+ fprintf(stderr, "%s\n", qPrintable(message));
+}
+
+void ProFileEvaluator::setVerbose(bool on)
+{
+ d->m_verbose = on;
+}
+
+void ProFileEvaluator::setCumulative(bool on)
+{
+ d->m_cumulative = on;
+}
+
+void ProFileEvaluator::setOutputDir(const QString &dir)
+{
+ d->m_outputDir = dir;
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/shared/profileevaluator.h b/src/linguist/shared/profileevaluator.h
new file mode 100644
index 000000000..3bceec108
--- /dev/null
+++ b/src/linguist/shared/profileevaluator.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** 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 PROFILEEVALUATOR_H
+#define PROFILEEVALUATOR_H
+
+#include "proitems.h"
+#include "abstractproitemvisitor.h"
+
+#include <QtCore/QIODevice>
+#include <QtCore/QHash>
+#include <QtCore/QStringList>
+#include <QtCore/QStack>
+
+#if (!defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)) && !defined(__SUNPRO_CC)
+# define HAVE_TEMPLATE_CLASS_FRIENDS
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class ProFileEvaluator
+{
+#ifdef HAVE_TEMPLATE_CLASS_FRIENDS
+private:
+#else
+public:
+#endif
+ class Private;
+
+public:
+ enum TemplateType {
+ TT_Unknown = 0,
+ TT_Application,
+ TT_Library,
+ TT_Script,
+ TT_Subdirs
+ };
+
+ ProFileEvaluator();
+ virtual ~ProFileEvaluator();
+
+ ProFileEvaluator::TemplateType templateType();
+ virtual bool contains(const QString &variableName) const;
+ void setVerbose(bool on); // Default is false
+ void setCumulative(bool on); // Default is true!
+ void setOutputDir(const QString &dir); // Default is empty
+
+ bool queryProFile(ProFile *pro);
+ bool accept(ProFile *pro);
+
+ void addVariables(const QHash<QString, QStringList> &variables);
+ void addProperties(const QHash<QString, QString> &properties);
+ QStringList values(const QString &variableName) const;
+ QStringList values(const QString &variableName, const ProFile *pro) const;
+ QStringList absolutePathValues(const QString &variable, const QString &baseDirectory) const;
+ QStringList absoluteFileValues(
+ const QString &variable, const QString &baseDirectory, const QStringList &searchDirs,
+ const ProFile *pro) const;
+ QString propertyValue(const QString &val) const;
+
+ // for our descendents
+ virtual ProFile *parsedProFile(const QString &fileName);
+ virtual void releaseParsedProFile(ProFile *proFile);
+ virtual void logMessage(const QString &msg);
+ virtual void errorMessage(const QString &msg); // .pro parse errors
+ virtual void fileMessage(const QString &msg); // error() and message() from .pro file
+
+private:
+ Private *d;
+
+#ifdef HAVE_TEMPLATE_CLASS_FRIENDS
+ template<typename T> friend class QTypeInfo;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // PROFILEEVALUATOR_H
diff --git a/src/linguist/shared/proitems.cpp b/src/linguist/shared/proitems.cpp
new file mode 100644
index 000000000..934b43190
--- /dev/null
+++ b/src/linguist/shared/proitems.cpp
@@ -0,0 +1,358 @@
+/****************************************************************************
+**
+** 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 "proitems.h"
+#include "abstractproitemvisitor.h"
+
+#include <QtCore/QFileInfo>
+
+QT_BEGIN_NAMESPACE
+
+// --------------- ProItem ------------
+void ProItem::setComment(const QString &comment)
+{
+ m_comment = comment;
+}
+
+QString ProItem::comment() const
+{
+ return m_comment;
+}
+
+// --------------- ProBlock ----------------
+
+ProBlock::ProBlock(ProBlock *parent)
+{
+ m_blockKind = 0;
+ m_parent = parent;
+ m_refCount = 1;
+}
+
+ProBlock::~ProBlock()
+{
+ foreach (ProItem *itm, m_proitems)
+ if (itm->kind() == BlockKind)
+ static_cast<ProBlock *>(itm)->deref();
+ else
+ delete itm;
+}
+
+void ProBlock::appendItem(ProItem *proitem)
+{
+ m_proitems << proitem;
+}
+
+void ProBlock::setItems(const QList<ProItem *> &proitems)
+{
+ m_proitems = proitems;
+}
+
+QList<ProItem *> ProBlock::items() const
+{
+ return m_proitems;
+}
+
+void ProBlock::setBlockKind(int blockKind)
+{
+ m_blockKind = blockKind;
+}
+
+int ProBlock::blockKind() const
+{
+ return m_blockKind;
+}
+
+void ProBlock::setParent(ProBlock *parent)
+{
+ m_parent = parent;
+}
+
+ProBlock *ProBlock::parent() const
+{
+ return m_parent;
+}
+
+ProItem::ProItemKind ProBlock::kind() const
+{
+ return ProItem::BlockKind;
+}
+
+ProItem::ProItemReturn ProBlock::Accept(AbstractProItemVisitor *visitor)
+{
+ if (visitor->visitBeginProBlock(this) == ReturnSkip)
+ return ReturnTrue;
+ ProItemReturn rt = ReturnTrue;
+ for (int i = 0; i < m_proitems.count(); ++i) {
+ rt = m_proitems.at(i)->Accept(visitor);
+ if (rt != ReturnTrue && rt != ReturnFalse) {
+ if (rt == ReturnLoop) {
+ rt = ReturnTrue;
+ while (visitor->visitProLoopIteration())
+ for (int j = i; ++j < m_proitems.count(); ) {
+ rt = m_proitems.at(j)->Accept(visitor);
+ if (rt != ReturnTrue && rt != ReturnFalse) {
+ if (rt == ReturnNext) {
+ rt = ReturnTrue;
+ break;
+ }
+ if (rt == ReturnBreak)
+ rt = ReturnTrue;
+ goto do_break;
+ }
+ }
+ do_break:
+ visitor->visitProLoopCleanup();
+ }
+ break;
+ }
+ }
+ visitor->visitEndProBlock(this);
+ return rt;
+}
+
+// --------------- ProVariable ----------------
+ProVariable::ProVariable(const QString &name, ProBlock *parent)
+ : ProBlock(parent)
+{
+ setBlockKind(ProBlock::VariableKind);
+ m_variable = name;
+ m_variableKind = SetOperator;
+}
+
+void ProVariable::setVariableOperator(VariableOperator variableKind)
+{
+ m_variableKind = variableKind;
+}
+
+ProVariable::VariableOperator ProVariable::variableOperator() const
+{
+ return m_variableKind;
+}
+
+void ProVariable::setVariable(const QString &name)
+{
+ m_variable = name;
+}
+
+QString ProVariable::variable() const
+{
+ return m_variable;
+}
+
+ProItem::ProItemReturn ProVariable::Accept(AbstractProItemVisitor *visitor)
+{
+ visitor->visitBeginProVariable(this);
+ foreach (ProItem *item, m_proitems)
+ item->Accept(visitor); // cannot fail
+ visitor->visitEndProVariable(this);
+ return ReturnTrue;
+}
+
+// --------------- ProValue ----------------
+ProValue::ProValue(const QString &value, ProVariable *variable)
+{
+ m_variable = variable;
+ m_value = value;
+}
+
+void ProValue::setValue(const QString &value)
+{
+ m_value = value;
+}
+
+QString ProValue::value() const
+{
+ return m_value;
+}
+
+void ProValue::setVariable(ProVariable *variable)
+{
+ m_variable = variable;
+}
+
+ProVariable *ProValue::variable() const
+{
+ return m_variable;
+}
+
+ProItem::ProItemKind ProValue::kind() const
+{
+ return ProItem::ValueKind;
+}
+
+ProItem::ProItemReturn ProValue::Accept(AbstractProItemVisitor *visitor)
+{
+ visitor->visitProValue(this);
+ return ReturnTrue;
+}
+
+// --------------- ProFunction ----------------
+ProFunction::ProFunction(const QString &text)
+{
+ m_text = text;
+}
+
+void ProFunction::setText(const QString &text)
+{
+ m_text = text;
+}
+
+QString ProFunction::text() const
+{
+ return m_text;
+}
+
+ProItem::ProItemKind ProFunction::kind() const
+{
+ return ProItem::FunctionKind;
+}
+
+ProItem::ProItemReturn ProFunction::Accept(AbstractProItemVisitor *visitor)
+{
+ return visitor->visitProFunction(this);
+}
+
+// --------------- ProCondition ----------------
+ProCondition::ProCondition(const QString &text)
+{
+ m_text = text;
+}
+
+void ProCondition::setText(const QString &text)
+{
+ m_text = text;
+}
+
+QString ProCondition::text() const
+{
+ return m_text;
+}
+
+ProItem::ProItemKind ProCondition::kind() const
+{
+ return ProItem::ConditionKind;
+}
+
+ProItem::ProItemReturn ProCondition::Accept(AbstractProItemVisitor *visitor)
+{
+ visitor->visitProCondition(this);
+ return ReturnTrue;
+}
+
+// --------------- ProOperator ----------------
+ProOperator::ProOperator(OperatorKind operatorKind)
+{
+ m_operatorKind = operatorKind;
+}
+
+void ProOperator::setOperatorKind(OperatorKind operatorKind)
+{
+ m_operatorKind = operatorKind;
+}
+
+ProOperator::OperatorKind ProOperator::operatorKind() const
+{
+ return m_operatorKind;
+}
+
+ProItem::ProItemKind ProOperator::kind() const
+{
+ return ProItem::OperatorKind;
+}
+
+ProItem::ProItemReturn ProOperator::Accept(AbstractProItemVisitor *visitor)
+{
+ visitor->visitProOperator(this);
+ return ReturnTrue;
+}
+
+// --------------- ProFile ----------------
+ProFile::ProFile(const QString &fileName)
+ : ProBlock(0)
+{
+ m_modified = false;
+ setBlockKind(ProBlock::ProFileKind);
+ m_fileName = fileName;
+
+ QFileInfo fi(fileName);
+ m_displayFileName = fi.fileName();
+ m_directoryName = fi.absolutePath();
+}
+
+ProFile::~ProFile()
+{
+}
+
+QString ProFile::displayFileName() const
+{
+ return m_displayFileName;
+}
+
+QString ProFile::fileName() const
+{
+ return m_fileName;
+}
+
+QString ProFile::directoryName() const
+{
+ return m_directoryName;
+}
+
+void ProFile::setModified(bool modified)
+{
+ m_modified = modified;
+}
+
+bool ProFile::isModified() const
+{
+ return m_modified;
+}
+
+ProItem::ProItemReturn ProFile::Accept(AbstractProItemVisitor *visitor)
+{
+ ProItemReturn rt;
+ if ((rt = visitor->visitBeginProFile(this)) != ReturnTrue)
+ return rt;
+ ProBlock::Accept(visitor); // cannot fail
+ return visitor->visitEndProFile(this);
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/shared/proitems.h b/src/linguist/shared/proitems.h
new file mode 100644
index 000000000..12fdb7e72
--- /dev/null
+++ b/src/linguist/shared/proitems.h
@@ -0,0 +1,248 @@
+/****************************************************************************
+**
+** 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 PROITEMS_H
+#define PROITEMS_H
+
+#include <QtCore/QString>
+#include <QtCore/QList>
+
+QT_BEGIN_NAMESPACE
+
+struct AbstractProItemVisitor;
+
+class ProItem
+{
+public:
+ enum ProItemKind {
+ ValueKind,
+ FunctionKind,
+ ConditionKind,
+ OperatorKind,
+ BlockKind
+ };
+
+ enum ProItemReturn {
+ ReturnFalse,
+ ReturnTrue,
+ ReturnBreak,
+ ReturnNext,
+ ReturnLoop,
+ ReturnSkip,
+ ReturnReturn
+ };
+
+ ProItem() : m_lineNumber(0) {}
+ virtual ~ProItem() {}
+
+ virtual ProItemKind kind() const = 0;
+
+ void setComment(const QString &comment);
+ QString comment() const;
+
+ virtual ProItemReturn Accept(AbstractProItemVisitor *visitor) = 0;
+ int lineNumber() const { return m_lineNumber; }
+ void setLineNumber(int lineNumber) { m_lineNumber = lineNumber; }
+
+private:
+ QString m_comment;
+ int m_lineNumber;
+};
+
+class ProBlock : public ProItem
+{
+public:
+ enum ProBlockKind {
+ NormalKind = 0x00,
+ ScopeKind = 0x01,
+ ScopeContentsKind = 0x02,
+ VariableKind = 0x04,
+ ProFileKind = 0x08,
+ FunctionBodyKind = 0x10,
+ SingleLine = 0x80
+ };
+
+ ProBlock(ProBlock *parent);
+ ~ProBlock();
+
+ void appendItem(ProItem *proitem);
+ void setItems(const QList<ProItem *> &proitems);
+ QList<ProItem *> items() const;
+
+ void setBlockKind(int blockKind);
+ int blockKind() const;
+
+ void setParent(ProBlock *parent);
+ ProBlock *parent() const;
+
+ void ref() { ++m_refCount; }
+ void deref() { if (!--m_refCount) delete this; }
+
+ ProItem::ProItemKind kind() const;
+
+ virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
+protected:
+ QList<ProItem *> m_proitems;
+private:
+ ProBlock *m_parent;
+ int m_blockKind;
+ int m_refCount;
+};
+
+class ProVariable : public ProBlock
+{
+public:
+ enum VariableOperator {
+ AddOperator = 0,
+ RemoveOperator = 1,
+ ReplaceOperator = 2,
+ SetOperator = 3,
+ UniqueAddOperator = 4
+ };
+
+ ProVariable(const QString &name, ProBlock *parent);
+
+ void setVariableOperator(VariableOperator variableKind);
+ VariableOperator variableOperator() const;
+
+ void setVariable(const QString &name);
+ QString variable() const;
+
+ virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
+private:
+ VariableOperator m_variableKind;
+ QString m_variable;
+};
+
+class ProValue : public ProItem
+{
+public:
+ ProValue(const QString &value, ProVariable *variable);
+
+ void setValue(const QString &value);
+ QString value() const;
+
+ void setVariable(ProVariable *variable);
+ ProVariable *variable() const;
+
+ ProItem::ProItemKind kind() const;
+
+ virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
+private:
+ QString m_value;
+ ProVariable *m_variable;
+};
+
+class ProFunction : public ProItem
+{
+public:
+ explicit ProFunction(const QString &text);
+
+ void setText(const QString &text);
+ QString text() const;
+
+ ProItem::ProItemKind kind() const;
+
+ virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
+private:
+ QString m_text;
+};
+
+class ProCondition : public ProItem
+{
+public:
+ explicit ProCondition(const QString &text);
+
+ void setText(const QString &text);
+ QString text() const;
+
+ ProItem::ProItemKind kind() const;
+
+ virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
+private:
+ QString m_text;
+};
+
+class ProOperator : public ProItem
+{
+public:
+ enum OperatorKind {
+ OrOperator = 1,
+ NotOperator = 2
+ };
+
+ explicit ProOperator(OperatorKind operatorKind);
+
+ void setOperatorKind(OperatorKind operatorKind);
+ OperatorKind operatorKind() const;
+
+ ProItem::ProItemKind kind() const;
+
+ virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
+private:
+ OperatorKind m_operatorKind;
+};
+
+class ProFile : public ProBlock
+{
+public:
+ explicit ProFile(const QString &fileName);
+ ~ProFile();
+
+ QString displayFileName() const;
+ QString fileName() const;
+ QString directoryName() const;
+
+ void setModified(bool modified);
+ bool isModified() const;
+
+ virtual ProItemReturn Accept(AbstractProItemVisitor *visitor);
+
+private:
+ QString m_fileName;
+ QString m_displayFileName;
+ QString m_directoryName;
+ bool m_modified;
+};
+
+QT_END_NAMESPACE
+
+#endif // PROITEMS_H
diff --git a/src/linguist/shared/proparser.pri b/src/linguist/shared/proparser.pri
new file mode 100644
index 000000000..372247e46
--- /dev/null
+++ b/src/linguist/shared/proparser.pri
@@ -0,0 +1,12 @@
+
+INCLUDEPATH *= $$PWD
+
+HEADERS += \
+ $$PWD/abstractproitemvisitor.h \
+ $$PWD/proitems.h \
+ $$PWD/profileevaluator.h \
+ $$PWD/proparserutils.h
+
+SOURCES += \
+ $$PWD/proitems.cpp \
+ $$PWD/profileevaluator.cpp
diff --git a/src/linguist/shared/proparserutils.h b/src/linguist/shared/proparserutils.h
new file mode 100644
index 000000000..b82398a4b
--- /dev/null
+++ b/src/linguist/shared/proparserutils.h
@@ -0,0 +1,324 @@
+/****************************************************************************
+**
+** 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 PROPARSERUTILS_H
+#define PROPARSERUTILS_H
+
+#include <QtCore/QDir>
+#ifndef QT_BOOTSTRAPPED
+#include <QtCore/QLibraryInfo>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_BOOTSTRAPPED
+// this is a stripped down version of the one found in QtCore
+class QLibraryInfo
+{
+public:
+ enum LibraryLocation
+ {
+ PrefixPath,
+ DocumentationPath,
+ HeadersPath,
+ LibrariesPath,
+ BinariesPath,
+ PluginsPath,
+ DataPath,
+ TranslationsPath,
+ SettingsPath,
+ DemosPath,
+ ExamplesPath
+ };
+ static QString location(LibraryLocation);
+};
+#endif
+
+// Pre- and postcondition macros
+#define PRE(cond) do {if (!(cond))qt_assert(#cond,__FILE__,__LINE__);} while (0)
+#define POST(cond) do {if (!(cond))qt_assert(#cond,__FILE__,__LINE__);} while (0)
+
+// This struct is from qmake, but we are not using everything.
+struct Option
+{
+ //simply global convenience
+ //static QString libtool_ext;
+ //static QString pkgcfg_ext;
+ //static QString prf_ext;
+ //static QString prl_ext;
+ //static QString ui_ext;
+ //static QStringList h_ext;
+ //static QStringList cpp_ext;
+ //static QString h_moc_ext;
+ //static QString cpp_moc_ext;
+ //static QString obj_ext;
+ //static QString lex_ext;
+ //static QString yacc_ext;
+ //static QString h_moc_mod;
+ //static QString cpp_moc_mod;
+ //static QString lex_mod;
+ //static QString yacc_mod;
+ static QString dir_sep;
+ static QString dirlist_sep;
+ static QString qmakespec;
+ static QChar field_sep;
+
+ enum TARG_MODE { TARG_UNIX_MODE, TARG_WIN_MODE, TARG_MACX_MODE, TARG_MAC9_MODE, TARG_QNX6_MODE };
+ static TARG_MODE target_mode;
+ //static QString pro_ext;
+ //static QString res_ext;
+
+ static void init()
+ {
+#ifdef Q_OS_WIN
+ Option::dirlist_sep = QLatin1Char(';');
+ Option::dir_sep = QLatin1Char('\\');
+#else
+ Option::dirlist_sep = QLatin1Char(':');
+ Option::dir_sep = QLatin1Char(QLatin1Char('/'));
+#endif
+ Option::qmakespec = QString::fromLatin1(qgetenv("QMAKESPEC").data());
+ Option::field_sep = QLatin1Char(' ');
+ }
+
+ enum StringFixFlags {
+ FixNone = 0x00,
+ FixEnvVars = 0x01,
+ FixPathCanonicalize = 0x02,
+ FixPathToLocalSeparators = 0x04,
+ FixPathToTargetSeparators = 0x08
+ };
+ static QString fixString(QString string, uchar flags);
+
+ inline static QString fixPathToLocalOS(const QString &in, bool fix_env = true, bool canonical = true)
+ {
+ uchar flags = FixPathToLocalSeparators;
+ if (fix_env)
+ flags |= FixEnvVars;
+ if (canonical)
+ flags |= FixPathCanonicalize;
+ return fixString(in, flags);
+ }
+};
+#if defined(Q_OS_WIN32)
+Option::TARG_MODE Option::target_mode = Option::TARG_WIN_MODE;
+#elif defined(Q_OS_MAC)
+Option::TARG_MODE Option::target_mode = Option::TARG_MACX_MODE;
+#elif defined(Q_OS_QNX6)
+Option::TARG_MODE Option::target_mode = Option::TARG_QNX6_MODE;
+#else
+Option::TARG_MODE Option::target_mode = Option::TARG_UNIX_MODE;
+#endif
+
+QString Option::qmakespec;
+QString Option::dirlist_sep;
+QString Option::dir_sep;
+QChar Option::field_sep;
+
+static void insertUnique(QHash<QString, QStringList> *map,
+ const QString &key, const QStringList &value)
+{
+ QStringList &sl = (*map)[key];
+ foreach (const QString &str, value)
+ if (!sl.contains(str))
+ sl.append(str);
+}
+
+static void removeEach(QHash<QString, QStringList> *map,
+ const QString &key, const QStringList &value)
+{
+ QStringList &sl = (*map)[key];
+ foreach (const QString &str, value)
+ sl.removeAll(str);
+}
+
+/*
+ See ProFileEvaluator::Private::visitProValue(...)
+
+static QStringList replaceInList(const QStringList &varList, const QRegExp &regexp,
+ const QString &replace, bool global)
+{
+ QStringList resultList = varList;
+
+ for (QStringList::Iterator varit = resultList.begin(); varit != resultList.end();) {
+ if (varit->contains(regexp)) {
+ *varit = varit->replace(regexp, replace);
+ if (varit->isEmpty())
+ varit = resultList.erase(varit);
+ else
+ ++varit;
+ if (!global)
+ break;
+ } else {
+ ++varit;
+ }
+ }
+ return resultList;
+}
+*/
+
+inline QString fixEnvVariables(const QString &x)
+{
+ return Option::fixString(x, Option::FixEnvVars);
+}
+
+inline QStringList splitPathList(const QString &paths)
+{
+ return paths.split(Option::dirlist_sep);
+}
+
+static QStringList split_arg_list(QString params)
+{
+ int quote = 0;
+ QStringList args;
+
+ const ushort LPAREN = '(';
+ const ushort RPAREN = ')';
+ const ushort SINGLEQUOTE = '\'';
+ const ushort DOUBLEQUOTE = '"';
+ const ushort COMMA = ',';
+ const ushort SPACE = ' ';
+ //const ushort TAB = '\t';
+
+ ushort unicode;
+ const QChar *params_data = params.data();
+ const int params_len = params.length();
+ int last = 0;
+ while (last < params_len && ((params_data+last)->unicode() == SPACE
+ /*|| (params_data+last)->unicode() == TAB*/))
+ ++last;
+ for (int x = last, parens = 0; x <= params_len; x++) {
+ unicode = (params_data+x)->unicode();
+ if (x == params_len) {
+ while (x && (params_data+(x-1))->unicode() == SPACE)
+ --x;
+ QString mid(params_data+last, x-last);
+ if (quote) {
+ if (mid[0] == quote && mid[(int)mid.length()-1] == quote)
+ mid = mid.mid(1, mid.length()-2);
+ quote = 0;
+ }
+ args << mid;
+ break;
+ }
+ if (unicode == LPAREN) {
+ --parens;
+ } else if (unicode == RPAREN) {
+ ++parens;
+ } else if (quote && unicode == quote) {
+ quote = 0;
+ } else if (!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) {
+ quote = unicode;
+ }
+ if (!parens && !quote && unicode == COMMA) {
+ QString mid = params.mid(last, x - last).trimmed();
+ args << mid;
+ last = x+1;
+ while (last < params_len && ((params_data+last)->unicode() == SPACE
+ /*|| (params_data+last)->unicode() == TAB*/))
+ ++last;
+ }
+ }
+ return args;
+}
+
+static QStringList split_value_list(const QString &vals, bool do_semicolon=false)
+{
+ QString build;
+ QStringList ret;
+ QStack<char> quote;
+
+ const ushort LPAREN = '(';
+ const ushort RPAREN = ')';
+ const ushort SINGLEQUOTE = '\'';
+ const ushort DOUBLEQUOTE = '"';
+ const ushort BACKSLASH = '\\';
+ const ushort SEMICOLON = ';';
+
+ ushort unicode;
+ const QChar *vals_data = vals.data();
+ const int vals_len = vals.length();
+ for (int x = 0, parens = 0; x < vals_len; x++) {
+ unicode = vals_data[x].unicode();
+ if (x != (int)vals_len-1 && unicode == BACKSLASH &&
+ (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) {
+ build += vals_data[x++]; //get that 'escape'
+ } else if (!quote.isEmpty() && unicode == quote.top()) {
+ quote.pop();
+ } else if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
+ quote.push(unicode);
+ } else if (unicode == RPAREN) {
+ --parens;
+ } else if (unicode == LPAREN) {
+ ++parens;
+ }
+
+ if (!parens && quote.isEmpty() && ((do_semicolon && unicode == SEMICOLON) ||
+ vals_data[x] == Option::field_sep)) {
+ ret << build;
+ build.clear();
+ } else {
+ build += vals_data[x];
+ }
+ }
+ if (!build.isEmpty())
+ ret << build;
+ return ret;
+}
+
+static QStringList qmake_mkspec_paths()
+{
+ QStringList ret;
+ const QString concat = QDir::separator() + QLatin1String("mkspecs");
+ QByteArray qmakepath = qgetenv("QMAKEPATH");
+ if (!qmakepath.isEmpty()) {
+ const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath));
+ for (QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it)
+ ret << ((*it) + concat);
+ }
+ ret << QLibraryInfo::location(QLibraryInfo::DataPath) + concat;
+
+ return ret;
+}
+
+QT_END_NAMESPACE
+
+#endif // PROPARSERUTILS_H
diff --git a/src/linguist/shared/qm.cpp b/src/linguist/shared/qm.cpp
new file mode 100644
index 000000000..0b4bd9e32
--- /dev/null
+++ b/src/linguist/shared/qm.cpp
@@ -0,0 +1,803 @@
+/****************************************************************************
+**
+** 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 "translator.h"
+
+#ifndef QT_BOOTSTRAPPED
+#include <QtCore/QCoreApplication>
+#endif
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QMap>
+#include <QtCore/QString>
+#include <QtCore/QTextCodec>
+
+QT_BEGIN_NAMESPACE
+
+// magic number for the file
+static const int MagicLength = 16;
+static const uchar magic[MagicLength] = {
+ 0x3c, 0xb8, 0x64, 0x18, 0xca, 0xef, 0x9c, 0x95,
+ 0xcd, 0x21, 0x1c, 0xbf, 0x60, 0xa1, 0xbd, 0xdd
+};
+
+
+namespace {
+
+enum Tag {
+ Tag_End = 1,
+ Tag_SourceText16 = 2,
+ Tag_Translation = 3,
+ Tag_Context16 = 4,
+ Tag_Obsolete1 = 5,
+ Tag_SourceText = 6,
+ Tag_Context = 7,
+ Tag_Comment = 8,
+ Tag_Obsolete2 = 9
+};
+
+enum Prefix {
+ NoPrefix,
+ Hash,
+ HashContext,
+ HashContextSourceText,
+ HashContextSourceTextComment
+};
+
+} // namespace anon
+
+static uint elfHash(const QByteArray &ba)
+{
+ const uchar *k = (const uchar *)ba.data();
+ uint h = 0;
+ uint g;
+
+ if (k) {
+ while (*k) {
+ h = (h << 4) + *k++;
+ if ((g = (h & 0xf0000000)) != 0)
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ }
+ if (!h)
+ h = 1;
+ return h;
+}
+
+class ByteTranslatorMessage
+{
+public:
+ ByteTranslatorMessage(
+ const QByteArray &context,
+ const QByteArray &sourceText,
+ const QByteArray &comment,
+ const QStringList &translations) :
+ m_context(context),
+ m_sourcetext(sourceText),
+ m_comment(comment),
+ m_translations(translations)
+ {}
+ const QByteArray &context() const { return m_context; }
+ const QByteArray &sourceText() const { return m_sourcetext; }
+ const QByteArray &comment() const { return m_comment; }
+ const QStringList &translations() const { return m_translations; }
+ bool operator<(const ByteTranslatorMessage& m) const;
+
+private:
+ QByteArray m_context;
+ QByteArray m_sourcetext;
+ QByteArray m_comment;
+ QStringList m_translations;
+};
+
+Q_DECLARE_TYPEINFO(ByteTranslatorMessage, Q_MOVABLE_TYPE);
+
+bool ByteTranslatorMessage::operator<(const ByteTranslatorMessage& m) const
+{
+ if (m_context != m.m_context)
+ return m_context < m.m_context;
+ if (m_sourcetext != m.m_sourcetext)
+ return m_sourcetext < m.m_sourcetext;
+ return m_comment < m.m_comment;
+}
+
+class Releaser
+{
+public:
+ struct Offset {
+ Offset()
+ : h(0), o(0)
+ {}
+ Offset(uint hash, uint offset)
+ : h(hash), o(offset)
+ {}
+
+ bool operator<(const Offset &other) const {
+ return (h != other.h) ? h < other.h : o < other.o;
+ }
+ bool operator==(const Offset &other) const {
+ return h == other.h && o == other.o;
+ }
+ uint h;
+ uint o;
+ };
+
+ enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69, NumerusRules = 0x88 };
+
+ Releaser() : m_codec(0) {}
+
+ void setCodecName(const QByteArray &codecName)
+ {
+ m_codec = QTextCodec::codecForName(codecName);
+ }
+
+ bool save(QIODevice *iod);
+
+ void insert(const TranslatorMessage &msg, const QStringList &tlns, bool forceComment);
+ void insertIdBased(const TranslatorMessage &message, const QStringList &tlns);
+
+ void squeeze(TranslatorSaveMode mode);
+
+ void setNumerusRules(const QByteArray &rules);
+
+private:
+ Q_DISABLE_COPY(Releaser)
+
+ // This should reproduce the byte array fetched from the source file, which
+ // on turn should be the same as passed to the actual tr(...) calls
+ QByteArray originalBytes(const QString &str, bool isUtf8) const;
+
+ void insertInternal(const TranslatorMessage &message, const QStringList &tlns,
+ bool forceComment, bool isUtf8);
+
+ static Prefix commonPrefix(const ByteTranslatorMessage &m1, const ByteTranslatorMessage &m2);
+
+ static uint msgHash(const ByteTranslatorMessage &msg);
+
+ void writeMessage(const ByteTranslatorMessage & msg, QDataStream & stream,
+ TranslatorSaveMode strip, Prefix prefix) const;
+
+ // for squeezed but non-file data, this is what needs to be deleted
+ QByteArray m_messageArray;
+ QByteArray m_offsetArray;
+ QByteArray m_contextArray;
+ QMap<ByteTranslatorMessage, void *> m_messages;
+ QByteArray m_numerusRules;
+
+ // Used to reproduce the original bytes
+ QTextCodec *m_codec;
+};
+
+QByteArray Releaser::originalBytes(const QString &str, bool isUtf8) const
+{
+ if (str.isEmpty()) {
+ // Do not use QByteArray() here as the result of the serialization
+ // will be different.
+ return QByteArray("");
+ }
+ if (isUtf8)
+ return str.toUtf8();
+ return m_codec ? m_codec->fromUnicode(str) : str.toLatin1();
+}
+
+uint Releaser::msgHash(const ByteTranslatorMessage &msg)
+{
+ return elfHash(msg.sourceText() + msg.comment());
+}
+
+Prefix Releaser::commonPrefix(const ByteTranslatorMessage &m1, const ByteTranslatorMessage &m2)
+{
+ if (msgHash(m1) != msgHash(m2))
+ return NoPrefix;
+ if (m1.context() != m2.context())
+ return Hash;
+ if (m1.sourceText() != m2.sourceText())
+ return HashContext;
+ if (m1.comment() != m2.comment())
+ return HashContextSourceText;
+ return HashContextSourceTextComment;
+}
+
+void Releaser::writeMessage(const ByteTranslatorMessage &msg, QDataStream &stream,
+ TranslatorSaveMode mode, Prefix prefix) const
+{
+ for (int i = 0; i < msg.translations().count(); ++i)
+ stream << quint8(Tag_Translation) << msg.translations().at(i);
+
+ if (mode == SaveEverything)
+ prefix = HashContextSourceTextComment;
+
+ // lrelease produces "wrong" QM files for QByteArrays that are .isNull().
+ switch (prefix) {
+ default:
+ case HashContextSourceTextComment:
+ stream << quint8(Tag_Comment) << msg.comment();
+ // fall through
+ case HashContextSourceText:
+ stream << quint8(Tag_SourceText) << msg.sourceText();
+ // fall through
+ case HashContext:
+ stream << quint8(Tag_Context) << msg.context();
+ break;
+ }
+
+ stream << quint8(Tag_End);
+}
+
+
+bool Releaser::save(QIODevice *iod)
+{
+ QDataStream s(iod);
+ s.writeRawData((const char *)magic, MagicLength);
+
+ if (!m_offsetArray.isEmpty()) {
+ quint32 oas = quint32(m_offsetArray.size());
+ s << quint8(Hashes) << oas;
+ s.writeRawData(m_offsetArray.constData(), oas);
+ }
+ if (!m_messageArray.isEmpty()) {
+ quint32 mas = quint32(m_messageArray.size());
+ s << quint8(Messages) << mas;
+ s.writeRawData(m_messageArray.constData(), mas);
+ }
+ if (!m_contextArray.isEmpty()) {
+ quint32 cas = quint32(m_contextArray.size());
+ s << quint8(Contexts) << cas;
+ s.writeRawData(m_contextArray.constData(), cas);
+ }
+ if (!m_numerusRules.isEmpty()) {
+ quint32 nrs = m_numerusRules.size();
+ s << quint8(NumerusRules) << nrs;
+ s.writeRawData(m_numerusRules.constData(), nrs);
+ }
+ return true;
+}
+
+void Releaser::squeeze(TranslatorSaveMode mode)
+{
+ if (m_messages.isEmpty() && mode == SaveEverything)
+ return;
+
+ QMap<ByteTranslatorMessage, void *> messages = m_messages;
+
+ // re-build contents
+ m_messageArray.clear();
+ m_offsetArray.clear();
+ m_contextArray.clear();
+ m_messages.clear();
+
+ QMap<Offset, void *> offsets;
+
+ QDataStream ms(&m_messageArray, QIODevice::WriteOnly);
+ QMap<ByteTranslatorMessage, void *>::const_iterator it, next;
+ int cpPrev = 0, cpNext = 0;
+ for (it = messages.constBegin(); it != messages.constEnd(); ++it) {
+ cpPrev = cpNext;
+ next = it;
+ ++next;
+ if (next == messages.constEnd())
+ cpNext = 0;
+ else
+ cpNext = commonPrefix(it.key(), next.key());
+ offsets.insert(Offset(msgHash(it.key()), ms.device()->pos()), (void *)0);
+ writeMessage(it.key(), ms, mode, Prefix(qMax(cpPrev, cpNext + 1)));
+ }
+
+ QMap<Offset, void *>::Iterator offset;
+ offset = offsets.begin();
+ QDataStream ds(&m_offsetArray, QIODevice::WriteOnly);
+ while (offset != offsets.end()) {
+ Offset k = offset.key();
+ ++offset;
+ ds << quint32(k.h) << quint32(k.o);
+ }
+
+ if (mode == SaveStripped) {
+ QMap<QByteArray, int> contextSet;
+ for (it = messages.constBegin(); it != messages.constEnd(); ++it)
+ ++contextSet[it.key().context()];
+
+ quint16 hTableSize;
+ if (contextSet.size() < 200)
+ hTableSize = (contextSet.size() < 60) ? 151 : 503;
+ else if (contextSet.size() < 2500)
+ hTableSize = (contextSet.size() < 750) ? 1511 : 5003;
+ else
+ hTableSize = (contextSet.size() < 10000) ? 15013 : 3 * contextSet.size() / 2;
+
+ QMultiMap<int, QByteArray> hashMap;
+ QMap<QByteArray, int>::const_iterator c;
+ for (c = contextSet.constBegin(); c != contextSet.constEnd(); ++c)
+ hashMap.insert(elfHash(c.key()) % hTableSize, c.key());
+
+ /*
+ The contexts found in this translator are stored in a hash
+ table to provide fast lookup. The context array has the
+ following format:
+
+ quint16 hTableSize;
+ quint16 hTable[hTableSize];
+ quint8 contextPool[...];
+
+ The context pool stores the contexts as Pascal strings:
+
+ quint8 len;
+ quint8 data[len];
+
+ Let's consider the look-up of context "FunnyDialog". A
+ hash value between 0 and hTableSize - 1 is computed, say h.
+ If hTable[h] is 0, "FunnyDialog" is not covered by this
+ translator. Else, we check in the contextPool at offset
+ 2 * hTable[h] to see if "FunnyDialog" is one of the
+ contexts stored there, until we find it or we meet the
+ empty string.
+ */
+ m_contextArray.resize(2 + (hTableSize << 1));
+ QDataStream t(&m_contextArray, QIODevice::WriteOnly);
+
+ quint16 *hTable = new quint16[hTableSize];
+ memset(hTable, 0, hTableSize * sizeof(quint16));
+
+ t << hTableSize;
+ t.device()->seek(2 + (hTableSize << 1));
+ t << quint16(0); // the entry at offset 0 cannot be used
+ uint upto = 2;
+
+ QMap<int, QByteArray>::const_iterator entry = hashMap.constBegin();
+ while (entry != hashMap.constEnd()) {
+ int i = entry.key();
+ hTable[i] = quint16(upto >> 1);
+
+ do {
+ const char *con = entry.value().constData();
+ uint len = uint(entry.value().length());
+ len = qMin(len, 255u);
+ t << quint8(len);
+ t.writeRawData(con, len);
+ upto += 1 + len;
+ ++entry;
+ } while (entry != hashMap.constEnd() && entry.key() == i);
+ if (upto & 0x1) {
+ // offsets have to be even
+ t << quint8(0); // empty string
+ ++upto;
+ }
+ }
+ t.device()->seek(2);
+ for (int j = 0; j < hTableSize; j++)
+ t << hTable[j];
+ delete [] hTable;
+
+ if (upto > 131072) {
+ qWarning("Releaser::squeeze: Too many contexts");
+ m_contextArray.clear();
+ }
+ }
+}
+
+void Releaser::insertInternal(const TranslatorMessage &message, const QStringList &tlns,
+ bool forceComment, bool isUtf8)
+{
+ ByteTranslatorMessage bmsg(originalBytes(message.context(), isUtf8),
+ originalBytes(message.sourceText(), isUtf8),
+ originalBytes(message.comment(), isUtf8),
+ tlns);
+ if (!forceComment) {
+ ByteTranslatorMessage bmsg2(
+ bmsg.context(), bmsg.sourceText(), QByteArray(""), bmsg.translations());
+ if (!m_messages.contains(bmsg2)) {
+ m_messages.insert(bmsg2, 0);
+ return;
+ }
+ }
+ m_messages.insert(bmsg, 0);
+}
+
+void Releaser::insert(const TranslatorMessage &message, const QStringList &tlns, bool forceComment)
+{
+ insertInternal(message, tlns, forceComment, message.isUtf8());
+ if (message.isUtf8() && message.isNonUtf8())
+ insertInternal(message, tlns, forceComment, false);
+}
+
+void Releaser::insertIdBased(const TranslatorMessage &message, const QStringList &tlns)
+{
+ ByteTranslatorMessage bmsg("", originalBytes(message.id(), false), "", tlns);
+ m_messages.insert(bmsg, 0);
+}
+
+void Releaser::setNumerusRules(const QByteArray &rules)
+{
+ m_numerusRules = rules;
+}
+
+static quint8 read8(const uchar *data)
+{
+ return *data;
+}
+
+static quint32 read32(const uchar *data)
+{
+ return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]);
+}
+
+static void fromBytes(const char *str, int len, QTextCodec *codec, QTextCodec *utf8Codec,
+ QString *out, QString *utf8Out,
+ bool *isSystem, bool *isUtf8, bool *needs8Bit)
+{
+ for (int i = 0; i < len; ++i)
+ if (str[i] & 0x80) {
+ if (utf8Codec) {
+ QTextCodec::ConverterState cvtState;
+ *utf8Out = utf8Codec->toUnicode(str, len, &cvtState);
+ *isUtf8 = !cvtState.invalidChars;
+ }
+ QTextCodec::ConverterState cvtState;
+ *out = codec->toUnicode(str, len, &cvtState);
+ *isSystem = !cvtState.invalidChars;
+ *needs8Bit = true;
+ return;
+ }
+ *out = QString::fromLatin1(str, len);
+ *isSystem = true;
+ if (utf8Codec) {
+ *utf8Out = *out;
+ *isUtf8 = true;
+ }
+ *needs8Bit = false;
+}
+
+bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ QByteArray ba = dev.readAll();
+ const uchar *data = (uchar*)ba.data();
+ int len = ba.size();
+ if (len < MagicLength || memcmp(data, magic, MagicLength) != 0) {
+ cd.appendError(QLatin1String("QM-Format error: magic marker missing"));
+ return false;
+ }
+
+ enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69, NumerusRules = 0x88 };
+
+ // for squeezed but non-file data, this is what needs to be deleted
+ const uchar *messageArray = 0;
+ const uchar *offsetArray = 0;
+ const uchar *contextArray = 0;
+ const uchar *numerusRulesArray = 0;
+ uint messageLength = 0;
+ uint offsetLength = 0;
+ uint contextLength = 0;
+ uint numerusRulesLength = 0;
+
+ bool ok = true;
+ const uchar *end = data + len;
+
+ data += MagicLength;
+
+ while (data < end - 4) {
+ quint8 tag = read8(data++);
+ quint32 blockLen = read32(data);
+ //qDebug() << "TAG:" << tag << "BLOCKLEN:" << blockLen;
+ data += 4;
+ if (!tag || !blockLen)
+ break;
+ if (data + blockLen > end) {
+ ok = false;
+ break;
+ }
+
+ if (tag == Contexts) {
+ contextArray = data;
+ contextLength = blockLen;
+ //qDebug() << "CONTEXTS: " << contextLength << QByteArray((const char *)contextArray, contextLength).toHex();
+ } else if (tag == Hashes) {
+ offsetArray = data;
+ offsetLength = blockLen;
+ //qDebug() << "HASHES: " << offsetLength << QByteArray((const char *)offsetArray, offsetLength).toHex();
+ } else if (tag == Messages) {
+ messageArray = data;
+ messageLength = blockLen;
+ //qDebug() << "MESSAGES: " << messageLength << QByteArray((const char *)messageArray, messageLength).toHex();
+ } else if (tag == NumerusRules) {
+ numerusRulesArray = data;
+ numerusRulesLength = blockLen;
+ //qDebug() << "NUMERUSRULES: " << numerusRulesLength << QByteArray((const char *)numerusRulesArray, numerusRulesLength).toHex();
+ }
+
+ data += blockLen;
+ }
+
+
+ size_t numItems = offsetLength / (2 * sizeof(quint32));
+ //qDebug() << "NUMITEMS: " << numItems;
+
+ QTextCodec *codec = QTextCodec::codecForName(
+ cd.m_codecForSource.isEmpty() ? QByteArray("Latin1") : cd.m_codecForSource);
+ QTextCodec *utf8Codec = 0;
+ if (codec->name() != "UTF-8")
+ utf8Codec = QTextCodec::codecForName("UTF-8");
+
+ QString strProN = QLatin1String("%n");
+ QLocale::Language l;
+ QLocale::Country c;
+ Translator::languageAndCountry(translator.languageCode(), &l, &c);
+ QStringList numerusForms;
+ bool guessPlurals = true;
+ if (getNumerusInfo(l, c, 0, &numerusForms, 0))
+ guessPlurals = (numerusForms.count() == 1);
+
+ QString context, contextUtf8;
+ bool contextIsSystem, contextIsUtf8, contextNeeds8Bit;
+ QString sourcetext, sourcetextUtf8;
+ bool sourcetextIsSystem, sourcetextIsUtf8, sourcetextNeeds8Bit;
+ QString comment, commentUtf8;
+ bool commentIsSystem, commentIsUtf8, commentNeeds8Bit;
+ QStringList translations;
+
+ for (const uchar *start = offsetArray; start != offsetArray + (numItems << 3); start += 8) {
+ //quint32 hash = read32(start);
+ quint32 ro = read32(start + 4);
+ //qDebug() << "\nHASH:" << hash;
+ const uchar *m = messageArray + ro;
+
+ for (;;) {
+ uchar tag = read8(m++);
+ //qDebug() << "Tag:" << tag << " ADDR: " << m;
+ switch(tag) {
+ case Tag_End:
+ goto end;
+ case Tag_Translation: {
+ int len = read32(m);
+ if (len % 1) {
+ cd.appendError(QLatin1String("QM-Format error"));
+ return false;
+ }
+ m += 4;
+ QString str = QString((const QChar *)m, len/2);
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
+ for (int i = 0; i < str.length(); ++i)
+ str[i] = QChar((str.at(i).unicode() >> 8) +
+ ((str.at(i).unicode() << 8) & 0xff00));
+ }
+ translations << str;
+ m += len;
+ break;
+ }
+ case Tag_Obsolete1:
+ m += 4;
+ //qDebug() << "OBSOLETE";
+ break;
+ case Tag_SourceText: {
+ quint32 len = read32(m);
+ m += 4;
+ //qDebug() << "SOURCE LEN: " << len;
+ //qDebug() << "SOURCE: " << QByteArray((const char*)m, len);
+ fromBytes((const char*)m, len, codec, utf8Codec,
+ &sourcetext, &sourcetextUtf8,
+ &sourcetextIsSystem, &sourcetextIsUtf8, &sourcetextNeeds8Bit);
+ m += len;
+ break;
+ }
+ case Tag_Context: {
+ quint32 len = read32(m);
+ m += 4;
+ //qDebug() << "CONTEXT LEN: " << len;
+ //qDebug() << "CONTEXT: " << QByteArray((const char*)m, len);
+ fromBytes((const char*)m, len, codec, utf8Codec,
+ &context, &contextUtf8,
+ &contextIsSystem, &contextIsUtf8, &contextNeeds8Bit);
+ m += len;
+ break;
+ }
+ case Tag_Comment: {
+ quint32 len = read32(m);
+ m += 4;
+ //qDebug() << "COMMENT LEN: " << len;
+ //qDebug() << "COMMENT: " << QByteArray((const char*)m, len);
+ fromBytes((const char*)m, len, codec, utf8Codec,
+ &comment, &commentUtf8,
+ &commentIsSystem, &commentIsUtf8, &commentNeeds8Bit);
+ m += len;
+ break;
+ }
+ default:
+ //qDebug() << "UNKNOWN TAG" << tag;
+ break;
+ }
+ }
+ end:;
+ TranslatorMessage msg;
+ msg.setType(TranslatorMessage::Finished);
+ if (translations.count() > 1) {
+ // If guessPlurals is not false here, plural form discard messages
+ // will be spewn out later.
+ msg.setPlural(true);
+ } else if (guessPlurals) {
+ // This might cause false positives, so it is a fallback only.
+ if (sourcetext.contains(strProN))
+ msg.setPlural(true);
+ }
+ msg.setTranslations(translations);
+ translations.clear();
+ if (contextNeeds8Bit || sourcetextNeeds8Bit || commentNeeds8Bit) {
+ if (utf8Codec && contextIsUtf8 && sourcetextIsUtf8 && commentIsUtf8) {
+ // The message is utf-8, but file is not.
+ msg.setUtf8(true);
+ msg.setContext(contextUtf8);
+ msg.setSourceText(sourcetextUtf8);
+ msg.setComment(commentUtf8);
+ translator.append(msg);
+ continue;
+ }
+ if (!(contextIsSystem && sourcetextIsSystem && commentIsSystem)) {
+ cd.appendError(QLatin1String(
+ "Cannot read file with specified input codec"));
+ return false;
+ }
+ // The message is 8-bit in the file's encoding (utf-8 or not).
+ }
+ msg.setContext(context);
+ msg.setSourceText(sourcetext);
+ msg.setComment(comment);
+ translator.append(msg);
+ }
+ return ok;
+}
+
+
+
+static bool containsStripped(const Translator &translator, const TranslatorMessage &msg)
+{
+ foreach (const TranslatorMessage &tmsg, translator.messages())
+ if (tmsg.sourceText() == msg.sourceText()
+ && tmsg.context() == msg.context()
+ && tmsg.comment().isEmpty())
+ return true;
+ return false;
+}
+
+static bool saveQM(const Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ Releaser releaser;
+ QLocale::Language l;
+ QLocale::Country c;
+ Translator::languageAndCountry(translator.languageCode(), &l, &c);
+ QByteArray rules;
+ if (getNumerusInfo(l, c, &rules, 0, 0))
+ releaser.setNumerusRules(rules);
+ releaser.setCodecName(translator.codecName());
+
+ int finished = 0;
+ int unfinished = 0;
+ int untranslated = 0;
+ int missingIds = 0;
+ int droppedData = 0;
+
+ for (int i = 0; i != translator.messageCount(); ++i) {
+ const TranslatorMessage &msg = translator.message(i);
+ TranslatorMessage::Type typ = msg.type();
+ if (typ != TranslatorMessage::Obsolete) {
+ if (cd.m_idBased && msg.id().isEmpty()) {
+ ++missingIds;
+ continue;
+ }
+ if (typ == TranslatorMessage::Unfinished) {
+ if (msg.translation().isEmpty() && !cd.m_idBased && cd.m_unTrPrefix.isEmpty()) {
+ ++untranslated;
+ continue;
+ } else {
+ if (cd.ignoreUnfinished())
+ continue;
+ ++unfinished;
+ }
+ } else {
+ ++finished;
+ }
+ QStringList tlns = msg.translations();
+ if (msg.type() == TranslatorMessage::Unfinished
+ && (cd.m_idBased || !cd.m_unTrPrefix.isEmpty()))
+ for (int j = 0; j < tlns.size(); ++j)
+ if (tlns.at(j).isEmpty())
+ tlns[j] = cd.m_unTrPrefix + msg.sourceText();
+ if (cd.m_idBased) {
+ if (!msg.context().isEmpty() || !msg.comment().isEmpty())
+ ++droppedData;
+ releaser.insertIdBased(msg, tlns);
+ } else {
+ // Drop the comment in (context, sourceText, comment),
+ // unless the context is empty,
+ // unless (context, sourceText, "") already exists or
+ // unless we already dropped the comment of (context,
+ // sourceText, comment0).
+ bool forceComment =
+ msg.comment().isEmpty()
+ || msg.context().isEmpty()
+ || containsStripped(translator, msg);
+ releaser.insert(msg, tlns, forceComment);
+ }
+ }
+ }
+
+ if (missingIds)
+ cd.appendError(QCoreApplication::translate("LRelease",
+ "Dropped %n message(s) which had no ID.", 0,
+ QCoreApplication::CodecForTr, missingIds));
+ if (droppedData)
+ cd.appendError(QCoreApplication::translate("LRelease",
+ "Excess context/disambiguation dropped from %n message(s).", 0,
+ QCoreApplication::CodecForTr, droppedData));
+
+ releaser.squeeze(cd.m_saveMode);
+ bool saved = releaser.save(&dev);
+ if (saved && cd.isVerbose()) {
+ int generatedCount = finished + unfinished;
+ cd.appendError(QCoreApplication::translate("LRelease",
+ " Generated %n translation(s) (%1 finished and %2 unfinished)", 0,
+ QCoreApplication::CodecForTr, generatedCount).arg(finished).arg(unfinished));
+ if (untranslated)
+ cd.appendError(QCoreApplication::translate("LRelease",
+ " Ignored %n untranslated source text(s)", 0,
+ QCoreApplication::CodecForTr, untranslated));
+ }
+ return saved;
+}
+
+int initQM()
+{
+ Translator::FileFormat format;
+
+ format.extension = QLatin1String("qm");
+ format.description = QObject::tr("Compiled Qt translations");
+ format.fileType = Translator::FileFormat::TranslationBinary;
+ format.priority = 0;
+ format.loader = &loadQM;
+ format.saver = &saveQM;
+ Translator::registerFileFormat(format);
+
+ return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(initQM)
+
+QT_END_NAMESPACE
diff --git a/src/linguist/shared/qph.cpp b/src/linguist/shared/qph.cpp
new file mode 100644
index 000000000..b22aa3fe8
--- /dev/null
+++ b/src/linguist/shared/qph.cpp
@@ -0,0 +1,207 @@
+/****************************************************************************
+**
+** 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 "translator.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QDebug>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextStream>
+
+#include <QtXml/QXmlStreamReader>
+#include <QtXml/QXmlStreamAttribute>
+
+QT_BEGIN_NAMESPACE
+
+class QPHReader : public QXmlStreamReader
+{
+public:
+ QPHReader(QIODevice &dev)
+ : QXmlStreamReader(&dev)
+ {}
+
+ // the "real thing"
+ bool read(Translator &translator);
+
+private:
+ bool isWhiteSpace() const
+ {
+ return isCharacters() && text().toString().trimmed().isEmpty();
+ }
+
+ enum DataField { NoField, SourceField, TargetField, DefinitionField };
+ DataField m_currentField;
+ QString m_currentSource;
+ QString m_currentTarget;
+ QString m_currentDefinition;
+};
+
+bool QPHReader::read(Translator &translator)
+{
+ m_currentField = NoField;
+ QString result;
+ while (!atEnd()) {
+ readNext();
+ if (isStartElement()) {
+ if (name() == QLatin1String("source")) {
+ m_currentField = SourceField;
+ } else if (name() == QLatin1String("target")) {
+ m_currentField = TargetField;
+ } else if (name() == QLatin1String("definition")) {
+ m_currentField = DefinitionField;
+ } else {
+ m_currentField = NoField;
+ if (name() == QLatin1String("QPH")) {
+ QXmlStreamAttributes atts = attributes();
+ translator.setLanguageCode(atts.value(QLatin1String("language")).toString());
+ translator.setSourceLanguageCode(atts.value(QLatin1String("sourcelanguage")).toString());
+ }
+ }
+ } else if (isWhiteSpace()) {
+ // ignore these
+ } else if (isCharacters()) {
+ if (m_currentField == SourceField)
+ m_currentSource += text();
+ else if (m_currentField == TargetField)
+ m_currentTarget += text();
+ else if (m_currentField == DefinitionField)
+ m_currentDefinition += text();
+ } else if (isEndElement() && name() == QLatin1String("phrase")) {
+ m_currentTarget.replace(QChar(Translator::TextVariantSeparator),
+ QChar(Translator::BinaryVariantSeparator));
+ TranslatorMessage msg;
+ msg.setSourceText(m_currentSource);
+ msg.setTranslation(m_currentTarget);
+ msg.setComment(m_currentDefinition);
+ translator.append(msg);
+ m_currentSource.clear();
+ m_currentTarget.clear();
+ m_currentDefinition.clear();
+ }
+ }
+ return true;
+}
+
+static bool loadQPH(Translator &translator, QIODevice &dev, ConversionData &)
+{
+ translator.setLocationsType(Translator::NoLocations);
+ QPHReader reader(dev);
+ return reader.read(translator);
+}
+
+static QString protect(const QString &str)
+{
+ QString result;
+ result.reserve(str.length() * 12 / 10);
+ for (int i = 0; i != str.size(); ++i) {
+ uint c = str.at(i).unicode();
+ switch (c) {
+ case '\"':
+ result += QLatin1String("&quot;");
+ break;
+ case '&':
+ result += QLatin1String("&amp;");
+ break;
+ case '>':
+ result += QLatin1String("&gt;");
+ break;
+ case '<':
+ result += QLatin1String("&lt;");
+ break;
+ case '\'':
+ result += QLatin1String("&apos;");
+ break;
+ default:
+ if (c < 0x20 && c != '\r' && c != '\n' && c != '\t')
+ result += QString(QLatin1String("&#%1;")).arg(c);
+ else // this also covers surrogates
+ result += QChar(c);
+ }
+ }
+ return result;
+}
+
+static bool saveQPH(const Translator &translator, QIODevice &dev, ConversionData &)
+{
+ QTextStream t(&dev);
+ t.setCodec(QTextCodec::codecForName("UTF-8"));
+ t << "<!DOCTYPE QPH>\n<QPH";
+ QString languageCode = translator.languageCode();
+ if (!languageCode.isEmpty() && languageCode != QLatin1String("C"))
+ t << " language=\"" << languageCode << "\"";
+ languageCode = translator.sourceLanguageCode();
+ if (!languageCode.isEmpty() && languageCode != QLatin1String("C"))
+ t << " sourcelanguage=\"" << languageCode << "\"";
+ t << ">\n";
+ foreach (const TranslatorMessage &msg, translator.messages()) {
+ t << "<phrase>\n";
+ t << " <source>" << protect(msg.sourceText()) << "</source>\n";
+ QString str = msg.translations().join(QLatin1String("@"));
+ str.replace(QChar(Translator::BinaryVariantSeparator),
+ QChar(Translator::TextVariantSeparator));
+ t << " <target>" << protect(str)
+ << "</target>\n";
+ if (!msg.comment().isEmpty())
+ t << " <definition>" << protect(msg.comment()) << "</definition>\n";
+ t << "</phrase>\n";
+ }
+ t << "</QPH>\n";
+ return true;
+}
+
+int initQPH()
+{
+ Translator::FileFormat format;
+
+ format.extension = QLatin1String("qph");
+ format.description = QObject::tr("Qt Linguist 'Phrase Book'");
+ format.fileType = Translator::FileFormat::TranslationSource;
+ format.priority = 0;
+ format.loader = &loadQPH;
+ format.saver = &saveQPH;
+ Translator::registerFileFormat(format);
+
+ return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(initQPH)
+
+QT_END_NAMESPACE
diff --git a/src/linguist/shared/simtexth.cpp b/src/linguist/shared/simtexth.cpp
new file mode 100644
index 000000000..58ea2eef5
--- /dev/null
+++ b/src/linguist/shared/simtexth.cpp
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** 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 "simtexth.h"
+#include "translator.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QString>
+#include <QtCore/QList>
+
+
+QT_BEGIN_NAMESPACE
+
+typedef QList<TranslatorMessage> TML;
+
+/*
+ How similar are two texts? The approach used here relies on co-occurrence
+ matrices and is very efficient.
+
+ Let's see with an example: how similar are "here" and "hither"? The
+ co-occurrence matrix M for "here" is M[h,e] = 1, M[e,r] = 1, M[r,e] = 1, and 0
+ elsewhere; the matrix N for "hither" is N[h,i] = 1, N[i,t] = 1, ...,
+ N[h,e] = 1, N[e,r] = 1, and 0 elsewhere. The union U of both matrices is the
+ matrix U[i,j] = max { M[i,j], N[i,j] }, and the intersection V is
+ V[i,j] = min { M[i,j], N[i,j] }. The score for a pair of texts is
+
+ score = (sum of V[i,j] over all i, j) / (sum of U[i,j] over all i, j),
+
+ a formula suggested by Arnt Gulbrandsen. Here we have
+
+ score = 2 / 6,
+
+ or one third.
+
+ The implementation differs from this in a few details. Most importantly,
+ repetitions are ignored; for input "xxx", M[x,x] equals 1, not 2.
+*/
+
+/*
+ Every character is assigned to one of 20 buckets so that the co-occurrence
+ matrix requires only 20 * 20 = 400 bits, not 256 * 256 = 65536 bits or even
+ more if we want the whole Unicode. Which character falls in which bucket is
+ arbitrary.
+
+ The second half of the table is a replica of the first half, because of
+ laziness.
+*/
+static const int indexOf[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// ! " # $ % & ' ( ) * + , - . /
+ 0, 2, 6, 7, 10, 12, 15, 19, 2, 6, 7, 10, 12, 15, 19, 0,
+// 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
+ 1, 3, 4, 5, 8, 9, 11, 13, 14, 16, 2, 6, 7, 10, 12, 15,
+// @ A B C D E F G H I J K L M N O
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 10, 11, 12, 13, 14,
+// P Q R S T U V W X Y Z [ \ ] ^ _
+ 15, 12, 16, 17, 18, 19, 2, 10, 15, 7, 19, 2, 6, 7, 10, 0,
+// ` a b c d e f g h i j k l m n o
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 10, 11, 12, 13, 14,
+// p q r s t u v w x y z { | } ~
+ 15, 12, 16, 17, 18, 19, 2, 10, 15, 7, 19, 2, 6, 7, 10, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 6, 7, 10, 12, 15, 19, 2, 6, 7, 10, 12, 15, 19, 0,
+ 1, 3, 4, 5, 8, 9, 11, 13, 14, 16, 2, 6, 7, 10, 12, 15,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 10, 11, 12, 13, 14,
+ 15, 12, 16, 17, 18, 19, 2, 10, 15, 7, 19, 2, 6, 7, 10, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 10, 11, 12, 13, 14,
+ 15, 12, 16, 17, 18, 19, 2, 10, 15, 7, 19, 2, 6, 7, 10, 0
+};
+
+/*
+ The entry bitCount[i] (for i between 0 and 255) is the number of bits used to
+ represent i in binary.
+*/
+static const int bitCount[256] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
+};
+
+struct CoMatrix
+{
+ /*
+ The matrix has 20 * 20 = 400 entries. This requires 50 bytes, or 13
+ words. Some operations are performed on words for more efficiency.
+ */
+ union {
+ quint8 b[52];
+ quint32 w[13];
+ };
+
+ CoMatrix() { memset( b, 0, 52 ); }
+
+ CoMatrix(const QString &str)
+ {
+ QByteArray ba = str.toUtf8();
+ const char *text = ba.constData();
+ char c = '\0', d;
+ memset( b, 0, 52 );
+ /*
+ The Knuth books are not in the office only for show; they help make
+ loops 30% faster and 20% as readable.
+ */
+ while ( (d = *text) != '\0' ) {
+ setCoOccurence( c, d );
+ if ( (c = *++text) != '\0' ) {
+ setCoOccurence( d, c );
+ text++;
+ }
+ }
+ }
+
+ void setCoOccurence( char c, char d ) {
+ int k = indexOf[(uchar) c] + 20 * indexOf[(uchar) d];
+ b[k >> 3] |= (1 << (k & 0x7));
+ }
+
+ int worth() const {
+ int w = 0;
+ for ( int i = 0; i < 50; i++ )
+ w += bitCount[b[i]];
+ return w;
+ }
+};
+
+static inline CoMatrix reunion(const CoMatrix &m, const CoMatrix &n)
+{
+ CoMatrix p;
+ for (int i = 0; i < 13; ++i)
+ p.w[i] = m.w[i] | n.w[i];
+ return p;
+}
+
+static inline CoMatrix intersection(const CoMatrix &m, const CoMatrix &n)
+{
+ CoMatrix p;
+ for (int i = 0; i < 13; ++i)
+ p.w[i] = m.w[i] & n.w[i];
+ return p;
+}
+
+StringSimilarityMatcher::StringSimilarityMatcher(const QString &stringToMatch)
+{
+ m_cm = new CoMatrix(stringToMatch);
+ m_length = stringToMatch.length();
+}
+
+int StringSimilarityMatcher::getSimilarityScore(const QString &strCandidate)
+{
+ CoMatrix cmTarget(strCandidate);
+ int delta = qAbs(m_length - strCandidate.size());
+ int score = ( (intersection(*m_cm, cmTarget).worth() + 1) << 10 ) /
+ ( reunion(*m_cm, cmTarget).worth() + (delta << 1) + 1 );
+ return score;
+}
+
+StringSimilarityMatcher::~StringSimilarityMatcher()
+{
+ delete m_cm;
+}
+
+/**
+ * Checks how similar two strings are.
+ * The return value is the score, and a higher score is more similar
+ * than one with a low score.
+ * Linguist considers a score over 190 to be a good match.
+ * \sa StringSimilarityMatcher
+ */
+int getSimilarityScore(const QString &str1, const QString &str2)
+{
+ CoMatrix cmTarget(str2);
+ CoMatrix cm(str1);
+ int delta = qAbs(str1.size() - str2.size());
+
+ int score = ( (intersection(cm, cmTarget).worth() + 1) << 10 )
+ / ( reunion(cm, cmTarget).worth() + (delta << 1) + 1 );
+
+ return score;
+}
+
+CandidateList similarTextHeuristicCandidates(const Translator *tor,
+ const QString &text, int maxCandidates)
+{
+ QList<int> scores;
+ CandidateList candidates;
+
+ TML all = tor->translatedMessages();
+
+ foreach (const TranslatorMessage &mtm, all) {
+ if (mtm.type() == TranslatorMessage::Unfinished
+ || mtm.translation().isEmpty())
+ continue;
+
+ QString s = mtm.sourceText();
+ int score = getSimilarityScore(s, text);
+
+ if (candidates.size() == maxCandidates && score > scores[maxCandidates - 1] )
+ candidates.removeLast();
+
+ if (candidates.size() < 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;
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/shared/simtexth.h b/src/linguist/shared/simtexth.h
new file mode 100644
index 000000000..efbe75036
--- /dev/null
+++ b/src/linguist/shared/simtexth.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** 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 SIMTEXTH_H
+#define SIMTEXTH_H
+
+const int textSimilarityThreshold = 190;
+
+#include <QString>
+#include <QList>
+
+QT_BEGIN_NAMESPACE
+
+class Translator;
+
+struct Candidate
+{
+ Candidate() {}
+ Candidate(const QString& source0, const QString &target0)
+ : source(source0), target(target0)
+ {}
+
+ QString source;
+ QString target;
+};
+
+inline bool operator==( const Candidate& c, const Candidate& d ) {
+ return c.target == d.target && c.source == d.source;
+}
+inline bool operator!=( const Candidate& c, const Candidate& d ) {
+ return !operator==( c, d );
+}
+
+typedef QList<Candidate> CandidateList;
+
+struct CoMatrix;
+/**
+ * This class is more efficient for searching through a large array of candidate strings, since we only
+ * have to construct the CoMatrix for the \a stringToMatch once,
+ * after that we just call getSimilarityScore(strCandidate).
+ * \sa getSimilarityScore
+ */
+class StringSimilarityMatcher {
+public:
+ StringSimilarityMatcher(const QString &stringToMatch);
+ ~StringSimilarityMatcher();
+ int getSimilarityScore(const QString &strCandidate);
+
+private:
+ CoMatrix *m_cm;
+ int m_length;
+};
+
+int getSimilarityScore(const QString &str1, const QString &str2);
+
+CandidateList similarTextHeuristicCandidates( const Translator *tor,
+ const QString &text,
+ int maxCandidates );
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/linguist/shared/translator.cpp b/src/linguist/shared/translator.cpp
new file mode 100644
index 000000000..1e5f2cba5
--- /dev/null
+++ b/src/linguist/shared/translator.cpp
@@ -0,0 +1,759 @@
+/****************************************************************************
+**
+** 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 "translator.h"
+
+#include "simtexth.h"
+
+#include <iostream>
+
+#include <stdio.h>
+#ifdef Q_OS_WIN
+// required for _setmode, to avoid _O_TEXT streams...
+# ifdef Q_OS_WINCE
+# include <stdlib.h>
+# else
+# include <io.h> // for _setmode
+# include <fcntl.h> // for _O_BINARY
+# endif
+#endif
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextStream>
+
+#include <private/qtranslator_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_BOOTSTRAPPED
+QString QObject::tr(const char *sourceText, const char *, int n)
+{
+ QString ret = QString::fromLatin1(sourceText);
+ if (n >= 0)
+ ret.replace(QLatin1String("%n"), QString::number(n));
+ return ret;
+}
+#endif
+
+Translator::Translator() :
+ m_codec(QTextCodec::codecForName("ISO-8859-1")),
+ m_locationsType(AbsoluteLocations)
+{
+}
+
+void Translator::registerFileFormat(const FileFormat &format)
+{
+ //qDebug() << "Translator: Registering format " << format.extension;
+ QList<Translator::FileFormat> &formats = registeredFileFormats();
+ for (int i = 0; i < formats.size(); ++i)
+ if (format.fileType == formats[i].fileType && format.priority < formats[i].priority) {
+ formats.insert(i, format);
+ return;
+ }
+ formats.append(format);
+}
+
+QList<Translator::FileFormat> &Translator::registeredFileFormats()
+{
+ static QList<Translator::FileFormat> theFormats;
+ return theFormats;
+}
+
+void Translator::replaceSorted(const TranslatorMessage &msg)
+{
+ int index = find(msg);
+ if (index == -1)
+ appendSorted(msg);
+ else
+ m_messages[index] = msg;
+}
+
+void Translator::extend(const TranslatorMessage &msg)
+{
+ int index = find(msg);
+ if (index == -1) {
+ m_messages.append(msg);
+ } else {
+ TranslatorMessage &emsg = m_messages[index];
+ emsg.addReferenceUniq(msg.fileName(), msg.lineNumber());
+ if (!msg.extraComment().isEmpty()) {
+ QString cmt = emsg.extraComment();
+ if (!cmt.isEmpty())
+ cmt.append(QLatin1String("\n----------\n"));
+ cmt.append(msg.extraComment());
+ emsg.setExtraComment(cmt);
+ }
+ if (msg.isUtf8() != emsg.isUtf8()) {
+ emsg.setUtf8(true);
+ emsg.setNonUtf8(true);
+ }
+ }
+}
+
+void Translator::append(const TranslatorMessage &msg)
+{
+ m_messages.append(msg);
+}
+
+void Translator::appendSorted(const TranslatorMessage &msg)
+{
+ int msgLine = msg.lineNumber();
+ if (msgLine < 0) {
+ m_messages.append(msg);
+ return;
+ }
+
+ int bestIdx = 0; // Best insertion point found so far
+ int bestScore = 0; // Its category: 0 = no hit, 1 = pre or post, 2 = middle
+ int bestSize = 0; // The length of the region. Longer is better within one category.
+
+ // The insertion point to use should this region turn out to be the best one so far
+ int thisIdx = 0;
+ int thisScore = 0;
+ int thisSize = 0;
+ // Working vars
+ int prevLine = 0;
+ int curIdx = 0;
+ foreach (const TranslatorMessage &mit, m_messages) {
+ bool sameFile = mit.fileName() == msg.fileName() && mit.context() == msg.context();
+ int curLine;
+ if (sameFile && (curLine = mit.lineNumber()) >= prevLine) {
+ if (msgLine >= prevLine && msgLine < curLine) {
+ thisIdx = curIdx;
+ thisScore = thisSize ? 2 : 1;
+ }
+ ++thisSize;
+ prevLine = curLine;
+ } else {
+ if (thisSize) {
+ if (!thisScore) {
+ thisIdx = curIdx;
+ thisScore = 1;
+ }
+ if (thisScore > bestScore || (thisScore == bestScore && thisSize > bestSize)) {
+ bestIdx = thisIdx;
+ bestScore = thisScore;
+ bestSize = thisSize;
+ }
+ thisScore = 0;
+ thisSize = sameFile ? 1 : 0;
+ prevLine = 0;
+ }
+ }
+ ++curIdx;
+ }
+ if (thisSize && !thisScore) {
+ thisIdx = curIdx;
+ thisScore = 1;
+ }
+ if (thisScore > bestScore || (thisScore == bestScore && thisSize > bestSize))
+ m_messages.insert(thisIdx, msg);
+ else if (bestScore)
+ m_messages.insert(bestIdx, msg);
+ else
+ m_messages.append(msg);
+}
+
+static QString guessFormat(const QString &filename, const QString &format)
+{
+ if (format != QLatin1String("auto"))
+ return format;
+
+ foreach (const Translator::FileFormat &fmt, Translator::registeredFileFormats()) {
+ if (filename.endsWith(QLatin1Char('.') + fmt.extension, Qt::CaseInsensitive))
+ return fmt.extension;
+ }
+
+ // the default format.
+ // FIXME: change to something more widely distributed later.
+ return QLatin1String("ts");
+}
+
+bool Translator::load(const QString &filename, ConversionData &cd, const QString &format)
+{
+ cd.m_sourceDir = QFileInfo(filename).absoluteDir();
+ cd.m_sourceFileName = filename;
+
+ QFile file;
+ if (filename.isEmpty() || filename == QLatin1String("-")) {
+#ifdef Q_OS_WIN
+ // QFile is broken for text files
+# ifdef Q_OS_WINCE
+ ::_setmode(stdin, _O_BINARY);
+# else
+ ::_setmode(0, _O_BINARY);
+# endif
+#endif
+ if (!file.open(stdin, QIODevice::ReadOnly)) {
+ cd.appendError(QString::fromLatin1("Cannot open stdin!? (%1)")
+ .arg(file.errorString()));
+ return false;
+ }
+ } else {
+ file.setFileName(filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ cd.appendError(QString::fromLatin1("Cannot open %1: %2")
+ .arg(filename, file.errorString()));
+ return false;
+ }
+ }
+
+ QString fmt = guessFormat(filename, format);
+
+ foreach (const FileFormat &format, registeredFileFormats()) {
+ if (fmt == format.extension) {
+ if (format.loader)
+ return (*format.loader)(*this, file, cd);
+ cd.appendError(QString(QLatin1String("No loader for format %1 found"))
+ .arg(fmt));
+ return false;
+ }
+ }
+
+ cd.appendError(QString(QLatin1String("Unknown format %1 for file %2"))
+ .arg(format, filename));
+ return false;
+}
+
+
+bool Translator::save(const QString &filename, ConversionData &cd, const QString &format) const
+{
+ QFile file;
+ if (filename.isEmpty() || filename == QLatin1String("-")) {
+#ifdef Q_OS_WIN
+ // QFile is broken for text files
+# ifdef Q_OS_WINCE
+ ::_setmode(stdout, _O_BINARY);
+# else
+ ::_setmode(1, _O_BINARY);
+# endif
+#endif
+ if (!file.open(stdout, QIODevice::WriteOnly)) {
+ cd.appendError(QString::fromLatin1("Cannot open stdout!? (%1)")
+ .arg(file.errorString()));
+ return false;
+ }
+ } else {
+ file.setFileName(filename);
+ if (!file.open(QIODevice::WriteOnly)) {
+ cd.appendError(QString::fromLatin1("Cannot create %1: %2")
+ .arg(filename, file.errorString()));
+ return false;
+ }
+ }
+
+ QString fmt = guessFormat(filename, format);
+ cd.m_targetDir = QFileInfo(filename).absoluteDir();
+
+ foreach (const FileFormat &format, registeredFileFormats()) {
+ if (fmt == format.extension) {
+ if (format.saver)
+ return (*format.saver)(*this, file, cd);
+ cd.appendError(QString(QLatin1String("Cannot save %1 files")).arg(fmt));
+ return false;
+ }
+ }
+
+ cd.appendError(QString(QLatin1String("Unknown format %1 for file %2"))
+ .arg(format).arg(filename));
+ return false;
+}
+
+QString Translator::makeLanguageCode(QLocale::Language language, QLocale::Country country)
+{
+ QLocale locale(language, country);
+ if (country == QLocale::AnyCountry) {
+ QString languageCode = locale.name().section(QLatin1Char('_'), 0, 0);
+ if (languageCode.length() <= 3)
+ return languageCode;
+ return QString();
+ } else {
+ return locale.name();
+ }
+}
+
+void Translator::languageAndCountry(const QString &languageCode,
+ QLocale::Language *lang, QLocale::Country *country)
+{
+ QLocale locale(languageCode);
+ if (lang)
+ *lang = locale.language();
+
+ if (country) {
+ if (languageCode.indexOf(QLatin1Char('_')) != -1)
+ *country = locale.country();
+ else
+ *country = QLocale::AnyCountry;
+ }
+}
+
+bool Translator::release(QFile *iod, ConversionData &cd) const
+{
+ foreach (const FileFormat &format, registeredFileFormats()) {
+ if (format.extension == QLatin1String("qm"))
+ return (*format.saver)(*this, *iod, cd);
+ }
+ cd.appendError(QLatin1String("No .qm saver available."));
+ return false;
+}
+
+int Translator::find(const TranslatorMessage &msg) const
+{
+ for (int i = 0; i < m_messages.count(); ++i) {
+ const TranslatorMessage &tmsg = m_messages.at(i);
+ if (msg.id().isEmpty() || tmsg.id().isEmpty()) {
+ if (msg.context() == tmsg.context()
+ && msg.sourceText() == tmsg.sourceText()
+ && msg.comment() == tmsg.comment())
+ return i;
+ } else {
+ if (msg.id() == tmsg.id())
+ return i;
+ }
+ }
+ return -1;
+}
+
+TranslatorMessage Translator::find(const QString &context,
+ const QString &comment, const TranslatorMessage::References &refs) const
+{
+ if (!refs.isEmpty()) {
+ for (TMM::ConstIterator it = m_messages.constBegin(); it != m_messages.constEnd(); ++it) {
+ if (it->context() == context && it->comment() == comment)
+ foreach (const TranslatorMessage::Reference &itref, it->allReferences())
+ foreach (const TranslatorMessage::Reference &ref, refs)
+ if (itref == ref)
+ return *it;
+ }
+ }
+ return TranslatorMessage();
+}
+
+bool Translator::contains(const QString &context) const
+{
+ foreach (const TranslatorMessage &msg, m_messages)
+ if (msg.context() == context && msg.sourceText().isEmpty() && msg.id().isEmpty())
+ return true;
+ return false;
+}
+
+TranslatorMessage Translator::find(const QString &context) const
+{
+ foreach (const TranslatorMessage &msg, m_messages)
+ if (msg.context() == context && msg.sourceText().isEmpty() && msg.id().isEmpty())
+ return msg;
+ return TranslatorMessage();
+}
+
+void Translator::stripObsoleteMessages()
+{
+ TMM newmm;
+ for (TMM::ConstIterator it = m_messages.begin(); it != m_messages.end(); ++it)
+ if (it->type() != TranslatorMessage::Obsolete)
+ newmm.append(*it);
+ m_messages = newmm;
+}
+
+void Translator::stripFinishedMessages()
+{
+ TMM newmm;
+ for (TMM::ConstIterator it = m_messages.begin(); it != m_messages.end(); ++it)
+ if (it->type() != TranslatorMessage::Finished)
+ newmm.append(*it);
+ m_messages = newmm;
+}
+
+void Translator::stripEmptyContexts()
+{
+ TMM newmm;
+ for (TMM::ConstIterator it = m_messages.begin(); it != m_messages.end(); ++it)
+ if (it->sourceText() != QLatin1String(ContextComment))
+ newmm.append(*it);
+ m_messages = newmm;
+}
+
+void Translator::stripNonPluralForms()
+{
+ TMM newmm;
+ for (TMM::ConstIterator it = m_messages.begin(); it != m_messages.end(); ++it)
+ if (it->isPlural())
+ newmm.append(*it);
+ m_messages = newmm;
+}
+
+void Translator::stripIdenticalSourceTranslations()
+{
+ TMM newmm;
+ for (TMM::ConstIterator it = m_messages.begin(); it != m_messages.end(); ++it) {
+ // we need to have just one translation, and it be equal to the source
+ if (it->translations().count() != 1)
+ newmm.append(*it);
+ else if (it->translation() != it->sourceText())
+ newmm.append(*it);
+ }
+ m_messages = newmm;
+}
+
+void Translator::dropTranslations()
+{
+ for (TMM::Iterator it = m_messages.begin(); it != m_messages.end(); ++it) {
+ if (it->type() == TranslatorMessage::Finished)
+ it->setType(TranslatorMessage::Unfinished);
+ it->setTranslation(QString());
+ }
+}
+
+void Translator::dropUiLines()
+{
+ QString uiXt = QLatin1String(".ui");
+ QString juiXt = QLatin1String(".jui");
+ for (TMM::Iterator it = m_messages.begin(); it != m_messages.end(); ++it) {
+ QHash<QString, int> have;
+ QList<TranslatorMessage::Reference> refs;
+ foreach (const TranslatorMessage::Reference &itref, it->allReferences()) {
+ const QString &fn = itref.fileName();
+ if (fn.endsWith(uiXt) || fn.endsWith(juiXt)) {
+ if (++have[fn] == 1)
+ refs.append(TranslatorMessage::Reference(fn, -1));
+ } else {
+ refs.append(itref);
+ }
+ }
+ it->setReferences(refs);
+ }
+}
+
+struct TranslatorMessageIdPtr {
+ explicit TranslatorMessageIdPtr(const TranslatorMessage &tm)
+ {
+ ptr = &tm;
+ }
+
+ inline const TranslatorMessage *operator->() const
+ {
+ return ptr;
+ }
+
+ const TranslatorMessage *ptr;
+};
+
+Q_DECLARE_TYPEINFO(TranslatorMessageIdPtr, Q_MOVABLE_TYPE);
+
+inline int qHash(TranslatorMessageIdPtr tmp)
+{
+ return qHash(tmp->id());
+}
+
+inline bool operator==(TranslatorMessageIdPtr tmp1, TranslatorMessageIdPtr tmp2)
+{
+ return tmp1->id() == tmp2->id();
+}
+
+struct TranslatorMessageContentPtr {
+ explicit TranslatorMessageContentPtr(const TranslatorMessage &tm)
+ {
+ ptr = &tm;
+ }
+
+ inline const TranslatorMessage *operator->() const
+ {
+ return ptr;
+ }
+
+ const TranslatorMessage *ptr;
+};
+
+Q_DECLARE_TYPEINFO(TranslatorMessageContentPtr, Q_MOVABLE_TYPE);
+
+inline int qHash(TranslatorMessageContentPtr tmp)
+{
+ int hash = qHash(tmp->context()) ^ qHash(tmp->sourceText());
+ if (!tmp->sourceText().isEmpty())
+ // Special treatment for context comments (empty source).
+ hash ^= qHash(tmp->comment());
+ return hash;
+}
+
+inline bool operator==(TranslatorMessageContentPtr tmp1, TranslatorMessageContentPtr tmp2)
+{
+ if (tmp1->context() != tmp2->context() || tmp1->sourceText() != tmp2->sourceText())
+ return false;
+ // Special treatment for context comments (empty source).
+ if (tmp1->sourceText().isEmpty())
+ return true;
+ return tmp1->comment() == tmp2->comment();
+}
+
+Translator::Duplicates Translator::resolveDuplicates()
+{
+ Duplicates dups;
+ QHash<TranslatorMessageIdPtr, int> idRefs;
+ QHash<TranslatorMessageContentPtr, int> contentRefs;
+ for (int i = 0; i < m_messages.count();) {
+ const TranslatorMessage &msg = m_messages.at(i);
+ TranslatorMessage *omsg;
+ int oi;
+ QSet<int> *pDup;
+ if (!msg.id().isEmpty()) {
+ QHash<TranslatorMessageIdPtr, int>::ConstIterator it =
+ idRefs.constFind(TranslatorMessageIdPtr(msg));
+ if (it != idRefs.constEnd()) {
+ oi = *it;
+ omsg = &m_messages[oi];
+ pDup = &dups.byId;
+ goto gotDupe;
+ }
+ }
+ {
+ QHash<TranslatorMessageContentPtr, int>::ConstIterator it =
+ contentRefs.constFind(TranslatorMessageContentPtr(msg));
+ if (it != contentRefs.constEnd()) {
+ oi = *it;
+ omsg = &m_messages[oi];
+ if (msg.id().isEmpty() || omsg->id().isEmpty()) {
+ if (!msg.id().isEmpty() && omsg->id().isEmpty()) {
+ omsg->setId(msg.id());
+ idRefs[TranslatorMessageIdPtr(*omsg)] = oi;
+ }
+ pDup = &dups.byContents;
+ goto gotDupe;
+ }
+ // This is really a content dupe, but with two distinct IDs.
+ }
+ }
+ if (!msg.id().isEmpty())
+ idRefs[TranslatorMessageIdPtr(msg)] = i;
+ contentRefs[TranslatorMessageContentPtr(msg)] = i;
+ ++i;
+ continue;
+ gotDupe:
+ if (omsg->isUtf8() != msg.isUtf8() && !omsg->isNonUtf8()) {
+ // Dual-encoded message
+ omsg->setUtf8(true);
+ omsg->setNonUtf8(true);
+ } else {
+ // Duplicate
+ pDup->insert(oi);
+ }
+ if (!omsg->isTranslated() && msg.isTranslated())
+ omsg->setTranslations(msg.translations());
+ m_messages.removeAt(i);
+ }
+ return dups;
+}
+
+void Translator::reportDuplicates(const Duplicates &dupes,
+ const QString &fileName, bool verbose)
+{
+ if (!dupes.byId.isEmpty() || !dupes.byContents.isEmpty()) {
+ std::cerr << "Warning: dropping duplicate messages in '" << qPrintable(fileName);
+ if (!verbose) {
+ std::cerr << "'\n(try -verbose for more info).\n";
+ } else {
+ std::cerr << "':\n";
+ foreach (int i, dupes.byId)
+ std::cerr << "\n* ID: " << qPrintable(message(i).id()) << std::endl;
+ foreach (int j, dupes.byContents) {
+ const TranslatorMessage &msg = message(j);
+ std::cerr << "\n* Context: " << qPrintable(msg.context())
+ << "\n* Source: " << qPrintable(msg.sourceText()) << std::endl;
+ if (!msg.comment().isEmpty())
+ std::cerr << "* Comment: " << qPrintable(msg.comment()) << std::endl;
+ }
+ std::cerr << std::endl;
+ }
+ }
+}
+
+// Used by lupdate to be able to search using absolute paths during merging
+void Translator::makeFileNamesAbsolute(const QDir &originalPath)
+{
+ TMM newmm;
+ for (TMM::iterator it = m_messages.begin(); it != m_messages.end(); ++it) {
+ TranslatorMessage msg = *it;
+ msg.setReferences(TranslatorMessage::References());
+ foreach (const TranslatorMessage::Reference &ref, it->allReferences()) {
+ QString fileName = ref.fileName();
+ QFileInfo fi (fileName);
+ if (fi.isRelative())
+ fileName = originalPath.absoluteFilePath(fileName);
+ msg.addReference(fileName, ref.lineNumber());
+ }
+ newmm.append(msg);
+ }
+ m_messages = newmm;
+}
+
+QList<TranslatorMessage> Translator::messages() const
+{
+ return m_messages;
+}
+
+QList<TranslatorMessage> Translator::translatedMessages() const
+{
+ TMM result;
+ for (TMM::ConstIterator it = m_messages.begin(); it != m_messages.end(); ++it)
+ if (it->type() == TranslatorMessage::Finished)
+ result.append(*it);
+ return result;
+}
+
+QStringList Translator::normalizedTranslations(const TranslatorMessage &msg, int numPlurals)
+{
+ QStringList translations = msg.translations();
+ int numTranslations = msg.isPlural() ? numPlurals : 1;
+
+ // make sure that the stringlist always have the size of the
+ // language's current numerus, or 1 if its not plural
+ if (translations.count() > numTranslations) {
+ for (int i = translations.count(); i > numTranslations; --i)
+ translations.removeLast();
+ } else if (translations.count() < numTranslations) {
+ for (int i = translations.count(); i < numTranslations; ++i)
+ translations.append(QString());
+ }
+ return translations;
+}
+
+void Translator::normalizeTranslations(ConversionData &cd)
+{
+ bool truncated = false;
+ QLocale::Language l;
+ QLocale::Country c;
+ languageAndCountry(languageCode(), &l, &c);
+ int numPlurals = 1;
+ if (l != QLocale::C) {
+ QStringList forms;
+ if (getNumerusInfo(l, c, 0, &forms, 0))
+ numPlurals = forms.count(); // includes singular
+ }
+ for (int i = 0; i < m_messages.count(); ++i) {
+ const TranslatorMessage &msg = m_messages.at(i);
+ QStringList tlns = msg.translations();
+ int ccnt = msg.isPlural() ? numPlurals : 1;
+ if (tlns.count() != ccnt) {
+ while (tlns.count() < ccnt)
+ tlns.append(QString());
+ while (tlns.count() > ccnt) {
+ tlns.removeLast();
+ truncated = true;
+ }
+ TranslatorMessage msg2(msg);
+ msg2.setTranslations(tlns);
+ m_messages[i] = msg2;
+ }
+ }
+ if (truncated)
+ cd.appendError(QLatin1String(
+ "Removed plural forms as the target language has less "
+ "forms.\nIf this sounds wrong, possibly the target language is "
+ "not set or recognized."));
+}
+
+QString Translator::guessLanguageCodeFromFileName(const QString &filename)
+{
+ QString str = filename;
+ foreach (const FileFormat &format, registeredFileFormats()) {
+ if (str.endsWith(format.extension)) {
+ str = str.left(str.size() - format.extension.size() - 1);
+ break;
+ }
+ }
+ static QRegExp re(QLatin1String("[\\._]"));
+ while (true) {
+ QLocale locale(str);
+ //qDebug() << "LANGUAGE FROM " << str << "LANG: " << locale.language();
+ if (locale.language() != QLocale::C) {
+ //qDebug() << "FOUND " << locale.name();
+ return locale.name();
+ }
+ int pos = str.indexOf(re);
+ if (pos == -1)
+ break;
+ str = str.mid(pos + 1);
+ }
+ //qDebug() << "LANGUAGE GUESSING UNSUCCESSFUL";
+ return QString();
+}
+
+bool Translator::hasExtra(const QString &key) const
+{
+ return m_extra.contains(key);
+}
+
+QString Translator::extra(const QString &key) const
+{
+ return m_extra[key];
+}
+
+void Translator::setExtra(const QString &key, const QString &value)
+{
+ m_extra[key] = value;
+}
+
+void Translator::setCodecName(const QByteArray &name)
+{
+ QTextCodec *codec = QTextCodec::codecForName(name);
+ if (!codec) {
+ if (!name.isEmpty())
+ std::cerr << "No QTextCodec for " << name.constData() << " available. Using Latin1.\n";
+ m_codec = QTextCodec::codecForName("ISO-8859-1");
+ } else {
+ m_codec = codec;
+ }
+}
+
+QByteArray Translator::codecName() const
+{
+ return m_codec->name();
+}
+
+void Translator::dump() const
+{
+ for (int i = 0; i != messageCount(); ++i)
+ message(i).dump();
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/shared/translator.h b/src/linguist/shared/translator.h
new file mode 100644
index 000000000..6a619414b
--- /dev/null
+++ b/src/linguist/shared/translator.h
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** 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 METATRANSLATOR_H
+#define METATRANSLATOR_H
+
+#include "translatormessage.h"
+
+#include <QDir>
+#include <QList>
+#include <QLocale>
+#include <QMultiHash>
+#include <QString>
+#include <QSet>
+
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_BOOTSTRAPPED
+class QObject {
+public:
+ static QString tr(const char *sourceText, const char * = 0, int n = -1);
+};
+class QCoreApplication : public QObject {
+public:
+ enum Encoding { CodecForTr };
+ static QString translate(const char *, const char *sourceText, const char * = 0,
+ Encoding = CodecForTr, int n = -1)
+ { return tr(sourceText, 0, n); }
+};
+#endif
+
+class QIODevice;
+
+// A struct of "interesting" data passed to and from the load and save routines
+class ConversionData
+{
+public:
+ ConversionData() :
+ m_verbose(false),
+ m_ignoreUnfinished(false),
+ m_sortContexts(false),
+ m_noUiLines(false),
+ m_idBased(false),
+ m_saveMode(SaveEverything)
+ {}
+
+ // tag manipulation
+ const QStringList &dropTags() const { return m_dropTags; }
+ QStringList &dropTags() { return m_dropTags; }
+ const QDir &targetDir() const { return m_targetDir; }
+ bool isVerbose() const { return m_verbose; }
+ bool ignoreUnfinished() const { return m_ignoreUnfinished; }
+ bool sortContexts() const { return m_sortContexts; }
+
+ void appendError(const QString &error) { m_errors.append(error); }
+ QString error() const { return m_errors.isEmpty() ? QString() : m_errors.join(QLatin1String("\n")) + QLatin1Char('\n'); }
+ QStringList errors() const { return m_errors; }
+ void clearErrors() { m_errors.clear(); }
+
+public:
+ QString m_defaultContext;
+ QByteArray m_codecForSource; // CPP, PO & QM specific
+ QByteArray m_outputCodec; // CPP & PO specific
+ QString m_unTrPrefix; // QM specific
+ QString m_sourceFileName;
+ QString m_targetFileName;
+ QDir m_sourceDir;
+ QDir m_targetDir; // FIXME: TS specific
+ QSet<QString> m_projectRoots;
+ QMultiHash<QString, QString> m_allCSources;
+ QStringList m_includePath;
+ QStringList m_dropTags; // tags to be dropped
+ QStringList m_errors;
+ bool m_verbose;
+ bool m_ignoreUnfinished;
+ bool m_sortContexts;
+ bool m_noUiLines;
+ bool m_idBased;
+ TranslatorSaveMode m_saveMode;
+};
+
+class Translator
+{
+public:
+ Translator();
+
+ bool load(const QString &filename, ConversionData &err, const QString &format /* = "auto" */);
+ bool save(const QString &filename, ConversionData &err, const QString &format /* = "auto" */) const;
+ bool release(QFile *iod, ConversionData &cd) const;
+
+ int find(const TranslatorMessage &msg) const;
+ TranslatorMessage find(const QString &context,
+ const QString &comment, const TranslatorMessage::References &refs) const;
+
+ bool contains(const QString &context) const;
+ TranslatorMessage find(const QString &context) const;
+
+ void replaceSorted(const TranslatorMessage &msg);
+ void extend(const TranslatorMessage &msg); // Only for single-location messages
+ void append(const TranslatorMessage &msg);
+ void appendSorted(const TranslatorMessage &msg);
+
+ void stripObsoleteMessages();
+ void stripFinishedMessages();
+ void stripEmptyContexts();
+ void stripNonPluralForms();
+ void stripIdenticalSourceTranslations();
+ void dropTranslations();
+ void dropUiLines();
+ void makeFileNamesAbsolute(const QDir &originalPath);
+
+ struct Duplicates { QSet<int> byId, byContents; };
+ Duplicates resolveDuplicates();
+ void reportDuplicates(const Duplicates &dupes, const QString &fileName, bool verbose);
+
+ void setCodecName(const QByteArray &name);
+ void setCodec(QTextCodec *codec) { m_codec = codec; }
+ QByteArray codecName() const;
+ QTextCodec *codec() const { return m_codec; }
+
+ QString languageCode() const { return m_language; }
+ QString sourceLanguageCode() const { return m_sourceLanguage; }
+
+ enum LocationsType { DefaultLocations, NoLocations, RelativeLocations, AbsoluteLocations };
+ void setLocationsType(LocationsType lt) { m_locationsType = lt; }
+ LocationsType locationsType() const { return m_locationsType; }
+
+ static QString makeLanguageCode(QLocale::Language language, QLocale::Country country);
+ static void languageAndCountry(const QString &languageCode,
+ QLocale::Language *lang, QLocale::Country *country);
+ void setLanguageCode(const QString &languageCode) { m_language = languageCode; }
+ void setSourceLanguageCode(const QString &languageCode) { m_sourceLanguage = languageCode; }
+ static QString guessLanguageCodeFromFileName(const QString &fileName);
+ QList<TranslatorMessage> messages() const;
+ QList<TranslatorMessage> translatedMessages() const;
+ static QStringList normalizedTranslations(const TranslatorMessage &m, int numPlurals);
+ void normalizeTranslations(ConversionData &cd);
+ QStringList normalizedTranslations(const TranslatorMessage &m, ConversionData &cd, bool *ok) const;
+
+ int messageCount() const { return m_messages.size(); }
+ TranslatorMessage &message(int i) { return m_messages[i]; }
+ const TranslatorMessage &message(int i) const { return m_messages.at(i); }
+ void dump() const;
+
+ // additional file format specific data
+ // note: use '<fileformat>:' as prefix for file format specific members,
+ // e.g. "po-flags", "po-msgid_plural"
+ typedef TranslatorMessage::ExtraData ExtraData;
+ QString extra(const QString &ba) const;
+ void setExtra(const QString &ba, const QString &var);
+ bool hasExtra(const QString &ba) const;
+ const ExtraData &extras() const { return m_extra; }
+ void setExtras(const ExtraData &extras) { m_extra = extras; }
+
+ // registration of file formats
+ typedef bool (*SaveFunction)(const Translator &, QIODevice &out, ConversionData &data);
+ typedef bool (*LoadFunction)(Translator &, QIODevice &in, ConversionData &data);
+ struct FileFormat {
+ FileFormat() : loader(0), saver(0), priority(-1) {}
+ QString extension; // such as "ts", "xlf", ...
+ QString description; // human-readable description
+ LoadFunction loader;
+ SaveFunction saver;
+ enum FileType { TranslationSource, TranslationBinary } fileType;
+ int priority; // 0 = highest, -1 = invisible
+ };
+ static void registerFileFormat(const FileFormat &format);
+ static QList<FileFormat> &registeredFileFormats();
+
+ enum {
+ TextVariantSeparator = 0x2762, // some weird character nobody ever heard of :-D
+ BinaryVariantSeparator = 0x9c // unicode "STRING TERMINATOR"
+ };
+
+private:
+ typedef QList<TranslatorMessage> TMM; // int stores the sequence position.
+
+ TMM m_messages;
+ QTextCodec *m_codec;
+ LocationsType m_locationsType;
+
+ // A string beginning with a 2 or 3 letter language code (ISO 639-1
+ // or ISO-639-2), followed by the optional country variant to distinguish
+ // between country-specific variations of the language. The language code
+ // and country code are always separated by '_'
+ // Note that the language part can also be a 3-letter ISO 639-2 code.
+ // Legal examples:
+ // 'pt' portuguese, assumes portuguese from portugal
+ // 'pt_BR' Brazilian portuguese (ISO 639-1 language code)
+ // 'por_BR' Brazilian portuguese (ISO 639-2 language code)
+ QString m_language;
+ QString m_sourceLanguage;
+ ExtraData m_extra;
+};
+
+bool getNumerusInfo(QLocale::Language language, QLocale::Country country,
+ QByteArray *rules, QStringList *forms, const char **gettextRules);
+
+/*
+ This is a quick hack. The proper way to handle this would be
+ to extend Translator's interface.
+*/
+#define ContextComment "QT_LINGUIST_INTERNAL_CONTEXT_COMMENT"
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/linguist/shared/translatormessage.cpp b/src/linguist/shared/translatormessage.cpp
new file mode 100644
index 000000000..8a0408a21
--- /dev/null
+++ b/src/linguist/shared/translatormessage.cpp
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** 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 "translatormessage.h"
+
+#include <qplatformdefs.h>
+
+#ifndef QT_NO_TRANSLATION
+
+#include <QDataStream>
+#include <QDebug>
+
+#include <stdlib.h>
+
+
+QT_BEGIN_NAMESPACE
+
+TranslatorMessage::TranslatorMessage()
+ : m_lineNumber(-1), m_type(Unfinished), m_utf8(false), m_nonUtf8(false), m_plural(false)
+{
+}
+
+TranslatorMessage::TranslatorMessage(const QString &context,
+ const QString &sourceText, const QString &comment,
+ const QString &userData,
+ const QString &fileName, int lineNumber, const QStringList &translations,
+ Type type, bool plural)
+ : m_context(context), m_sourcetext(sourceText), m_comment(comment),
+ m_userData(userData),
+ m_translations(translations), m_fileName(fileName), m_lineNumber(lineNumber),
+ m_type(type), m_utf8(false), m_nonUtf8(false), m_plural(plural)
+{
+}
+
+void TranslatorMessage::addReference(const QString &fileName, int lineNumber)
+{
+ if (m_fileName.isEmpty()) {
+ m_fileName = fileName;
+ m_lineNumber = lineNumber;
+ } else {
+ m_extraRefs.append(Reference(fileName, lineNumber));
+ }
+}
+
+void TranslatorMessage::addReferenceUniq(const QString &fileName, int lineNumber)
+{
+ if (m_fileName.isEmpty()) {
+ m_fileName = fileName;
+ m_lineNumber = lineNumber;
+ } else {
+ if (fileName == m_fileName && lineNumber == m_lineNumber)
+ return;
+ if (!m_extraRefs.isEmpty()) // Rather common case, so special-case it
+ foreach (const Reference &ref, m_extraRefs)
+ if (fileName == ref.fileName() && lineNumber == ref.lineNumber())
+ return;
+ m_extraRefs.append(Reference(fileName, lineNumber));
+ }
+}
+
+void TranslatorMessage::clearReferences()
+{
+ m_fileName.clear();
+ m_lineNumber = -1;
+ m_extraRefs.clear();
+}
+
+void TranslatorMessage::setReferences(const TranslatorMessage::References &refs0)
+{
+ if (!refs0.isEmpty()) {
+ References refs = refs0;
+ const Reference &ref = refs.takeFirst();
+ m_fileName = ref.fileName();
+ m_lineNumber = ref.lineNumber();
+ m_extraRefs = refs;
+ } else {
+ clearReferences();
+ }
+}
+
+TranslatorMessage::References TranslatorMessage::allReferences() const
+{
+ References refs;
+ if (!m_fileName.isEmpty()) {
+ refs.append(Reference(m_fileName, m_lineNumber));
+ refs += m_extraRefs;
+ }
+ return refs;
+}
+
+static bool needs8BitHelper(const QString &ba)
+{
+ for (int i = ba.size(); --i >= 0; )
+ if (ba.at(i).unicode() >= 0x80)
+ return true;
+ return false;
+}
+
+bool TranslatorMessage::needs8Bit() const
+{
+ //dump();
+ return needs8BitHelper(m_sourcetext)
+ || needs8BitHelper(m_comment)
+ || needs8BitHelper(m_context);
+}
+
+
+bool TranslatorMessage::hasExtra(const QString &key) const
+{
+ return m_extra.contains(key);
+}
+
+QString TranslatorMessage::extra(const QString &key) const
+{
+ return m_extra[key];
+}
+
+void TranslatorMessage::setExtra(const QString &key, const QString &value)
+{
+ m_extra[key] = value;
+}
+
+void TranslatorMessage::unsetExtra(const QString &key)
+{
+ m_extra.remove(key);
+}
+
+void TranslatorMessage::dump() const
+{
+ qDebug()
+ << "\nId : " << m_id
+ << "\nContext : " << m_context
+ << "\nSource : " << m_sourcetext
+ << "\nComment : " << m_comment
+ << "\nUserData : " << m_userData
+ << "\nExtraComment : " << m_extraComment
+ << "\nTranslatorComment : " << m_translatorComment
+ << "\nTranslations : " << m_translations
+ << "\nFileName : " << m_fileName
+ << "\nLineNumber : " << m_lineNumber
+ << "\nType : " << m_type
+ << "\nPlural : " << m_plural
+ << "\nExtra : " << m_extra;
+}
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TRANSLATION
diff --git a/src/linguist/shared/translatormessage.h b/src/linguist/shared/translatormessage.h
new file mode 100644
index 000000000..4bb5dc84a
--- /dev/null
+++ b/src/linguist/shared/translatormessage.h
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** 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 TRANSLATORMESSAGE_H
+#define TRANSLATORMESSAGE_H
+
+#ifndef QT_NO_TRANSLATION
+
+#include <QString>
+#include <QStringList>
+#include <QHash>
+
+
+QT_BEGIN_NAMESPACE
+
+enum TranslatorSaveMode { SaveEverything, SaveStripped };
+
+class TranslatorMessage
+{
+public:
+ enum Type { Unfinished, Finished, Obsolete };
+ typedef QHash<QString, QString> ExtraData;
+ class Reference
+ {
+ QString m_fileName;
+ int m_lineNumber;
+ public:
+ Reference(const QString &n, int l) : m_fileName(n), m_lineNumber(l) {}
+ bool operator==(const Reference &other) const
+ { return fileName() == other.fileName() && lineNumber() == other.lineNumber(); }
+ QString fileName() const { return m_fileName; }
+ int lineNumber() const { return m_lineNumber; }
+ };
+ typedef QList<Reference> References;
+
+ TranslatorMessage();
+ TranslatorMessage(const QString &context, const QString &sourceText,
+ const QString &comment, const QString &userData,
+ const QString &fileName, int lineNumber,
+ const QStringList &translations = QStringList(),
+ Type type = Unfinished, bool plural = false);
+
+ uint hash() const;
+
+ QString id() const { return m_id; }
+ void setId(const QString &id) { m_id = id; }
+
+ QString context() const { return m_context; }
+ void setContext(const QString &context) { m_context = context; }
+
+ QString sourceText() const { return m_sourcetext; }
+ void setSourceText(const QString &sourcetext) { m_sourcetext = sourcetext; }
+ QString oldSourceText() const { return m_oldsourcetext; }
+ void setOldSourceText(const QString &oldsourcetext) { m_oldsourcetext = oldsourcetext; }
+
+ QString comment() const { return m_comment; }
+ void setComment(const QString &comment) { m_comment = comment; }
+ QString oldComment() const { return m_oldcomment; }
+ void setOldComment(const QString &oldcomment) { m_oldcomment = oldcomment; }
+
+ QStringList translations() const { return m_translations; }
+ void setTranslations(const QStringList &translations) { m_translations = translations; }
+ QString translation() const { return m_translations.value(0); }
+ void setTranslation(const QString &translation) { m_translations = QStringList(translation); }
+ void appendTranslation(const QString &translation) { m_translations.append(translation); }
+ bool isTranslated() const
+ {
+ foreach (const QString &trans, m_translations)
+ if (!trans.isEmpty())
+ return true;
+ return false;
+ }
+
+ QString fileName() const { return m_fileName; }
+ void setFileName(const QString &fileName) { m_fileName = fileName; }
+ int lineNumber() const { return m_lineNumber; }
+ void setLineNumber(int lineNumber) { m_lineNumber = lineNumber; }
+ void clearReferences();
+ void setReferences(const References &refs);
+ void addReference(const QString &fileName, int lineNumber);
+ void addReference(const Reference &ref) { addReference(ref.fileName(), ref.lineNumber()); }
+ void addReferenceUniq(const QString &fileName, int lineNumber);
+ References extraReferences() const { return m_extraRefs; }
+ References allReferences() const;
+ QString userData() const { return m_userData; }
+ void setUserData(const QString &userData) { m_userData = userData; }
+ QString extraComment() const { return m_extraComment; }
+ void setExtraComment(const QString &extraComment) { m_extraComment = extraComment; }
+ QString translatorComment() const { return m_translatorComment; }
+ void setTranslatorComment(const QString &translatorComment) { m_translatorComment = translatorComment; }
+
+ bool isNull() const { return m_sourcetext.isNull() && m_lineNumber == -1 && m_translations.isEmpty(); }
+
+ Type type() const { return m_type; }
+ void setType(Type t) { m_type = t; }
+ bool isUtf8() const { return m_utf8; } // codecForTr override
+ void setUtf8(bool on) { m_utf8 = on; }
+ bool isNonUtf8() const { return m_nonUtf8; } // codecForTr override
+ void setNonUtf8(bool on) { m_nonUtf8 = on; }
+ bool isPlural() const { return m_plural; }
+ void setPlural(bool isplural) { m_plural = isplural; }
+
+ // note: use '<fileformat>:' as prefix for file format specific members,
+ // e.g. "po-msgid_plural"
+ QString extra(const QString &ba) const;
+ void setExtra(const QString &ba, const QString &var);
+ bool hasExtra(const QString &ba) const;
+ const ExtraData &extras() const { return m_extra; }
+ void setExtras(const ExtraData &extras) { m_extra = extras; }
+ void unsetExtra(const QString &key);
+
+ bool needs8Bit() const;
+ void dump() const;
+
+private:
+ QString m_id;
+ QString m_context;
+ QString m_sourcetext;
+ QString m_oldsourcetext;
+ QString m_comment;
+ QString m_oldcomment;
+ QString m_userData;
+ ExtraData m_extra; // PO flags, PO plurals
+ QString m_extraComment;
+ QString m_translatorComment;
+ QStringList m_translations;
+ QString m_fileName;
+ int m_lineNumber;
+ References m_extraRefs;
+
+ Type m_type;
+ bool m_utf8;
+ bool m_nonUtf8;
+ bool m_plural;
+};
+
+Q_DECLARE_TYPEINFO(TranslatorMessage, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TRANSLATION
+
+#endif // TRANSLATORMESSAGE_H
diff --git a/src/linguist/shared/ts.cpp b/src/linguist/shared/ts.cpp
new file mode 100644
index 000000000..4523bc55d
--- /dev/null
+++ b/src/linguist/shared/ts.cpp
@@ -0,0 +1,781 @@
+/****************************************************************************
+**
+** 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 "translator.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QDebug>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextStream>
+
+#include <QtXml/QXmlStreamReader>
+#include <QtXml/QXmlStreamAttribute>
+
+#define STRINGIFY_INTERNAL(x) #x
+#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
+#define STRING(s) static QString str##s(QLatin1String(STRINGIFY(s)))
+
+QT_BEGIN_NAMESPACE
+
+/*
+ * The encodings are a total mess.
+ * A Translator has a codecForTr(). Each message's text will be passed to tr()
+ * in that encoding or as UTF-8 to trUtf8() if it is flagged as such.
+ * For ts 2.0, the file content is always uniformly in UTF-8. The file stores
+ * the codecForTr default and marks deviating messages accordingly.
+ * For ts 1.1, the file content is in mixed encoding. Each message is encoded
+ * the way it will be passed to tr() (with 8-bit characters encoded as numeric
+ * entities) or trUtf8(). The file stores the encoding and codecForTr in one
+ * attribute, for both the default and each deviating message.
+ */
+
+
+QDebug &operator<<(QDebug &d, const QXmlStreamAttribute &attr)
+{
+ return d << "[" << attr.name().toString() << "," << attr.value().toString() << "]";
+}
+
+
+class TSReader : public QXmlStreamReader
+{
+public:
+ TSReader(QIODevice &dev, ConversionData &cd)
+ : QXmlStreamReader(&dev), m_cd(cd)
+ {}
+
+ // the "real thing"
+ bool read(Translator &translator);
+
+private:
+ bool elementStarts(const QString &str) const
+ {
+ return isStartElement() && name() == str;
+ }
+
+ bool isWhiteSpace() const
+ {
+ return isCharacters() && text().toString().trimmed().isEmpty();
+ }
+
+ // needed to expand <byte ... />
+ QString readContents();
+ // needed to join <lengthvariant>s
+ QString readTransContents();
+
+ void handleError();
+
+ ConversionData &m_cd;
+};
+
+void TSReader::handleError()
+{
+ if (isComment())
+ return;
+ if (hasError() && error() == CustomError) // raised by readContents
+ return;
+
+ const QString loc = QString::fromLatin1("at %3:%1:%2")
+ .arg(lineNumber()).arg(columnNumber()).arg(m_cd.m_sourceFileName);
+
+ switch (tokenType()) {
+ case NoToken: // Cannot happen
+ default: // likewise
+ case Invalid:
+ raiseError(QString::fromLatin1("Parse error %1: %2").arg(loc, errorString()));
+ break;
+ case StartElement:
+ raiseError(QString::fromLatin1("Unexpected tag <%1> %2").arg(name().toString(), loc));
+ break;
+ case Characters:
+ {
+ QString tok = text().toString();
+ if (tok.length() > 30)
+ tok = tok.left(30) + QLatin1String("[...]");
+ raiseError(QString::fromLatin1("Unexpected characters '%1' %2").arg(tok, loc));
+ }
+ break;
+ case EntityReference:
+ raiseError(QString::fromLatin1("Unexpected entity '&%1;' %2").arg(name().toString(), loc));
+ break;
+ case ProcessingInstruction:
+ raiseError(QString::fromLatin1("Unexpected processing instruction %1").arg(loc));
+ break;
+ }
+}
+
+static QString byteValue(QString value)
+{
+ int base = 10;
+ if (value.startsWith(QLatin1String("x"))) {
+ base = 16;
+ value.remove(0, 1);
+ }
+ int n = value.toUInt(0, base);
+ return (n != 0) ? QString(QChar(n)) : QString();
+}
+
+QString TSReader::readContents()
+{
+ STRING(byte);
+ STRING(value);
+
+ QString result;
+ while (!atEnd()) {
+ readNext();
+ if (isEndElement()) {
+ break;
+ } else if (isCharacters()) {
+ result += text();
+ } else if (elementStarts(strbyte)) {
+ // <byte value="...">
+ result += byteValue(attributes().value(strvalue).toString());
+ readNext();
+ if (!isEndElement()) {
+ handleError();
+ break;
+ }
+ } else {
+ handleError();
+ break;
+ }
+ }
+ //qDebug() << "TEXT: " << result;
+ return result;
+}
+
+QString TSReader::readTransContents()
+{
+ STRING(lengthvariant);
+ STRING(variants);
+ STRING(yes);
+
+ if (attributes().value(strvariants) == stryes) {
+ QString result;
+ while (!atEnd()) {
+ readNext();
+ if (isEndElement()) {
+ break;
+ } else if (isWhiteSpace()) {
+ // ignore these, just whitespace
+ } else if (elementStarts(strlengthvariant)) {
+ if (!result.isEmpty())
+ result += QChar(Translator::BinaryVariantSeparator);
+ result += readContents();
+ } else {
+ handleError();
+ break;
+ }
+ }
+ return result;
+ } else {
+ return readContents();
+ }
+}
+
+bool TSReader::read(Translator &translator)
+{
+ STRING(both);
+ STRING(byte);
+ STRING(comment);
+ STRING(context);
+ STRING(defaultcodec);
+ STRING(encoding);
+ STRING(extracomment);
+ STRING(filename);
+ STRING(id);
+ STRING(language);
+ STRING(line);
+ STRING(location);
+ STRING(message);
+ STRING(name);
+ STRING(numerus);
+ STRING(numerusform);
+ STRING(obsolete);
+ STRING(oldcomment);
+ STRING(oldsource);
+ STRING(source);
+ STRING(sourcelanguage);
+ STRING(translation);
+ STRING(translatorcomment);
+ STRING(true);
+ STRING(TS);
+ STRING(type);
+ STRING(unfinished);
+ STRING(userdata);
+ STRING(utf8);
+ STRING(value);
+ //STRING(version);
+ STRING(yes);
+
+ static const QString strextrans(QLatin1String("extra-"));
+ static const QString strUtf8(QLatin1String("UTF-8"));
+
+ while (!atEnd()) {
+ readNext();
+ if (isStartDocument()) {
+ // <!DOCTYPE TS>
+ //qDebug() << attributes();
+ } else if (isEndDocument()) {
+ // <!DOCTYPE TS>
+ //qDebug() << attributes();
+ } else if (isDTD()) {
+ // <!DOCTYPE TS>
+ //qDebug() << tokenString();
+ } else if (elementStarts(strTS)) {
+ // <TS>
+ //qDebug() << "TS " << attributes();
+ QHash<QString, int> currentLine;
+ QString currentFile;
+ bool maybeRelative = false, maybeAbsolute = false;
+
+ QXmlStreamAttributes atts = attributes();
+ //QString version = atts.value(strversion).toString();
+ translator.setLanguageCode(atts.value(strlanguage).toString());
+ translator.setSourceLanguageCode(atts.value(strsourcelanguage).toString());
+ while (!atEnd()) {
+ readNext();
+ if (isEndElement()) {
+ // </TS> found, finish local loop
+ break;
+ } else if (isWhiteSpace()) {
+ // ignore these, just whitespace
+ } else if (elementStarts(strdefaultcodec)) {
+ // <defaultcodec>
+ const QString &codec = readElementText();
+ if (!codec.isEmpty())
+ translator.setCodecName(codec.toLatin1());
+ // </defaultcodec>
+ } else if (isStartElement()
+ && name().toString().startsWith(strextrans)) {
+ // <extra-...>
+ QString tag = name().toString();
+ translator.setExtra(tag.mid(6), readContents());
+ // </extra-...>
+ } else if (elementStarts(strcontext)) {
+ // <context>
+ QString context;
+ while (!atEnd()) {
+ readNext();
+ if (isEndElement()) {
+ // </context> found, finish local loop
+ break;
+ } else if (isWhiteSpace()) {
+ // ignore these, just whitespace
+ } else if (elementStarts(strname)) {
+ // <name>
+ context = readElementText();
+ // </name>
+ } else if (elementStarts(strmessage)) {
+ // <message>
+ TranslatorMessage::References refs;
+ QString currentMsgFile = currentFile;
+
+ TranslatorMessage msg;
+ msg.setId(attributes().value(strid).toString());
+ msg.setContext(context);
+ msg.setType(TranslatorMessage::Finished);
+ msg.setPlural(attributes().value(strnumerus) == stryes);
+ const QStringRef &utf8Attr = attributes().value(strutf8);
+ msg.setNonUtf8(utf8Attr == strboth);
+ msg.setUtf8(msg.isNonUtf8() || utf8Attr == strtrue
+ || attributes().value(strencoding) == strUtf8);
+ while (!atEnd()) {
+ readNext();
+ if (isEndElement()) {
+ // </message> found, finish local loop
+ msg.setReferences(refs);
+ translator.append(msg);
+ break;
+ } else if (isWhiteSpace()) {
+ // ignore these, just whitespace
+ } else if (elementStarts(strsource)) {
+ // <source>...</source>
+ msg.setSourceText(readContents());
+ } else if (elementStarts(stroldsource)) {
+ // <oldsource>...</oldsource>
+ msg.setOldSourceText(readContents());
+ } else if (elementStarts(stroldcomment)) {
+ // <oldcomment>...</oldcomment>
+ msg.setOldComment(readContents());
+ } else if (elementStarts(strextracomment)) {
+ // <extracomment>...</extracomment>
+ msg.setExtraComment(readContents());
+ } else if (elementStarts(strtranslatorcomment)) {
+ // <translatorcomment>...</translatorcomment>
+ msg.setTranslatorComment(readContents());
+ } else if (elementStarts(strlocation)) {
+ // <location/>
+ maybeAbsolute = true;
+ QXmlStreamAttributes atts = attributes();
+ QString fileName = atts.value(strfilename).toString();
+ if (fileName.isEmpty()) {
+ fileName = currentMsgFile;
+ maybeRelative = true;
+ } else {
+ if (refs.isEmpty())
+ currentFile = fileName;
+ currentMsgFile = fileName;
+ }
+ const QString lin = atts.value(strline).toString();
+ if (lin.isEmpty()) {
+ refs.append(TranslatorMessage::Reference(fileName, -1));
+ } else {
+ bool bOK;
+ int lineNo = lin.toInt(&bOK);
+ if (bOK) {
+ if (lin.startsWith(QLatin1Char('+')) || lin.startsWith(QLatin1Char('-'))) {
+ lineNo = (currentLine[fileName] += lineNo);
+ maybeRelative = true;
+ }
+ refs.append(TranslatorMessage::Reference(fileName, lineNo));
+ }
+ }
+ readContents();
+ } else if (elementStarts(strcomment)) {
+ // <comment>...</comment>
+ msg.setComment(readContents());
+ } else if (elementStarts(struserdata)) {
+ // <userdata>...</userdata>
+ msg.setUserData(readContents());
+ } else if (elementStarts(strtranslation)) {
+ // <translation>
+ QXmlStreamAttributes atts = attributes();
+ QStringRef type = atts.value(strtype);
+ if (type == strunfinished)
+ msg.setType(TranslatorMessage::Unfinished);
+ else if (type == strobsolete)
+ msg.setType(TranslatorMessage::Obsolete);
+ if (msg.isPlural()) {
+ QStringList translations;
+ while (!atEnd()) {
+ readNext();
+ if (isEndElement()) {
+ break;
+ } else if (isWhiteSpace()) {
+ // ignore these, just whitespace
+ } else if (elementStarts(strnumerusform)) {
+ translations.append(readTransContents());
+ } else {
+ handleError();
+ break;
+ }
+ }
+ msg.setTranslations(translations);
+ } else {
+ msg.setTranslation(readTransContents());
+ }
+ // </translation>
+ } else if (isStartElement()
+ && name().toString().startsWith(strextrans)) {
+ // <extra-...>
+ QString tag = name().toString();
+ msg.setExtra(tag.mid(6), readContents());
+ // </extra-...>
+ } else {
+ handleError();
+ }
+ }
+ // </message>
+ } else {
+ handleError();
+ }
+ }
+ // </context>
+ } else {
+ handleError();
+ }
+ translator.setLocationsType(maybeRelative ? Translator::RelativeLocations :
+ maybeAbsolute ? Translator::AbsoluteLocations :
+ Translator::NoLocations);
+ } // </TS>
+ } else {
+ handleError();
+ }
+ }
+ if (hasError()) {
+ m_cd.appendError(errorString());
+ return false;
+ }
+ return true;
+}
+
+static QString numericEntity(int ch)
+{
+ return QString(ch <= 0x20 ? QLatin1String("<byte value=\"x%1\"/>")
+ : QLatin1String("&#x%1;")) .arg(ch, 0, 16);
+}
+
+static QString protect(const QString &str)
+{
+ QString result;
+ result.reserve(str.length() * 12 / 10);
+ for (int i = 0; i != str.size(); ++i) {
+ uint c = str.at(i).unicode();
+ switch (c) {
+ case '\"':
+ result += QLatin1String("&quot;");
+ break;
+ case '&':
+ result += QLatin1String("&amp;");
+ break;
+ case '>':
+ result += QLatin1String("&gt;");
+ break;
+ case '<':
+ result += QLatin1String("&lt;");
+ break;
+ case '\'':
+ result += QLatin1String("&apos;");
+ break;
+ default:
+ if (c < 0x20 && c != '\r' && c != '\n' && c != '\t')
+ result += numericEntity(c);
+ else // this also covers surrogates
+ result += QChar(c);
+ }
+ }
+ return result;
+}
+
+static QString evilBytes(const QString& str,
+ bool isUtf8, int format, const QByteArray &codecName)
+{
+ //qDebug() << "EVIL: " << str << isUtf8 << format << codecName;
+ if (isUtf8)
+ return protect(str);
+ if (format == 20)
+ return protect(str);
+ if (codecName == "UTF-8")
+ return protect(str);
+ QTextCodec *codec = QTextCodec::codecForName(codecName);
+ if (!codec)
+ return protect(str);
+ QString t = QString::fromLatin1(codec->fromUnicode(protect(str)).data());
+ int len = (int) t.length();
+ QString result;
+ // FIXME: Factor is sensible only for latin scripts, probably.
+ result.reserve(t.length() * 2);
+ for (int k = 0; k < len; k++) {
+ if (t[k].unicode() >= 0x7f)
+ result += numericEntity(t[k].unicode());
+ else
+ result += t[k];
+ }
+ return result;
+}
+
+static void writeExtras(QTextStream &t, const char *indent,
+ const TranslatorMessage::ExtraData &extras, const QRegExp &drops)
+{
+ for (Translator::ExtraData::ConstIterator it = extras.begin(); it != extras.end(); ++it) {
+ if (!drops.exactMatch(it.key())) {
+ t << indent << "<extra-" << it.key() << '>'
+ << protect(it.value())
+ << "</extra-" << it.key() << ">\n";
+ }
+ }
+}
+
+static void writeVariants(QTextStream &t, const char *indent, const QString &input)
+{
+ int offset;
+ if ((offset = input.indexOf(QChar(Translator::BinaryVariantSeparator))) >= 0) {
+ t << " variants=\"yes\">";
+ int start = 0;
+ forever {
+ t << "\n " << indent << "<lengthvariant>"
+ << protect(input.mid(start, offset - start))
+ << "</lengthvariant>";
+ if (offset == input.length())
+ break;
+ start = offset + 1;
+ offset = input.indexOf(QChar(Translator::BinaryVariantSeparator), start);
+ if (offset < 0)
+ offset = input.length();
+ }
+ t << "\n" << indent;
+ } else {
+ t << ">" << protect(input);
+ }
+}
+
+bool saveTS(const Translator &translator, QIODevice &dev, ConversionData &cd, int format)
+{
+ bool result = true;
+ QTextStream t(&dev);
+ t.setCodec(QTextCodec::codecForName("UTF-8"));
+ bool trIsUtf8 = (translator.codecName() == "UTF-8");
+ //qDebug() << translator.codecName();
+ bool fileIsUtf8 = (format == 20 || trIsUtf8);
+
+ // The xml prolog allows processors to easily detect the correct encoding
+ t << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n";
+
+ if (format == 11)
+ t << "<TS version=\"1.1\"";
+ else
+ t << "<TS version=\"2.0\"";
+
+ QString languageCode = translator.languageCode();
+ if (!languageCode.isEmpty() && languageCode != QLatin1String("C"))
+ t << " language=\"" << languageCode << "\"";
+ if (format == 20) {
+ languageCode = translator.sourceLanguageCode();
+ if (!languageCode.isEmpty() && languageCode != QLatin1String("C"))
+ t << " sourcelanguage=\"" << languageCode << "\"";
+ }
+ t << ">\n";
+
+ QByteArray codecName = translator.codecName();
+ if (codecName != "ISO-8859-1")
+ t << "<defaultcodec>" << codecName << "</defaultcodec>\n";
+
+ QRegExp drops(cd.dropTags().join(QLatin1String("|")));
+
+ if (format == 20)
+ writeExtras(t, " ", translator.extras(), drops);
+
+ QHash<QString, QList<TranslatorMessage> > messageOrder;
+ QList<QString> contextOrder;
+ foreach (const TranslatorMessage &msg, translator.messages()) {
+ // no need for such noise
+ if (msg.type() == TranslatorMessage::Obsolete && msg.translation().isEmpty())
+ continue;
+
+ QList<TranslatorMessage> &context = messageOrder[msg.context()];
+ if (context.isEmpty())
+ contextOrder.append(msg.context());
+ context.append(msg);
+ }
+ if (cd.sortContexts())
+ qSort(contextOrder);
+
+ QHash<QString, int> currentLine;
+ QString currentFile;
+ foreach (const QString &context, contextOrder) {
+ const TranslatorMessage &firstMsg = messageOrder[context].first();
+ t << "<context" << ((!fileIsUtf8 && firstMsg.isUtf8()) ? " encoding=\"UTF-8\"" : "") << ">\n";
+
+ t << " <name>"
+ << evilBytes(context, firstMsg.isUtf8() || fileIsUtf8, format, codecName)
+ << "</name>\n";
+ foreach (const TranslatorMessage &msg, messageOrder[context]) {
+ //msg.dump();
+
+ bool isUtf8 = msg.isUtf8();
+ bool second = false;
+ forever {
+
+ t << " <message";
+ if (!msg.id().isEmpty())
+ t << " id=\"" << msg.id() << "\"";
+ if (!trIsUtf8) {
+ if (format == 11) {
+ if (isUtf8)
+ t << " encoding=\"UTF-8\"";
+ } else {
+ if (msg.isUtf8()) {
+ if (msg.isNonUtf8())
+ t << " utf8=\"both\"";
+ else
+ t << " utf8=\"true\"";
+ }
+ }
+ }
+ if (msg.isPlural())
+ t << " numerus=\"yes\"";
+ t << ">\n";
+ if (translator.locationsType() != Translator::NoLocations) {
+ QString cfile = currentFile;
+ bool first = true;
+ foreach (const TranslatorMessage::Reference &ref, msg.allReferences()) {
+ QString fn = cd.m_targetDir.relativeFilePath(ref.fileName())
+ .replace(QLatin1Char('\\'),QLatin1Char('/'));
+ int ln = ref.lineNumber();
+ QString ld;
+ if (translator.locationsType() == Translator::RelativeLocations) {
+ if (ln != -1) {
+ int dlt = ln - currentLine[fn];
+ if (dlt >= 0)
+ ld.append(QLatin1Char('+'));
+ ld.append(QString::number(dlt));
+ currentLine[fn] = ln;
+ }
+
+ if (fn != cfile) {
+ if (first)
+ currentFile = fn;
+ cfile = fn;
+ } else {
+ fn.clear();
+ }
+ first = false;
+ } else {
+ if (ln != -1)
+ ld = QString::number(ln);
+ }
+ t << " <location";
+ if (!fn.isEmpty())
+ t << " filename=\"" << fn << "\"";
+ if (!ld.isEmpty())
+ t << " line=\"" << ld << "\"";
+ t << "/>\n";
+ }
+ }
+
+ t << " <source>"
+ << evilBytes(msg.sourceText(), isUtf8, format, codecName)
+ << "</source>\n";
+
+ if (format != 11 && !msg.oldSourceText().isEmpty())
+ t << " <oldsource>" << protect(msg.oldSourceText()) << "</oldsource>\n";
+
+ if (!msg.comment().isEmpty()) {
+ t << " <comment>"
+ << evilBytes(msg.comment(), isUtf8, format, codecName)
+ << "</comment>\n";
+ }
+
+ if (format != 11) {
+
+ if (!msg.oldComment().isEmpty())
+ t << " <oldcomment>" << protect(msg.oldComment()) << "</oldcomment>\n";
+
+ if (!msg.extraComment().isEmpty())
+ t << " <extracomment>" << protect(msg.extraComment())
+ << "</extracomment>\n";
+
+ if (!msg.translatorComment().isEmpty())
+ t << " <translatorcomment>" << protect(msg.translatorComment())
+ << "</translatorcomment>\n";
+
+ }
+
+ t << " <translation";
+ if (msg.type() == TranslatorMessage::Unfinished)
+ t << " type=\"unfinished\"";
+ else if (msg.type() == TranslatorMessage::Obsolete)
+ t << " type=\"obsolete\"";
+ if (msg.isPlural()) {
+ t << ">";
+ const QStringList &translns = msg.translations();
+ for (int j = 0; j < translns.count(); ++j) {
+ t << "\n <numerusform";
+ writeVariants(t, " ", translns[j]);
+ t << "</numerusform>";
+ }
+ t << "\n ";
+ } else {
+ writeVariants(t, " ", msg.translation());
+ }
+ t << "</translation>\n";
+
+ if (format != 11)
+ writeExtras(t, " ", msg.extras(), drops);
+
+ if (!msg.userData().isEmpty())
+ t << " <userdata>" << msg.userData() << "</userdata>\n";
+ t << " </message>\n";
+
+ if (format != 11 || second || !msg.isUtf8() || !msg.isNonUtf8())
+ break;
+ isUtf8 = false;
+ second = true;
+ }
+ }
+ t << "</context>\n";
+ }
+
+ t << "</TS>\n";
+ return result;
+}
+
+bool loadTS(Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ TSReader reader(dev, cd);
+ return reader.read(translator);
+}
+
+bool saveTS11(const Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ return saveTS(translator, dev, cd, 11);
+}
+
+bool saveTS20(const Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ return saveTS(translator, dev, cd, 20);
+}
+
+int initTS()
+{
+ Translator::FileFormat format;
+
+ format.extension = QLatin1String("ts11");
+ format.fileType = Translator::FileFormat::TranslationSource;
+ format.priority = -1;
+ format.description = QObject::tr("Qt translation sources (format 1.1)");
+ format.loader = &loadTS;
+ format.saver = &saveTS11;
+ Translator::registerFileFormat(format);
+
+ format.extension = QLatin1String("ts20");
+ format.fileType = Translator::FileFormat::TranslationSource;
+ format.priority = -1;
+ format.description = QObject::tr("Qt translation sources (format 2.0)");
+ format.loader = &loadTS;
+ format.saver = &saveTS20;
+ Translator::registerFileFormat(format);
+
+ // "ts" is always the latest. right now it's ts20.
+ format.extension = QLatin1String("ts");
+ format.fileType = Translator::FileFormat::TranslationSource;
+ format.priority = 0;
+ format.description = QObject::tr("Qt translation sources (latest format)");
+ format.loader = &loadTS;
+ format.saver = &saveTS20;
+ Translator::registerFileFormat(format);
+
+ return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(initTS)
+
+QT_END_NAMESPACE
diff --git a/src/linguist/shared/ts.dtd b/src/linguist/shared/ts.dtd
new file mode 100644
index 000000000..12d356201
--- /dev/null
+++ b/src/linguist/shared/ts.dtd
@@ -0,0 +1,100 @@
+<!--
+ !
+ ! Some notes to the DTD:
+ !
+ ! The location element is set as optional since it was introduced first in Qt 4.2.
+ ! The userdata element is set as optional since it was introduced first in Qt 4.4.
+ !
+ -->
+<!--
+ ! Macro used in order to escape byte entities not allowed in an xml document
+ ! for instance, only #x9, #xA and #xD are allowed characters below #x20.
+ -->
+<!ENTITY % evilstring '(#PCDATA | byte)*' >
+<!ELEMENT byte EMPTY>
+<!-- value contains decimal (e.g. 1000) or hex (e.g. x3e8) unicode encoding of one char -->
+<!ATTLIST byte
+ value CDATA #REQUIRED>
+<!--
+ ! This element wildcard is no valid DTD. No better solution available.
+ ! extra elements may appear in TS and message elements. Each element may appear
+ ! only once within each scope. The contents are preserved verbatim; any
+ ! attributes are dropped. Currently recognized extra tags include:
+ ! extra-po-msgid_plural, extra-po-old_msgid_plural
+ ! extra-po-flags (comma-space separated list)
+ ! extra-loc-layout_id
+ ! extra-loc-feature
+ ! extra-loc-blank
+ -->
+<!ELEMENT extra-* %evilstring; >
+<!ELEMENT TS (defaultcodec?, extra-**, (context|message)+) >
+<!ATTLIST TS
+ version CDATA #IMPLIED
+ sourcelanguage CDATA #IMPLIED
+ language CDATA #IMPLIED>
+<!-- The encoding to use in the QM file by default. Default is ISO-8859-1. -->
+<!ELEMENT defaultcodec (#PCDATA) >
+<!ELEMENT context (name, comment?, (context|message)+) >
+<!ATTLIST context
+ encoding CDATA #IMPLIED>
+<!ELEMENT name %evilstring; >
+<!-- This is "disambiguation" in the (new) API, or "msgctxt" in gettext speak -->
+<!ELEMENT comment %evilstring; >
+<!-- Previous content of comment (result of merge) -->
+<!ELEMENT oldcomment %evilstring; >
+<!-- The real comment (added by developer/designer) -->
+<!ELEMENT extracomment %evilstring; >
+<!-- Comment added by translator -->
+<!ELEMENT translatorcomment %evilstring; >
+<!ELEMENT message (location*, source?, oldsource?, comment?, oldcomment?, extracomment?, translatorcomment?, translation?, userdata?, extra-**) >
+<!--
+ ! If utf8 is "true", the defaultcodec is overridden and the message is encoded
+ ! in UTF-8 in the QM file. If it is "both", both source encodings are stored
+ ! in the QM file.
+ -->
+<!ATTLIST message
+ id CDATA #IMPLIED
+ utf8 (true|false|both) "false"
+ numerus (yes|no) "no">
+<!ELEMENT location EMPTY>
+<!--
+ ! If the line is omitted, the location specifies only a file.
+ !
+ ! location supports relative specifications as well. Line numbers are
+ ! relative (explicitly positive or negative) to the last reference to a
+ ! given filename; each file starts with current line 0. If the filename
+ ! is omitted, the "current" one is used. For the 1st location in a message,
+ ! "current" is the filename used for the 1st location of the previous message.
+ ! For subsequent locations, it is the filename used for the previous location.
+ ! A single TS file has either all absolute or all relative locations.
+ -->
+<!ATTLIST location
+ filename CDATA #IMPLIED
+ line CDATA #IMPLIED>
+<!ELEMENT source %evilstring;>
+<!-- Previous content of source (result of merge) -->
+<!ELEMENT oldsource %evilstring;>
+<!--
+ ! The following should really say one evilstring macro or several
+ ! numerusform or lengthvariant elements, but the DTD can't express this.
+ -->
+<!ELEMENT translation (#PCDATA|byte|numerusform|lengthvariant)* >
+<!--
+ ! If no type is set, the message is "finished".
+ ! Length variants must be ordered by falling display length.
+ ! variants may not be yes if the message has numerus yes.
+ -->
+<!ATTLIST translation
+ type (unfinished|obsolete) #IMPLIED
+ variants (yes|no) "no">
+<!-- Deprecated. Use extra-* -->
+<!ELEMENT userdata (#PCDATA)* >
+<!--
+ ! The following should really say one evilstring macro or several
+ ! lengthvariant elements, but the DTD can't express this.
+ ! Length variants must be ordered by falling display length.
+ -->
+<!ELEMENT numerusform (#PCDATA|byte|lengthvariant)* >
+<!ATTLIST numerusform
+ variants (yes|no) "no">
+<!ELEMENT lengthvariant %evilstring; >
diff --git a/src/linguist/shared/xliff.cpp b/src/linguist/shared/xliff.cpp
new file mode 100644
index 000000000..40fbcabc1
--- /dev/null
+++ b/src/linguist/shared/xliff.cpp
@@ -0,0 +1,851 @@
+/****************************************************************************
+**
+** 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 "translator.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QMap>
+#include <QtCore/QStack>
+#include <QtCore/QString>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextStream>
+
+#include <QtXml/QXmlAttributes>
+#include <QtXml/QXmlDefaultHandler>
+#include <QtXml/QXmlParseException>
+
+
+// The string value is historical and reflects the main purpose: Keeping
+// obsolete entries separate from the magic file message (which both have
+// no location information, but typically reside at opposite ends of the file).
+#define MAGIC_OBSOLETE_REFERENCE "Obsolete_PO_entries"
+
+QT_BEGIN_NAMESPACE
+
+/**
+ * Implementation of XLIFF file format for Linguist
+ */
+//static const char *restypeDomain = "x-gettext-domain";
+static const char *restypeContext = "x-trolltech-linguist-context";
+static const char *restypePlurals = "x-gettext-plurals";
+static const char *restypeDummy = "x-dummy";
+static const char *dataTypeUIFile = "x-trolltech-designer-ui";
+static const char *contextMsgctxt = "x-gettext-msgctxt"; // XXX Troll invention, so far.
+static const char *contextOldMsgctxt = "x-gettext-previous-msgctxt"; // XXX Troll invention, so far.
+static const char *attribPlural = "trolltech:plural";
+static const char *XLIFF11namespaceURI = "urn:oasis:names:tc:xliff:document:1.1";
+static const char *XLIFF12namespaceURI = "urn:oasis:names:tc:xliff:document:1.2";
+static const char *TrollTsNamespaceURI = "urn:trolltech:names:ts:document:1.0";
+
+#define COMBINE4CHARS(c1, c2, c3, c4) \
+ (int(c1) << 24 | int(c2) << 16 | int(c3) << 8 | int(c4) )
+
+static QString dataType(const TranslatorMessage &m)
+{
+ QByteArray fileName = m.fileName().toAscii();
+ unsigned int extHash = 0;
+ int pos = fileName.count() - 1;
+ for (int pass = 0; pass < 4 && pos >=0; ++pass, --pos) {
+ if (fileName.at(pos) == '.')
+ break;
+ extHash |= ((int)fileName.at(pos) << (8*pass));
+ }
+
+ switch (extHash) {
+ case COMBINE4CHARS(0,'c','p','p'):
+ case COMBINE4CHARS(0,'c','x','x'):
+ case COMBINE4CHARS(0,'c','+','+'):
+ case COMBINE4CHARS(0,'h','p','p'):
+ case COMBINE4CHARS(0,'h','x','x'):
+ case COMBINE4CHARS(0,'h','+','+'):
+ return QLatin1String("cpp");
+ case COMBINE4CHARS(0, 0 , 0 ,'c'):
+ case COMBINE4CHARS(0, 0 , 0 ,'h'):
+ case COMBINE4CHARS(0, 0 ,'c','c'):
+ case COMBINE4CHARS(0, 0 ,'c','h'):
+ case COMBINE4CHARS(0, 0 ,'h','h'):
+ return QLatin1String("c");
+ case COMBINE4CHARS(0, 0 ,'u','i'):
+ return QLatin1String(dataTypeUIFile); //### form?
+ default:
+ return QLatin1String("plaintext"); // we give up
+ }
+}
+
+static void writeIndent(QTextStream &ts, int indent)
+{
+ ts << QString().fill(QLatin1Char(' '), indent * 2);
+}
+
+struct CharMnemonic
+{
+ char ch;
+ char escape;
+ const char *mnemonic;
+};
+
+static const CharMnemonic charCodeMnemonics[] = {
+ {0x07, 'a', "bel"},
+ {0x08, 'b', "bs"},
+ {0x09, 't', "tab"},
+ {0x0a, 'n', "lf"},
+ {0x0b, 'v', "vt"},
+ {0x0c, 'f', "ff"},
+ {0x0d, 'r', "cr"}
+};
+
+static char charFromEscape(char escape)
+{
+ for (uint i = 0; i < sizeof(charCodeMnemonics)/sizeof(CharMnemonic); ++i) {
+ CharMnemonic cm = charCodeMnemonics[i];
+ if (cm.escape == escape)
+ return cm.ch;
+ }
+ Q_ASSERT(0);
+ return escape;
+}
+
+static QString numericEntity(int ch, bool makePhs)
+{
+ // ### This needs to be reviewed, to reflect the updated XLIFF-PO spec.
+ if (!makePhs || ch < 7 || ch > 0x0d)
+ return QString::fromAscii("&#x%1;").arg(QString::number(ch, 16));
+
+ CharMnemonic cm = charCodeMnemonics[int(ch) - 7];
+ QString name = QLatin1String(cm.mnemonic);
+ char escapechar = cm.escape;
+
+ static int id = 0;
+ return QString::fromAscii("<ph id=\"ph%1\" ctype=\"x-ch-%2\">\\%3</ph>")
+ .arg(++id) .arg(name) .arg(escapechar);
+}
+
+static QString protect(const QString &str, bool makePhs = true)
+{
+ QString result;
+ int len = str.size();
+ for (int i = 0; i != len; ++i) {
+ uint c = str.at(i).unicode();
+ switch (c) {
+ case '\"':
+ result += QLatin1String("&quot;");
+ break;
+ case '&':
+ result += QLatin1String("&amp;");
+ break;
+ case '>':
+ result += QLatin1String("&gt;");
+ break;
+ case '<':
+ result += QLatin1String("&lt;");
+ break;
+ case '\'':
+ result += QLatin1String("&apos;");
+ break;
+ default:
+ if (c < 0x20 && c != '\r' && c != '\n' && c != '\t')
+ result += numericEntity(c, makePhs);
+ else // this also covers surrogates
+ result += QChar(c);
+ }
+ }
+ return result;
+}
+
+
+static void writeExtras(QTextStream &ts, int indent,
+ const TranslatorMessage::ExtraData &extras, const QRegExp &drops)
+{
+ for (Translator::ExtraData::ConstIterator it = extras.begin(); it != extras.end(); ++it) {
+ if (!drops.exactMatch(it.key())) {
+ writeIndent(ts, indent);
+ ts << "<trolltech:" << it.key() << '>'
+ << protect(it.value())
+ << "</trolltech:" << it.key() << ">\n";
+ }
+ }
+}
+
+static void writeLineNumber(QTextStream &ts, const TranslatorMessage &msg, int indent)
+{
+ if (msg.lineNumber() == -1)
+ return;
+ writeIndent(ts, indent);
+ ts << "<context-group purpose=\"location\"><context context-type=\"linenumber\">"
+ << msg.lineNumber() << "</context></context-group>\n";
+ foreach (const TranslatorMessage::Reference &ref, msg.extraReferences()) {
+ writeIndent(ts, indent);
+ ts << "<context-group purpose=\"location\">";
+ if (ref.fileName() != msg.fileName())
+ ts << "<context context-type=\"sourcefile\">" << ref.fileName() << "</context>";
+ ts << "<context context-type=\"linenumber\">" << ref.lineNumber()
+ << "</context></context-group>\n";
+ }
+}
+
+static void writeComment(QTextStream &ts, const TranslatorMessage &msg, const QRegExp &drops, int indent)
+{
+ if (!msg.comment().isEmpty()) {
+ writeIndent(ts, indent);
+ ts << "<context-group><context context-type=\"" << contextMsgctxt << "\">"
+ << protect(msg.comment(), false)
+ << "</context></context-group>\n";
+ }
+ if (!msg.oldComment().isEmpty()) {
+ writeIndent(ts, indent);
+ ts << "<context-group><context context-type=\"" << contextOldMsgctxt << "\">"
+ << protect(msg.oldComment(), false)
+ << "</context></context-group>\n";
+ }
+ writeExtras(ts, indent, msg.extras(), drops);
+ if (!msg.extraComment().isEmpty()) {
+ writeIndent(ts, indent);
+ ts << "<note annotates=\"source\" from=\"developer\">"
+ << protect(msg.extraComment()) << "</note>\n";
+ }
+ if (!msg.translatorComment().isEmpty()) {
+ writeIndent(ts, indent);
+ ts << "<note from=\"translator\">"
+ << protect(msg.translatorComment()) << "</note>\n";
+ }
+}
+
+static void writeTransUnits(QTextStream &ts, const TranslatorMessage &msg, const QRegExp &drops, int indent)
+{
+ static int msgid;
+ QString msgidstr = !msg.id().isEmpty() ? msg.id() : QString::fromAscii("_msg%1").arg(++msgid);
+
+ QStringList translns = msg.translations();
+ QHash<QString, QString>::const_iterator it;
+ QString pluralStr;
+ QStringList sources(msg.sourceText());
+ if ((it = msg.extras().find(QString::fromLatin1("po-msgid_plural"))) != msg.extras().end())
+ sources.append(*it);
+ QStringList oldsources;
+ if (!msg.oldSourceText().isEmpty())
+ oldsources.append(msg.oldSourceText());
+ if ((it = msg.extras().find(QString::fromLatin1("po-old_msgid_plural"))) != msg.extras().end()) {
+ if (oldsources.isEmpty()) {
+ if (sources.count() == 2)
+ oldsources.append(QString());
+ else
+ pluralStr = QLatin1Char(' ') + QLatin1String(attribPlural) + QLatin1String("=\"yes\"");
+ }
+ oldsources.append(*it);
+ }
+
+ QStringList::const_iterator
+ srcit = sources.begin(), srcend = sources.end(),
+ oldsrcit = oldsources.begin(), oldsrcend = oldsources.end(),
+ transit = translns.begin(), transend = translns.end();
+ int plural = 0;
+ QString source;
+ while (srcit != srcend || oldsrcit != oldsrcend || transit != transend) {
+ QByteArray attribs;
+ QByteArray state;
+ if (msg.type() == TranslatorMessage::Obsolete) {
+ if (!msg.isPlural())
+ attribs = " translate=\"no\"";
+ } else if (msg.type() == TranslatorMessage::Finished) {
+ attribs = " approved=\"yes\"";
+ } else if (transit != transend && !transit->isEmpty()) {
+ state = " state=\"needs-review-translation\"";
+ }
+ writeIndent(ts, indent);
+ ts << "<trans-unit id=\"" << msgidstr;
+ if (msg.isPlural())
+ ts << "[" << plural++ << "]";
+ ts << "\"" << attribs << ">\n";
+ ++indent;
+
+ writeIndent(ts, indent);
+ if (srcit != srcend) {
+ source = *srcit;
+ ++srcit;
+ } // else just repeat last element
+ ts << "<source xml:space=\"preserve\">" << protect(source) << "</source>\n";
+
+ bool puttrans = false;
+ QString translation;
+ if (transit != transend) {
+ translation = *transit;
+ translation.replace(QChar(Translator::BinaryVariantSeparator),
+ QChar(Translator::TextVariantSeparator));
+ ++transit;
+ puttrans = true;
+ }
+ do {
+ if (oldsrcit != oldsrcend && !oldsrcit->isEmpty()) {
+ writeIndent(ts, indent);
+ ts << "<alt-trans>\n";
+ ++indent;
+ writeIndent(ts, indent);
+ ts << "<source xml:space=\"preserve\"" << pluralStr << '>' << protect(*oldsrcit) << "</source>\n";
+ if (!puttrans) {
+ writeIndent(ts, indent);
+ ts << "<target restype=\"" << restypeDummy << "\"/>\n";
+ }
+ }
+
+ if (puttrans) {
+ writeIndent(ts, indent);
+ ts << "<target xml:space=\"preserve\"" << state << ">" << protect(translation) << "</target>\n";
+ }
+
+ if (oldsrcit != oldsrcend) {
+ if (!oldsrcit->isEmpty()) {
+ --indent;
+ writeIndent(ts, indent);
+ ts << "</alt-trans>\n";
+ }
+ ++oldsrcit;
+ }
+
+ puttrans = false;
+ } while (srcit == srcend && oldsrcit != oldsrcend);
+
+ if (!msg.isPlural()) {
+ writeLineNumber(ts, msg, indent);
+ writeComment(ts, msg, drops, indent);
+ }
+
+ --indent;
+ writeIndent(ts, indent);
+ ts << "</trans-unit>\n";
+ }
+}
+
+static void writeMessage(QTextStream &ts, const TranslatorMessage &msg, const QRegExp &drops, int indent)
+{
+ if (msg.isPlural()) {
+ writeIndent(ts, indent);
+ ts << "<group restype=\"" << restypePlurals << "\"";
+ if (!msg.id().isEmpty())
+ ts << " id=\"" << msg.id() << "\"";
+ if (msg.type() == TranslatorMessage::Obsolete)
+ ts << " translate=\"no\"";
+ ts << ">\n";
+ ++indent;
+ writeLineNumber(ts, msg, indent);
+ writeComment(ts, msg, drops, indent);
+
+ writeTransUnits(ts, msg, drops, indent);
+ --indent;
+ writeIndent(ts, indent);
+ ts << "</group>\n";
+ } else {
+ writeTransUnits(ts, msg, drops, indent);
+ }
+}
+
+
+class XLIFFHandler : public QXmlDefaultHandler
+{
+public:
+ XLIFFHandler(Translator &translator, ConversionData &cd);
+
+ bool startElement(const QString& namespaceURI, const QString &localName,
+ const QString &qName, const QXmlAttributes &atts );
+ bool endElement(const QString& namespaceURI, const QString &localName,
+ const QString &qName );
+ bool characters(const QString &ch);
+ bool fatalError(const QXmlParseException &exception);
+
+ bool endDocument();
+
+private:
+ enum XliffContext {
+ XC_xliff,
+ XC_group,
+ XC_trans_unit,
+ XC_context_group,
+ XC_context_group_any,
+ XC_context,
+ XC_context_filename,
+ XC_context_linenumber,
+ XC_context_context,
+ XC_context_comment,
+ XC_context_old_comment,
+ XC_ph,
+ XC_extra_comment,
+ XC_translator_comment,
+ XC_restype_context,
+ XC_restype_translation,
+ XC_restype_plurals,
+ XC_alt_trans
+ };
+ void pushContext(XliffContext ctx);
+ bool popContext(XliffContext ctx);
+ XliffContext currentContext() const;
+ bool hasContext(XliffContext ctx) const;
+ bool finalizeMessage(bool isPlural);
+
+private:
+ Translator &m_translator;
+ ConversionData &m_cd;
+ TranslatorMessage::Type m_type;
+ QString m_language;
+ QString m_sourceLanguage;
+ QString m_context;
+ QString m_id;
+ QStringList m_sources;
+ QStringList m_oldSources;
+ QString m_comment;
+ QString m_oldComment;
+ QString m_extraComment;
+ QString m_translatorComment;
+ bool m_isPlural;
+ bool m_hadAlt;
+ QStringList m_translations;
+ QString m_fileName;
+ int m_lineNumber;
+ QString m_extraFileName;
+ TranslatorMessage::References m_refs;
+ TranslatorMessage::ExtraData m_extra;
+
+ QString accum;
+ QString m_ctype;
+ const QString m_URITT; // convenience and efficiency
+ const QString m_URI; // ...
+ const QString m_URI12; // ...
+ QStack<int> m_contextStack;
+};
+
+XLIFFHandler::XLIFFHandler(Translator &translator, ConversionData &cd)
+ : m_translator(translator), m_cd(cd),
+ m_type(TranslatorMessage::Finished),
+ m_lineNumber(-1),
+ m_URITT(QLatin1String(TrollTsNamespaceURI)),
+ m_URI(QLatin1String(XLIFF11namespaceURI)),
+ m_URI12(QLatin1String(XLIFF12namespaceURI))
+{}
+
+
+void XLIFFHandler::pushContext(XliffContext ctx)
+{
+ m_contextStack.push_back(ctx);
+}
+
+// Only pops it off if the top of the stack contains ctx
+bool XLIFFHandler::popContext(XliffContext ctx)
+{
+ if (!m_contextStack.isEmpty() && m_contextStack.top() == ctx) {
+ m_contextStack.pop();
+ return true;
+ }
+ return false;
+}
+
+XLIFFHandler::XliffContext XLIFFHandler::currentContext() const
+{
+ if (!m_contextStack.isEmpty())
+ return (XliffContext)m_contextStack.top();
+ return XC_xliff;
+}
+
+// traverses to the top to check all of the parent contexes.
+bool XLIFFHandler::hasContext(XliffContext ctx) const
+{
+ for (int i = m_contextStack.count() - 1; i >= 0; --i) {
+ if (m_contextStack.at(i) == ctx)
+ return true;
+ }
+ return false;
+}
+
+bool XLIFFHandler::startElement(const QString& namespaceURI,
+ const QString &localName, const QString &qName, const QXmlAttributes &atts )
+{
+ Q_UNUSED(qName);
+ if (namespaceURI == m_URITT)
+ goto bail;
+ if (namespaceURI != m_URI && namespaceURI != m_URI12)
+ return false;
+ if (localName == QLatin1String("xliff")) {
+ // make sure that the stack is not empty during parsing
+ pushContext(XC_xliff);
+ } else if (localName == QLatin1String("file")) {
+ m_fileName = atts.value(QLatin1String("original"));
+ m_language = atts.value(QLatin1String("target-language"));
+ m_language.replace(QLatin1Char('-'), QLatin1Char('_'));
+ m_sourceLanguage = atts.value(QLatin1String("source-language"));
+ m_sourceLanguage.replace(QLatin1Char('-'), QLatin1Char('_'));
+ if (m_sourceLanguage == QLatin1String("en"))
+ m_sourceLanguage.clear();
+ } else if (localName == QLatin1String("group")) {
+ if (atts.value(QLatin1String("restype")) == QLatin1String(restypeContext)) {
+ m_context = atts.value(QLatin1String("resname"));
+ pushContext(XC_restype_context);
+ } else {
+ if (atts.value(QLatin1String("restype")) == QLatin1String(restypePlurals)) {
+ pushContext(XC_restype_plurals);
+ m_id = atts.value(QLatin1String("id"));
+ if (atts.value(QLatin1String("translate")) == QLatin1String("no"))
+ m_type = TranslatorMessage::Obsolete;
+ } else {
+ pushContext(XC_group);
+ }
+ }
+ } else if (localName == QLatin1String("trans-unit")) {
+ if (!hasContext(XC_restype_plurals) || m_sources.isEmpty() /* who knows ... */)
+ if (atts.value(QLatin1String("translate")) == QLatin1String("no"))
+ m_type = TranslatorMessage::Obsolete;
+ if (!hasContext(XC_restype_plurals)) {
+ m_id = atts.value(QLatin1String("id"));
+ if (m_id.startsWith(QLatin1String("_msg")))
+ m_id.clear();
+ }
+ if (m_type != TranslatorMessage::Obsolete &&
+ atts.value(QLatin1String("approved")) != QLatin1String("yes"))
+ m_type = TranslatorMessage::Unfinished;
+ pushContext(XC_trans_unit);
+ m_hadAlt = false;
+ } else if (localName == QLatin1String("alt-trans")) {
+ pushContext(XC_alt_trans);
+ } else if (localName == QLatin1String("source")) {
+ m_isPlural = atts.value(QLatin1String(attribPlural)) == QLatin1String("yes");
+ } else if (localName == QLatin1String("target")) {
+ if (atts.value(QLatin1String("restype")) != QLatin1String(restypeDummy))
+ pushContext(XC_restype_translation);
+ } else if (localName == QLatin1String("context-group")) {
+ QString purpose = atts.value(QLatin1String("purpose"));
+ if (purpose == QLatin1String("location"))
+ pushContext(XC_context_group);
+ else
+ pushContext(XC_context_group_any);
+ } else if (currentContext() == XC_context_group && localName == QLatin1String("context")) {
+ QString ctxtype = atts.value(QLatin1String("context-type"));
+ if (ctxtype == QLatin1String("linenumber"))
+ pushContext(XC_context_linenumber);
+ else if (ctxtype == QLatin1String("sourcefile"))
+ pushContext(XC_context_filename);
+ } else if (currentContext() == XC_context_group_any && localName == QLatin1String("context")) {
+ QString ctxtype = atts.value(QLatin1String("context-type"));
+ if (ctxtype == QLatin1String(contextMsgctxt))
+ pushContext(XC_context_comment);
+ else if (ctxtype == QLatin1String(contextOldMsgctxt))
+ pushContext(XC_context_old_comment);
+ } else if (localName == QLatin1String("note")) {
+ if (atts.value(QLatin1String("annotates")) == QLatin1String("source") &&
+ atts.value(QLatin1String("from")) == QLatin1String("developer"))
+ pushContext(XC_extra_comment);
+ else
+ pushContext(XC_translator_comment);
+ } else if (localName == QLatin1String("ph")) {
+ QString ctype = atts.value(QLatin1String("ctype"));
+ if (ctype.startsWith(QLatin1String("x-ch-")))
+ m_ctype = ctype.mid(5);
+ pushContext(XC_ph);
+ }
+bail:
+ if (currentContext() != XC_ph)
+ accum.clear();
+ return true;
+}
+
+bool XLIFFHandler::endElement(const QString &namespaceURI, const QString& localName,
+ const QString &qName)
+{
+ Q_UNUSED(qName);
+ if (namespaceURI == m_URITT) {
+ if (hasContext(XC_trans_unit) || hasContext(XC_restype_plurals))
+ m_extra[localName] = accum;
+ else
+ m_translator.setExtra(localName, accum);
+ return true;
+ }
+ if (namespaceURI != m_URI && namespaceURI != m_URI12)
+ return false;
+ //qDebug() << "URI:" << namespaceURI << "QNAME:" << qName;
+ if (localName == QLatin1String("xliff")) {
+ popContext(XC_xliff);
+ } else if (localName == QLatin1String("source")) {
+ if (hasContext(XC_alt_trans)) {
+ if (m_isPlural && m_oldSources.isEmpty())
+ m_oldSources.append(QString());
+ m_oldSources.append(accum);
+ m_hadAlt = true;
+ } else {
+ m_sources.append(accum);
+ }
+ } else if (localName == QLatin1String("target")) {
+ if (popContext(XC_restype_translation)) {
+ accum.replace(QChar(Translator::TextVariantSeparator),
+ QChar(Translator::BinaryVariantSeparator));
+ m_translations.append(accum);
+ }
+ } else if (localName == QLatin1String("context-group")) {
+ if (popContext(XC_context_group)) {
+ m_refs.append(TranslatorMessage::Reference(
+ m_extraFileName.isEmpty() ? m_fileName : m_extraFileName, m_lineNumber));
+ m_extraFileName.clear();
+ m_lineNumber = -1;
+ } else {
+ popContext(XC_context_group_any);
+ }
+ } else if (localName == QLatin1String("context")) {
+ if (popContext(XC_context_linenumber)) {
+ bool ok;
+ m_lineNumber = accum.trimmed().toInt(&ok);
+ if (!ok)
+ m_lineNumber = -1;
+ } else if (popContext(XC_context_filename)) {
+ m_extraFileName = accum;
+ } else if (popContext(XC_context_comment)) {
+ m_comment = accum;
+ } else if (popContext(XC_context_old_comment)) {
+ m_oldComment = accum;
+ }
+ } else if (localName == QLatin1String("note")) {
+ if (popContext(XC_extra_comment))
+ m_extraComment = accum;
+ else if (popContext(XC_translator_comment))
+ m_translatorComment = accum;
+ } else if (localName == QLatin1String("ph")) {
+ m_ctype.clear();
+ popContext(XC_ph);
+ } else if (localName == QLatin1String("trans-unit")) {
+ popContext(XC_trans_unit);
+ if (!m_hadAlt)
+ m_oldSources.append(QString());
+ if (!hasContext(XC_restype_plurals)) {
+ if (!finalizeMessage(false))
+ return false;
+ }
+ } else if (localName == QLatin1String("alt-trans")) {
+ popContext(XC_alt_trans);
+ } else if (localName == QLatin1String("group")) {
+ if (popContext(XC_restype_plurals)) {
+ if (!finalizeMessage(true))
+ return false;
+ } else if (popContext(XC_restype_context)) {
+ m_context.clear();
+ } else {
+ popContext(XC_group);
+ }
+ }
+ return true;
+}
+
+bool XLIFFHandler::characters(const QString &ch)
+{
+ if (currentContext() == XC_ph) {
+ // handle the content of <ph> elements
+ for (int i = 0; i < ch.count(); ++i) {
+ QChar chr = ch.at(i);
+ if (accum.endsWith(QLatin1Char('\\')))
+ accum[accum.size() - 1] = QLatin1Char(charFromEscape(chr.toAscii()));
+ else
+ accum.append(chr);
+ }
+ } else {
+ QString t = ch;
+ t.replace(QLatin1String("\r"), QLatin1String(""));
+ accum.append(t);
+ }
+ return true;
+}
+
+bool XLIFFHandler::endDocument()
+{
+ m_translator.setLanguageCode(m_language);
+ m_translator.setSourceLanguageCode(m_sourceLanguage);
+ return true;
+}
+
+bool XLIFFHandler::finalizeMessage(bool isPlural)
+{
+ if (m_sources.isEmpty()) {
+ m_cd.appendError(QLatin1String("XLIFF syntax error: Message without source string."));
+ return false;
+ }
+ if (m_type == TranslatorMessage::Obsolete && m_refs.size() == 1
+ && m_refs.at(0).fileName() == QLatin1String(MAGIC_OBSOLETE_REFERENCE))
+ m_refs.clear();
+ TranslatorMessage msg(m_context, m_sources[0],
+ m_comment, QString(), QString(), -1,
+ m_translations, m_type, isPlural);
+ msg.setId(m_id);
+ msg.setReferences(m_refs);
+ msg.setOldComment(m_oldComment);
+ msg.setExtraComment(m_extraComment);
+ msg.setTranslatorComment(m_translatorComment);
+ if (m_sources.count() > 1 && m_sources[1] != m_sources[0])
+ m_extra.insert(QLatin1String("po-msgid_plural"), m_sources[1]);
+ if (!m_oldSources.isEmpty()) {
+ if (!m_oldSources[0].isEmpty())
+ msg.setOldSourceText(m_oldSources[0]);
+ if (m_oldSources.count() > 1 && m_oldSources[1] != m_oldSources[0])
+ m_extra.insert(QLatin1String("po-old_msgid_plural"), m_oldSources[1]);
+ }
+ msg.setExtras(m_extra);
+ m_translator.append(msg);
+
+ m_id.clear();
+ m_sources.clear();
+ m_oldSources.clear();
+ m_translations.clear();
+ m_comment.clear();
+ m_oldComment.clear();
+ m_extraComment.clear();
+ m_translatorComment.clear();
+ m_extra.clear();
+ m_refs.clear();
+ m_type = TranslatorMessage::Finished;
+ return true;
+}
+
+bool XLIFFHandler::fatalError(const QXmlParseException &exception)
+{
+ QString msg;
+ msg.sprintf("XML error: Parse error at line %d, column %d (%s).\n",
+ exception.lineNumber(), exception.columnNumber(),
+ exception.message().toLatin1().data() );
+ m_cd.appendError(msg);
+ return false;
+}
+
+bool loadXLIFF(Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ QXmlInputSource in(&dev);
+ QXmlSimpleReader reader;
+ XLIFFHandler hand(translator, cd);
+ reader.setContentHandler(&hand);
+ reader.setErrorHandler(&hand);
+ return reader.parse(in);
+}
+
+bool saveXLIFF(const Translator &translator, QIODevice &dev, ConversionData &cd)
+{
+ bool ok = true;
+ int indent = 0;
+
+ QTextStream ts(&dev);
+ ts.setCodec(QTextCodec::codecForName("UTF-8"));
+
+ QStringList dtgs = cd.dropTags();
+ dtgs << QLatin1String("po-(old_)?msgid_plural");
+ QRegExp drops(dtgs.join(QLatin1String("|")));
+
+ QHash<QString, QHash<QString, QList<TranslatorMessage> > > messageOrder;
+ QHash<QString, QList<QString> > contextOrder;
+ QList<QString> fileOrder;
+ foreach (const TranslatorMessage &msg, translator.messages()) {
+ QString fn = msg.fileName();
+ if (fn.isEmpty() && msg.type() == TranslatorMessage::Obsolete)
+ fn = QLatin1String(MAGIC_OBSOLETE_REFERENCE);
+ QHash<QString, QList<TranslatorMessage> > &file = messageOrder[fn];
+ if (file.isEmpty())
+ fileOrder.append(fn);
+ QList<TranslatorMessage> &context = file[msg.context()];
+ if (context.isEmpty())
+ contextOrder[fn].append(msg.context());
+ context.append(msg);
+ }
+
+ ts.setFieldAlignment(QTextStream::AlignRight);
+ ts << "<?xml version=\"1.0\"";
+ ts << " encoding=\"utf-8\"?>\n";
+ ts << "<xliff version=\"1.2\" xmlns=\"" << XLIFF12namespaceURI
+ << "\" xmlns:trolltech=\"" << TrollTsNamespaceURI << "\">\n";
+ ++indent;
+ writeExtras(ts, indent, translator.extras(), drops);
+ QString sourceLanguageCode = translator.sourceLanguageCode();
+ if (sourceLanguageCode.isEmpty() || sourceLanguageCode == QLatin1String("C"))
+ sourceLanguageCode = QLatin1String("en");
+ else
+ sourceLanguageCode.replace(QLatin1Char('_'), QLatin1Char('-'));
+ QString languageCode = translator.languageCode();
+ languageCode.replace(QLatin1Char('_'), QLatin1Char('-'));
+ foreach (const QString &fn, fileOrder) {
+ writeIndent(ts, indent);
+ ts << "<file original=\"" << fn << "\""
+ << " datatype=\"" << dataType(messageOrder[fn].begin()->first()) << "\""
+ << " source-language=\"" << sourceLanguageCode.toLatin1() << "\""
+ << " target-language=\"" << languageCode.toLatin1() << "\""
+ << "><body>\n";
+ ++indent;
+
+ foreach (const QString &ctx, contextOrder[fn]) {
+ if (!ctx.isEmpty()) {
+ writeIndent(ts, indent);
+ ts << "<group restype=\"" << restypeContext << "\""
+ << " resname=\"" << protect(ctx) << "\">\n";
+ ++indent;
+ }
+
+ foreach (const TranslatorMessage &msg, messageOrder[fn][ctx])
+ writeMessage(ts, msg, drops, indent);
+
+ if (!ctx.isEmpty()) {
+ --indent;
+ writeIndent(ts, indent);
+ ts << "</group>\n";
+ }
+ }
+
+ --indent;
+ writeIndent(ts, indent);
+ ts << "</body></file>\n";
+ }
+ --indent;
+ writeIndent(ts, indent);
+ ts << "</xliff>\n";
+
+ return ok;
+}
+
+int initXLIFF()
+{
+ Translator::FileFormat format;
+ format.extension = QLatin1String("xlf");
+ format.description = QObject::tr("XLIFF localization files");
+ format.fileType = Translator::FileFormat::TranslationSource;
+ format.priority = 1;
+ format.loader = &loadXLIFF;
+ format.saver = &saveXLIFF;
+ Translator::registerFileFormat(format);
+ return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(initXLIFF)
+
+QT_END_NAMESPACE
diff --git a/src/linguist/tests/data/main.cpp b/src/linguist/tests/data/main.cpp
new file mode 100644
index 000000000..dff1364dd
--- /dev/null
+++ b/src/linguist/tests/data/main.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** 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 test suite 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 <QtGui>
+#include <QtCore>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ QStringList args = app.arguments();
+
+ if (argc <= 1) {
+ qDebug() << "Usage: " << qPrintable(args[0]) << " <ts-file>";
+ return 1;
+ }
+
+ QTranslator trans;
+ trans.load(args[1], ".");
+ app.installTranslator(&trans);
+
+ QWidget w;
+ QVBoxLayout *layout = new QVBoxLayout(&w);
+
+ QLabel label1(QObject::tr("XXXXXXXXX \33 XXXXXXXXXXX • and → "), 0);
+ QLabel label2(QObject::tr("\32"), 0);
+ QLabel label3(QObject::tr("\176"), 0);
+ QLabel label4(QObject::tr("\301"), 0);
+
+ layout->addWidget(&label1);
+ layout->addWidget(&label2);
+ layout->addWidget(&label3);
+ layout->addWidget(&label4);
+
+ w.show();
+
+ return app.exec();
+}
diff --git a/src/linguist/tests/data/test.pro b/src/linguist/tests/data/test.pro
new file mode 100644
index 000000000..90e5704b4
--- /dev/null
+++ b/src/linguist/tests/data/test.pro
@@ -0,0 +1,9 @@
+TEMPLATE = app
+TARGET +=
+DEPENDPATH += .
+INCLUDEPATH += .
+
+SOURCES += main.cpp
+
+TRANSLATIONS += t1_en.ts
+TRANSLATIONS += t1_de.ts
diff --git a/src/linguist/tests/tests.pro b/src/linguist/tests/tests.pro
new file mode 100644
index 000000000..a67725c46
--- /dev/null
+++ b/src/linguist/tests/tests.pro
@@ -0,0 +1,16 @@
+load(qttest_p4)
+
+QT += xml
+
+HEADERS += \
+ tst_linguist.h \
+ ../shared/translator.h
+
+SOURCES += \
+ tst_linguist.cpp \
+ tst_lupdate.cpp \
+ tst_simtexth.cpp \
+ ../shared/simtexth.cpp \
+ ../shared/translator.cpp \
+ ../shared/translatormessage.cpp \
+ ../shared/xliff.cpp
diff --git a/src/linguist/tests/tst_linguist.cpp b/src/linguist/tests/tst_linguist.cpp
new file mode 100644
index 000000000..7abefd226
--- /dev/null
+++ b/src/linguist/tests/tst_linguist.cpp
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** 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 test suite 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 "tst_linguist.h"
+#include "moc_tst_linguist.cpp"
+
+QTEST_MAIN(tst_linguist)
diff --git a/src/linguist/tests/tst_linguist.h b/src/linguist/tests/tst_linguist.h
new file mode 100644
index 000000000..4b329b899
--- /dev/null
+++ b/src/linguist/tests/tst_linguist.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** 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 test suite 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 TST_LINGUIST
+#define TST_LINGUIST
+
+#include <QtTest/QtTest>
+#include <QtCore/QtCore>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+
+class tst_linguist : public QObject
+{
+ Q_OBJECT
+private slots:
+ void fetchtr();
+ void fetchtr_data();
+
+ void simtexth();
+ void simtexth_data();
+};
+
+#endif
diff --git a/src/linguist/tests/tst_lupdate.cpp b/src/linguist/tests/tst_lupdate.cpp
new file mode 100644
index 000000000..c39525622
--- /dev/null
+++ b/src/linguist/tests/tst_lupdate.cpp
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** 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 test suite 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 <QtTest/QtTest>
+#include <QtCore/QtCore>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+#include "tst_linguist.h"
+
+void tst_linguist::fetchtr()
+{
+ // FIXME: This probably should use some yet-to-be-invented
+ // binary interface to 'lupdate' instead of playing around
+ // with the filesystem,
+
+ QRegExp reg("\\s*");
+ QString lupdate("lupdate");
+
+ QFETCH(QString, input);
+
+ QFETCH(QString, name);
+ QFETCH(QString, file);
+ QFETCH(QString, line);
+ QFETCH(QString, src);
+
+ QString result;
+
+ QTemporaryFile profile("tst_lu_XXXXXX.pro");
+ QTemporaryFile cppfile("tst_lu_XXXXXX.cpp");
+ QTemporaryFile tsfile("tst_lu_XXXXXX.ts");
+
+ profile.open();
+ cppfile.open();
+ tsfile.open();
+
+#if 0
+ profile.setAutoRemove(false);
+ cppfile.setAutoRemove(false);
+ tsfile.setAutoRemove(false);
+
+ qDebug() << ".pro: " << profile.fileName();
+ qDebug() << ".cpp: " << cppfile.fileName();
+ qDebug() << ".ts: " << tsfile.fileName();
+#endif
+
+ QTextStream prots(&profile);
+ prots << "SOURCES += " << cppfile.fileName() << "\n";
+ prots << "TRANSLATIONS += " << tsfile.fileName() << "\n";
+ prots.flush();
+
+ QTextStream cppts(&cppfile);
+ cppts.setCodec("ISO 8859-1");
+ cppts << input << '\n';
+ cppts.flush();
+
+ QProcess proc;
+ proc.start(lupdate, QStringList() << profile.fileName());
+ proc.waitForFinished();
+
+ result = tsfile.readAll();
+
+ static QRegExp re(
+ "<name>(.+)</name>\\s*"
+ "<message.*>\\s*" // there might be a numerus="yes" attribiute
+ "<location filename=\"(.+)\" line=\"(\\d+)\"/>\\s*"
+ "<source>(.+)</source>\\s*"
+ "<translation type=\"unfinished\">.*</translation>\\s*"
+ );
+
+ re.indexIn(result);
+ QString resname = re.cap(1);
+ //QString resfile = re.cap(2);
+ QString resline = re.cap(3);
+ QString ressrc = re.cap(4);
+
+ //qDebug() << "pattern:" << re.pattern();
+ //qDebug() << "result:" << result;
+ //qDebug() << "resname:" << resname;
+ ////qDebug() << "resfile:" << resfile;
+ //qDebug() << "resline:" << resline;
+ //qDebug() << "ressource:" << ressrc;
+
+ QCOMPARE(src + ": " + resname, src + ": " + name);
+ QCOMPARE(src + ": " + resline, src + ": " + line);
+ QCOMPARE(src + ": " + ressrc, src + ": " + src);
+}
+
+void tst_linguist::fetchtr_data()
+{
+ using namespace QTest;
+
+ addColumn<QString>("input");
+ addColumn<QString>("name");
+ addColumn<QString>("file");
+ addColumn<QString>("line");
+ addColumn<QString>("src");
+
+ // plain stuff
+ newRow("00") << "int main() { tr(\"foo\"); }"
+ << "@default" << "XXXXXX" << "1" << "foo";
+
+ // space at beginning of text
+ newRow("01") << "int main() { tr(\" foo\"); }"
+ << "@default" << "XXXXXX" << "1" << " foo";
+ // space at end of text
+ newRow("02") << "int main() { tr(\"foo \"); }"
+ << "@default" << "XXXXXX" << "1" << "foo ";
+ // space in the middle of the text
+ newRow("03") << "int main() { tr(\"foo bar\"); }"
+ << "@default" << "XXXXXX" << "1" << "foo bar";
+
+ // tab at beginning of text
+ newRow("04") << "int main() { tr(\"\tfoo\"); }"
+ << "@default" << "XXXXXX" << "1" << "<byte value=\"x9\"/>foo";
+ // tab at end of text
+ newRow("05") << "int main() { tr(\"foo\t\"); }"
+ << "@default" << "XXXXXX" << "1" << "foo<byte value=\"x9\"/>";
+ // tab in the middle of the text
+ newRow("06") << "int main() { tr(\"foo\tbar\"); }"
+ << "@default" << "XXXXXX" << "1" << "foo<byte value=\"x9\"/>bar";
+
+ // check for unicode
+ newRow("07") << "int main() { tr(\"\32\"); }" // 26 dec
+ << "@default" << "XXXXXX" << "1" << "<byte value=\"x1a\"/>";
+ // check for unicode
+ newRow("08") << "int main() { tr(\"\33\"); }" // 27 dec
+ << "@default" << "XXXXXX" << "1" << "<byte value=\"x1b\"/>";
+ // check for unicode
+ newRow("09") << "int main() { tr(\"\176\"); }" // 124 dec
+ << "@default" << "XXXXXX" << "1" << "~";
+ // check for unicode
+ newRow("10") << "int main() { tr(\"\301\"); }" // 193 dec
+ << "@default" << "XXXXXX" << "1" << "&#xc1;";
+
+ // Bug 162562: lupdate does not find QCoreApplication::translate() strings
+ newRow("11") << "int main() { QString s = QCoreApplication::translate"
+ "(\"mycontext\", \"msg\", \"\", QCoreApplication::CodecForTr, 2);"
+ << "mycontext" << "XXXXXX" << "1" << "msg";
+
+ // Bug 161504: lupdate produces wrong ts file with context "N::QObject"
+ newRow("12") << "namespace N { QString foo() "
+ "{ return QObject::tr(\"msg\"); }"
+ << "QObject" << "XXXXXX" << "1" << "msg";
+
+ // Correct example from 161504:
+ newRow("13") << "namespace N { QString foo(); }"
+ "QString N::anyfunc() { return QObject::tr(\"msg\"); }"
+ << "QObject" << "XXXXXX" << "1" << "msg";
+
+ // Bug 161106: When specifying ::QObject::tr() then lupdate will
+ // take the previous word as being the namespace
+ newRow("14") << " std::cout << ::QObject::tr(\"msg\");"
+ << "QObject" << "XXXXXX" << "1" << "msg";
+
+}
diff --git a/src/linguist/tests/tst_simtexth.cpp b/src/linguist/tests/tst_simtexth.cpp
new file mode 100644
index 000000000..d254bd92c
--- /dev/null
+++ b/src/linguist/tests/tst_simtexth.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** 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 test suite 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 <QtTest/QtTest>
+#include <QtCore/QtCore>
+
+//int getSimilarityScore(const QString &str1, const char* str2);
+#include "../shared/simtexth.h"
+#include "tst_linguist.h"
+
+void tst_linguist::simtexth()
+{
+ QFETCH(QString, one);
+ QFETCH(QString, two);
+ QFETCH(int, expected);
+
+ int measured = getSimilarityScore(one, two.toLatin1());
+ QCOMPARE(measured, expected);
+}
+
+
+void tst_linguist::simtexth_data()
+{
+ using namespace QTest;
+
+ addColumn<QString>("one");
+ addColumn<QString>("two");
+ addColumn<int>("expected");
+
+ newRow("00") << "" << "" << 1024;
+ newRow("01") << "a" << "a" << 1024;
+ newRow("02") << "ab" << "ab" << 1024;
+ newRow("03") << "abc" << "abc" << 1024;
+ newRow("04") << "abcd" << "abcd" << 1024;
+}