diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:34:13 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:34:13 (GMT) |
commit | 67ad0519fd165acee4a4d2a94fa502e9e4847bd0 (patch) | |
tree | 1dbf50b3dff8d5ca7e9344733968c72704eb15ff /tools/linguist | |
download | Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.zip Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.gz Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.bz2 |
Long live Qt!
Diffstat (limited to 'tools/linguist')
177 files changed, 43494 insertions, 0 deletions
diff --git a/tools/linguist/LICENSE.GPL b/tools/linguist/LICENSE.GPL new file mode 100644 index 0000000..b6e1c33 --- /dev/null +++ b/tools/linguist/LICENSE.GPL @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/tools/linguist/lconvert/lconvert.pro b/tools/linguist/lconvert/lconvert.pro new file mode 100644 index 0000000..66a194e --- /dev/null +++ b/tools/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/tools/linguist/lconvert/main.cpp b/tools/linguist/lconvert/main.cpp new file mode 100644 index 0000000..569ed22 --- /dev/null +++ b/tools/linguist/lconvert/main.cpp @@ -0,0 +1,235 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "translator.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QDebug> +#include <QtCore/QString> +#include <QtCore/QStringList> + +static int usage(const QStringList &args) +{ + Q_UNUSED(args); + + QString loaders; + QString savers; + QString line = QString(QLatin1String(" %1 - %2\n")); + foreach (Translator::FileFormat format, Translator::registeredFileFormats()) { + loaders += line.arg(format.extension, -5).arg(format.description); + savers += line.arg(format.extension, -5).arg(format.description); + } + + qWarning("%s", qPrintable(QString(QLatin1String("\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 translation data files from one of the\n" + "following input formats\n\n%1\n" + "to one of the following output formats\n\n%2\n" + "If multiple input files are specified the translations 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" + " --drop-tags <regexp>\n" + " Drop named extra tags when writing 'ts' or 'xlf' 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" + " --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" + " --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).arg(savers))); + return 1; +} + +struct File +{ + QString name; + QString format; +}; + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + 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; + + 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("-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("-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 (!targetLanguage.isEmpty()) + tr.setLanguageCode(targetLanguage); + if (!sourceLanguage.isEmpty()) + tr.setSourceLanguageCode(sourceLanguage); + + if (!tr.load(inFiles[0].name, cd, inFiles[0].format)) { + qWarning() << qPrintable(cd.error()); + return 2; + } + + for (int i = 1; i < inFiles.size(); ++i) { + Translator tr2; + if (!tr2.load(inFiles[i].name, cd, inFiles[i].format)) { + qWarning() << qPrintable(cd.error()); + return 2; + } + for (int j = 0; j < tr2.messageCount(); ++j) + tr.replaceSorted(tr2.message(j)); + } + + if (noObsolete) + tr.stripObsoleteMessages(); + if (noFinished) + tr.stripFinishedMessages(); + if (dropTranslations) + tr.dropTranslations(); + + if (!tr.save(outFileName, cd, outFormat)) { + qWarning("%s", qPrintable(cd.error())); + return 3; + } + return 0; +} diff --git a/tools/linguist/linguist.pro b/tools/linguist/linguist.pro new file mode 100644 index 0000000..e1c8a63 --- /dev/null +++ b/tools/linguist/linguist.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs +SUBDIRS = \ + linguist \ + lrelease \ + lupdate \ + lconvert +CONFIG += ordered + diff --git a/tools/linguist/linguist/Info_mac.plist b/tools/linguist/linguist/Info_mac.plist new file mode 100644 index 0000000..b11f493 --- /dev/null +++ b/tools/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/tools/linguist/linguist/batchtranslation.ui b/tools/linguist/linguist/batchtranslation.ui new file mode 100644 index 0000000..4824dfb --- /dev/null +++ b/tools/linguist/linguist/batchtranslation.ui @@ -0,0 +1,260 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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>&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/tools/linguist/linguist/batchtranslationdialog.cpp b/tools/linguist/linguist/batchtranslationdialog.cpp new file mode 100644 index 0000000..101ad97 --- /dev/null +++ b/tools/linguist/linguist/batchtranslationdialog.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/batchtranslationdialog.h b/tools/linguist/linguist/batchtranslationdialog.h new file mode 100644 index 0000000..1ceeb6e --- /dev/null +++ b/tools/linguist/linguist/batchtranslationdialog.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/errorsview.cpp b/tools/linguist/linguist/errorsview.cpp new file mode 100644 index 0000000..9ffebaa --- /dev/null +++ b/tools/linguist/linguist/errorsview.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/errorsview.h b/tools/linguist/linguist/errorsview.h new file mode 100644 index 0000000..bc6097b --- /dev/null +++ b/tools/linguist/linguist/errorsview.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/finddialog.cpp b/tools/linguist/linguist/finddialog.cpp new file mode 100644 index 0000000..ae243ea --- /dev/null +++ b/tools/linguist/linguist/finddialog.cpp @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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(const QString &)), this, SLOT(verifyText(const 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/tools/linguist/linguist/finddialog.h b/tools/linguist/linguist/finddialog.h new file mode 100644 index 0000000..a9fc146 --- /dev/null +++ b/tools/linguist/linguist/finddialog.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/finddialog.ui b/tools/linguist/linguist/finddialog.ui new file mode 100644 index 0000000..fd1c4a1 --- /dev/null +++ b/tools/linguist/linguist/finddialog.ui @@ -0,0 +1,266 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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>&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>&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>&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>&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>&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 &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/tools/linguist/linguist/formpreviewview.cpp b/tools/linguist/linguist/formpreviewview.cpp new file mode 100644 index 0000000..990414b --- /dev/null +++ b/tools/linguist/linguist/formpreviewview.cpp @@ -0,0 +1,535 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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)[qVariantValue<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, qVariantFromValue(a->font().resolve(fnt))); + fnt.setBold(true); + fnt.setItalic(true); + a->setFont(fnt); + } + } else { + if (bak.isValid()) { + a->setFont(qVariantValue<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, qVariantFromValue(w->palette().resolve(pal))); + w->setProperty(AUTOFILL_BACKUP_PROP, qVariantFromValue(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(qVariantValue<QPalette>(bak)); + w->setAutoFillBackground(qVariantValue<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); +} + +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 = 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/tools/linguist/linguist/formpreviewview.h b/tools/linguist/linguist/formpreviewview.h new file mode 100644 index 0000000..5923f24 --- /dev/null +++ b/tools/linguist/linguist/formpreviewview.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/images/appicon.png b/tools/linguist/linguist/images/appicon.png Binary files differnew file mode 100644 index 0000000..d388cbd --- /dev/null +++ b/tools/linguist/linguist/images/appicon.png diff --git a/tools/linguist/linguist/images/down.png b/tools/linguist/linguist/images/down.png Binary files differnew file mode 100644 index 0000000..29d1d44 --- /dev/null +++ b/tools/linguist/linguist/images/down.png diff --git a/tools/linguist/linguist/images/editdelete.png b/tools/linguist/linguist/images/editdelete.png Binary files differnew file mode 100644 index 0000000..df2a147 --- /dev/null +++ b/tools/linguist/linguist/images/editdelete.png diff --git a/tools/linguist/linguist/images/icons/linguist-128-32.png b/tools/linguist/linguist/images/icons/linguist-128-32.png Binary files differnew file mode 100644 index 0000000..d108208 --- /dev/null +++ b/tools/linguist/linguist/images/icons/linguist-128-32.png diff --git a/tools/linguist/linguist/images/icons/linguist-128-8.png b/tools/linguist/linguist/images/icons/linguist-128-8.png Binary files differnew file mode 100644 index 0000000..6678dd2 --- /dev/null +++ b/tools/linguist/linguist/images/icons/linguist-128-8.png diff --git a/tools/linguist/linguist/images/icons/linguist-16-32.png b/tools/linguist/linguist/images/icons/linguist-16-32.png Binary files differnew file mode 100644 index 0000000..8dfc974 --- /dev/null +++ b/tools/linguist/linguist/images/icons/linguist-16-32.png diff --git a/tools/linguist/linguist/images/icons/linguist-16-8.png b/tools/linguist/linguist/images/icons/linguist-16-8.png Binary files differnew file mode 100644 index 0000000..4f4e839 --- /dev/null +++ b/tools/linguist/linguist/images/icons/linguist-16-8.png diff --git a/tools/linguist/linguist/images/icons/linguist-32-32.png b/tools/linguist/linguist/images/icons/linguist-32-32.png Binary files differnew file mode 100644 index 0000000..d388cbd --- /dev/null +++ b/tools/linguist/linguist/images/icons/linguist-32-32.png diff --git a/tools/linguist/linguist/images/icons/linguist-32-8.png b/tools/linguist/linguist/images/icons/linguist-32-8.png Binary files differnew file mode 100644 index 0000000..3db4bc5 --- /dev/null +++ b/tools/linguist/linguist/images/icons/linguist-32-8.png diff --git a/tools/linguist/linguist/images/icons/linguist-48-32.png b/tools/linguist/linguist/images/icons/linguist-48-32.png Binary files differnew file mode 100644 index 0000000..ceb7387 --- /dev/null +++ b/tools/linguist/linguist/images/icons/linguist-48-32.png diff --git a/tools/linguist/linguist/images/icons/linguist-48-8.png b/tools/linguist/linguist/images/icons/linguist-48-8.png Binary files differnew file mode 100644 index 0000000..9a13c20 --- /dev/null +++ b/tools/linguist/linguist/images/icons/linguist-48-8.png diff --git a/tools/linguist/linguist/images/icons/linguist-64-32.png b/tools/linguist/linguist/images/icons/linguist-64-32.png Binary files differnew file mode 100644 index 0000000..0cce805 --- /dev/null +++ b/tools/linguist/linguist/images/icons/linguist-64-32.png diff --git a/tools/linguist/linguist/images/icons/linguist-64-8.png b/tools/linguist/linguist/images/icons/linguist-64-8.png Binary files differnew file mode 100644 index 0000000..05c833d --- /dev/null +++ b/tools/linguist/linguist/images/icons/linguist-64-8.png diff --git a/tools/linguist/linguist/images/mac/accelerator.png b/tools/linguist/linguist/images/mac/accelerator.png Binary files differnew file mode 100644 index 0000000..9a684c1 --- /dev/null +++ b/tools/linguist/linguist/images/mac/accelerator.png diff --git a/tools/linguist/linguist/images/mac/book.png b/tools/linguist/linguist/images/mac/book.png Binary files differnew file mode 100644 index 0000000..7a3204c --- /dev/null +++ b/tools/linguist/linguist/images/mac/book.png diff --git a/tools/linguist/linguist/images/mac/doneandnext.png b/tools/linguist/linguist/images/mac/doneandnext.png Binary files differnew file mode 100644 index 0000000..512fea8 --- /dev/null +++ b/tools/linguist/linguist/images/mac/doneandnext.png diff --git a/tools/linguist/linguist/images/mac/editcopy.png b/tools/linguist/linguist/images/mac/editcopy.png Binary files differnew file mode 100644 index 0000000..f551364 --- /dev/null +++ b/tools/linguist/linguist/images/mac/editcopy.png diff --git a/tools/linguist/linguist/images/mac/editcut.png b/tools/linguist/linguist/images/mac/editcut.png Binary files differnew file mode 100644 index 0000000..a784fd5 --- /dev/null +++ b/tools/linguist/linguist/images/mac/editcut.png diff --git a/tools/linguist/linguist/images/mac/editpaste.png b/tools/linguist/linguist/images/mac/editpaste.png Binary files differnew file mode 100644 index 0000000..64c0b2d --- /dev/null +++ b/tools/linguist/linguist/images/mac/editpaste.png diff --git a/tools/linguist/linguist/images/mac/filenew.png b/tools/linguist/linguist/images/mac/filenew.png Binary files differnew file mode 100644 index 0000000..d3882c7 --- /dev/null +++ b/tools/linguist/linguist/images/mac/filenew.png diff --git a/tools/linguist/linguist/images/mac/fileopen.png b/tools/linguist/linguist/images/mac/fileopen.png Binary files differnew file mode 100644 index 0000000..fc06c5e --- /dev/null +++ b/tools/linguist/linguist/images/mac/fileopen.png diff --git a/tools/linguist/linguist/images/mac/fileprint.png b/tools/linguist/linguist/images/mac/fileprint.png Binary files differnew file mode 100644 index 0000000..808c97e --- /dev/null +++ b/tools/linguist/linguist/images/mac/fileprint.png diff --git a/tools/linguist/linguist/images/mac/filesave.png b/tools/linguist/linguist/images/mac/filesave.png Binary files differnew file mode 100644 index 0000000..b41ecf5 --- /dev/null +++ b/tools/linguist/linguist/images/mac/filesave.png diff --git a/tools/linguist/linguist/images/mac/next.png b/tools/linguist/linguist/images/mac/next.png Binary files differnew file mode 100644 index 0000000..ba0792c --- /dev/null +++ b/tools/linguist/linguist/images/mac/next.png diff --git a/tools/linguist/linguist/images/mac/nextunfinished.png b/tools/linguist/linguist/images/mac/nextunfinished.png Binary files differnew file mode 100644 index 0000000..bffbf0b --- /dev/null +++ b/tools/linguist/linguist/images/mac/nextunfinished.png diff --git a/tools/linguist/linguist/images/mac/phrase.png b/tools/linguist/linguist/images/mac/phrase.png Binary files differnew file mode 100644 index 0000000..6512341 --- /dev/null +++ b/tools/linguist/linguist/images/mac/phrase.png diff --git a/tools/linguist/linguist/images/mac/prev.png b/tools/linguist/linguist/images/mac/prev.png Binary files differnew file mode 100644 index 0000000..612fb34 --- /dev/null +++ b/tools/linguist/linguist/images/mac/prev.png diff --git a/tools/linguist/linguist/images/mac/prevunfinished.png b/tools/linguist/linguist/images/mac/prevunfinished.png Binary files differnew file mode 100644 index 0000000..4d937b2 --- /dev/null +++ b/tools/linguist/linguist/images/mac/prevunfinished.png diff --git a/tools/linguist/linguist/images/mac/print.png b/tools/linguist/linguist/images/mac/print.png Binary files differnew file mode 100644 index 0000000..10ca56c --- /dev/null +++ b/tools/linguist/linguist/images/mac/print.png diff --git a/tools/linguist/linguist/images/mac/punctuation.png b/tools/linguist/linguist/images/mac/punctuation.png Binary files differnew file mode 100644 index 0000000..4719fc6 --- /dev/null +++ b/tools/linguist/linguist/images/mac/punctuation.png diff --git a/tools/linguist/linguist/images/mac/redo.png b/tools/linguist/linguist/images/mac/redo.png Binary files differnew file mode 100644 index 0000000..8875bf2 --- /dev/null +++ b/tools/linguist/linguist/images/mac/redo.png diff --git a/tools/linguist/linguist/images/mac/searchfind.png b/tools/linguist/linguist/images/mac/searchfind.png Binary files differnew file mode 100644 index 0000000..3561745 --- /dev/null +++ b/tools/linguist/linguist/images/mac/searchfind.png diff --git a/tools/linguist/linguist/images/mac/undo.png b/tools/linguist/linguist/images/mac/undo.png Binary files differnew file mode 100644 index 0000000..a3bd5e0 --- /dev/null +++ b/tools/linguist/linguist/images/mac/undo.png diff --git a/tools/linguist/linguist/images/mac/validateplacemarkers.png b/tools/linguist/linguist/images/mac/validateplacemarkers.png Binary files differnew file mode 100644 index 0000000..18ccc4c --- /dev/null +++ b/tools/linguist/linguist/images/mac/validateplacemarkers.png diff --git a/tools/linguist/linguist/images/mac/whatsthis.png b/tools/linguist/linguist/images/mac/whatsthis.png Binary files differnew file mode 100644 index 0000000..5b7078f --- /dev/null +++ b/tools/linguist/linguist/images/mac/whatsthis.png diff --git a/tools/linguist/linguist/images/s_check_danger.png b/tools/linguist/linguist/images/s_check_danger.png Binary files differnew file mode 100644 index 0000000..e101577 --- /dev/null +++ b/tools/linguist/linguist/images/s_check_danger.png diff --git a/tools/linguist/linguist/images/s_check_empty.png b/tools/linguist/linguist/images/s_check_empty.png Binary files differnew file mode 100644 index 0000000..759a41b --- /dev/null +++ b/tools/linguist/linguist/images/s_check_empty.png diff --git a/tools/linguist/linguist/images/s_check_obsolete.png b/tools/linguist/linguist/images/s_check_obsolete.png Binary files differnew file mode 100644 index 0000000..b852b63 --- /dev/null +++ b/tools/linguist/linguist/images/s_check_obsolete.png diff --git a/tools/linguist/linguist/images/s_check_off.png b/tools/linguist/linguist/images/s_check_off.png Binary files differnew file mode 100644 index 0000000..640b689 --- /dev/null +++ b/tools/linguist/linguist/images/s_check_off.png diff --git a/tools/linguist/linguist/images/s_check_on.png b/tools/linguist/linguist/images/s_check_on.png Binary files differnew file mode 100644 index 0000000..afcaf63 --- /dev/null +++ b/tools/linguist/linguist/images/s_check_on.png diff --git a/tools/linguist/linguist/images/s_check_warning.png b/tools/linguist/linguist/images/s_check_warning.png Binary files differnew file mode 100644 index 0000000..f689c33 --- /dev/null +++ b/tools/linguist/linguist/images/s_check_warning.png diff --git a/tools/linguist/linguist/images/splash.png b/tools/linguist/linguist/images/splash.png Binary files differnew file mode 100644 index 0000000..0e99a1c --- /dev/null +++ b/tools/linguist/linguist/images/splash.png diff --git a/tools/linguist/linguist/images/transbox.png b/tools/linguist/linguist/images/transbox.png Binary files differnew file mode 100644 index 0000000..2d7219b --- /dev/null +++ b/tools/linguist/linguist/images/transbox.png diff --git a/tools/linguist/linguist/images/up.png b/tools/linguist/linguist/images/up.png Binary files differnew file mode 100644 index 0000000..e437312 --- /dev/null +++ b/tools/linguist/linguist/images/up.png diff --git a/tools/linguist/linguist/images/win/accelerator.png b/tools/linguist/linguist/images/win/accelerator.png Binary files differnew file mode 100644 index 0000000..c423c12 --- /dev/null +++ b/tools/linguist/linguist/images/win/accelerator.png diff --git a/tools/linguist/linguist/images/win/book.png b/tools/linguist/linguist/images/win/book.png Binary files differnew file mode 100644 index 0000000..09ec4d3 --- /dev/null +++ b/tools/linguist/linguist/images/win/book.png diff --git a/tools/linguist/linguist/images/win/doneandnext.png b/tools/linguist/linguist/images/win/doneandnext.png Binary files differnew file mode 100644 index 0000000..9d1d58d --- /dev/null +++ b/tools/linguist/linguist/images/win/doneandnext.png diff --git a/tools/linguist/linguist/images/win/editcopy.png b/tools/linguist/linguist/images/win/editcopy.png Binary files differnew file mode 100644 index 0000000..1121b47 --- /dev/null +++ b/tools/linguist/linguist/images/win/editcopy.png diff --git a/tools/linguist/linguist/images/win/editcut.png b/tools/linguist/linguist/images/win/editcut.png Binary files differnew file mode 100644 index 0000000..4b6c82c --- /dev/null +++ b/tools/linguist/linguist/images/win/editcut.png diff --git a/tools/linguist/linguist/images/win/editpaste.png b/tools/linguist/linguist/images/win/editpaste.png Binary files differnew file mode 100644 index 0000000..ffab15a --- /dev/null +++ b/tools/linguist/linguist/images/win/editpaste.png diff --git a/tools/linguist/linguist/images/win/filenew.png b/tools/linguist/linguist/images/win/filenew.png Binary files differnew file mode 100644 index 0000000..af5d122 --- /dev/null +++ b/tools/linguist/linguist/images/win/filenew.png diff --git a/tools/linguist/linguist/images/win/fileopen.png b/tools/linguist/linguist/images/win/fileopen.png Binary files differnew file mode 100644 index 0000000..fc6f17e --- /dev/null +++ b/tools/linguist/linguist/images/win/fileopen.png diff --git a/tools/linguist/linguist/images/win/filesave.png b/tools/linguist/linguist/images/win/filesave.png Binary files differnew file mode 100644 index 0000000..8feec99 --- /dev/null +++ b/tools/linguist/linguist/images/win/filesave.png diff --git a/tools/linguist/linguist/images/win/next.png b/tools/linguist/linguist/images/win/next.png Binary files differnew file mode 100644 index 0000000..8df4127 --- /dev/null +++ b/tools/linguist/linguist/images/win/next.png diff --git a/tools/linguist/linguist/images/win/nextunfinished.png b/tools/linguist/linguist/images/win/nextunfinished.png Binary files differnew file mode 100644 index 0000000..636b921 --- /dev/null +++ b/tools/linguist/linguist/images/win/nextunfinished.png diff --git a/tools/linguist/linguist/images/win/phrase.png b/tools/linguist/linguist/images/win/phrase.png Binary files differnew file mode 100644 index 0000000..8ff952c --- /dev/null +++ b/tools/linguist/linguist/images/win/phrase.png diff --git a/tools/linguist/linguist/images/win/prev.png b/tools/linguist/linguist/images/win/prev.png Binary files differnew file mode 100644 index 0000000..0780bc2 --- /dev/null +++ b/tools/linguist/linguist/images/win/prev.png diff --git a/tools/linguist/linguist/images/win/prevunfinished.png b/tools/linguist/linguist/images/win/prevunfinished.png Binary files differnew file mode 100644 index 0000000..139d11b --- /dev/null +++ b/tools/linguist/linguist/images/win/prevunfinished.png diff --git a/tools/linguist/linguist/images/win/print.png b/tools/linguist/linguist/images/win/print.png Binary files differnew file mode 100644 index 0000000..ba7c02d --- /dev/null +++ b/tools/linguist/linguist/images/win/print.png diff --git a/tools/linguist/linguist/images/win/punctuation.png b/tools/linguist/linguist/images/win/punctuation.png Binary files differnew file mode 100644 index 0000000..e2372a2 --- /dev/null +++ b/tools/linguist/linguist/images/win/punctuation.png diff --git a/tools/linguist/linguist/images/win/redo.png b/tools/linguist/linguist/images/win/redo.png Binary files differnew file mode 100644 index 0000000..686ad14 --- /dev/null +++ b/tools/linguist/linguist/images/win/redo.png diff --git a/tools/linguist/linguist/images/win/searchfind.png b/tools/linguist/linguist/images/win/searchfind.png Binary files differnew file mode 100644 index 0000000..6ea35e9 --- /dev/null +++ b/tools/linguist/linguist/images/win/searchfind.png diff --git a/tools/linguist/linguist/images/win/undo.png b/tools/linguist/linguist/images/win/undo.png Binary files differnew file mode 100644 index 0000000..c3b8c513 --- /dev/null +++ b/tools/linguist/linguist/images/win/undo.png diff --git a/tools/linguist/linguist/images/win/validateplacemarkers.png b/tools/linguist/linguist/images/win/validateplacemarkers.png Binary files differnew file mode 100644 index 0000000..cc127fd --- /dev/null +++ b/tools/linguist/linguist/images/win/validateplacemarkers.png diff --git a/tools/linguist/linguist/images/win/whatsthis.png b/tools/linguist/linguist/images/win/whatsthis.png Binary files differnew file mode 100644 index 0000000..623cad6 --- /dev/null +++ b/tools/linguist/linguist/images/win/whatsthis.png diff --git a/tools/linguist/linguist/linguist.icns b/tools/linguist/linguist/linguist.icns Binary files differnew file mode 100644 index 0000000..5918e00 --- /dev/null +++ b/tools/linguist/linguist/linguist.icns diff --git a/tools/linguist/linguist/linguist.ico b/tools/linguist/linguist/linguist.ico Binary files differnew file mode 100644 index 0000000..5bbdc48 --- /dev/null +++ b/tools/linguist/linguist/linguist.ico diff --git a/tools/linguist/linguist/linguist.pro b/tools/linguist/linguist/linguist.pro new file mode 100644 index 0000000..417ef67 --- /dev/null +++ b/tools/linguist/linguist/linguist.pro @@ -0,0 +1,107 @@ +TEMPLATE = app +LANGUAGE = C++ +DESTDIR = ../../../bin + +QT += xml \ + network + +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 \ + 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 \ + 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 +linguisttranslations.files = *.qm +linguisttranslations.path = $$[QT_INSTALL_TRANSLATIONS] +INSTALLS += linguisttranslations +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 + +TRANSLATIONS=$$[QT_INSTALL_TRANSLATIONS]/linguist_ja.ts \ + $$[QT_INSTALL_TRANSLATIONS]/linguist_pl.ts \ + $$[QT_INSTALL_TRANSLATIONS]/linguist_untranslated.ts \ + $$[QT_INSTALL_TRANSLATIONS]/linguist_tr_TR.ts \ + $$[QT_INSTALL_TRANSLATIONS]/linguist_zh_CN.ts \ + $$[QT_INSTALL_TRANSLATIONS]/linguist_zh_TW.ts \ + $$[QT_INSTALL_TRANSLATIONS]/linguist_de.ts \ + $$[QT_INSTALL_TRANSLATIONS]/linguist_fr.ts diff --git a/tools/linguist/linguist/linguist.qrc b/tools/linguist/linguist/linguist.qrc new file mode 100644 index 0000000..42cf6e3 --- /dev/null +++ b/tools/linguist/linguist/linguist.qrc @@ -0,0 +1,56 @@ +<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/transbox.png</file> + <file>images/up.png</file> + <file>images/down.png</file> + <file>images/editdelete.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/tools/linguist/linguist/linguist.rc b/tools/linguist/linguist/linguist.rc new file mode 100644 index 0000000..865e021 --- /dev/null +++ b/tools/linguist/linguist/linguist.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "linguist.ico" diff --git a/tools/linguist/linguist/main.cpp b/tools/linguist/linguist/main.cpp new file mode 100644 index 0000000..018fbc5 --- /dev/null +++ b/tools/linguist/linguist/main.cpp @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.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; + translator.load(QLatin1String("linguist_") + QLocale::system().name(), resourceDir); + app.installTranslator(&translator); + + QTranslator qtTranslator; + qtTranslator.load(QLatin1String("qt_") + QLocale::system().name(), resourceDir); + app.installTranslator(&qtTranslator); + + app.setOrganizationName(QLatin1String("Trolltech")); + app.setApplicationName(QLatin1String("Linguist")); + QString keybase(QString::number( (QT_VERSION >> 16) & 0xff ) + + QLatin1Char('.') + QString::number( (QT_VERSION >> 8) & 0xff ) + QLatin1Char('/') ); + + QSettings config; + + QWidget tmp; + tmp.restoreGeometry(config.value(keybase + QLatin1String("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/tools/linguist/linguist/mainwindow.cpp b/tools/linguist/linguist/mainwindow.cpp new file mode 100644 index 0000000..e0a8a56 --- /dev/null +++ b/tools/linguist/linguist/mainwindow.cpp @@ -0,0 +1,2673 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 "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 <QProcess> +#include <QRegExp> +#include <QSettings> +#include <QSortFilterProxyModel> +#include <QStackedWidget> +#include <QStatusBar> +#include <QTextStream> +#include <QToolBar> +#include <QUrl> +#include <QWhatsThis> + +QT_BEGIN_NAMESPACE + +static const int MessageMS = 2500; + +const QString &settingsPrefix() +{ + static QString prefix = QString(QLatin1String("%1.%2/")) + .arg((QT_VERSION >> 16) & 0xff) + .arg((QT_VERSION >> 8) & 0xff); + return prefix; +} + +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(); + int ch = 0; + if (!str.isEmpty()) + ch = str.right(1)[0].unicode(); + + switch (ch) { + case 0x002e: // full stop + if (str.endsWith(QString(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 + return End_Interrobang; + case 0x003b: // greek 'compatibility' questionmark + return lang == QLocale::Greek ? End_Interrobang : End_None; + case 0x003a: // colon + return End_Colon; + case 0x2026: // horizontal ellipsis + return End_Ellipsis; + default: + return End_None; + } +} + + +class ContextItemDelegate : public QItemDelegate +{ +public: + ContextItemDelegate(QObject *parent, MultiDataModel *model) : QItemDelegate(parent), m_dataModel(model) {} + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const + { + const QAbstractItemModel *model = index.model(); + Q_ASSERT(model); + + if (!model->parent(index).isValid()) { + if (index.column() - 1 == m_dataModel->modelCount()) { + QStyleOptionViewItem opt = option; + opt.font.setBold(true); + QItemDelegate::paint(painter, opt, index); + return; + } + } + QItemDelegate::paint(painter, option, index); + } + +private: + MultiDataModel *m_dataModel; +}; + +static const QVariant &pxObsolete() +{ + static const QVariant v = + qVariantFromValue(QPixmap(QLatin1String(":/images/s_check_obsolete.png"))); + return v; +} + + +class SortedMessagesModel : public QSortFilterProxyModel +{ +public: + SortedMessagesModel(QObject *parent, MultiDataModel *model) : QSortFilterProxyModel(parent), m_dataModel(model) {} + + QVariant headerData(int section, Qt::Orientation orientation, int role) const + { + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) + switch (section - m_dataModel->modelCount()) { + case 0: return QString(); + case 1: return MainWindow::tr("Source text"); + case 2: return MainWindow::tr("Index"); + } + + if (role == Qt::DecorationRole && orientation == Qt::Horizontal && section - 1 < m_dataModel->modelCount()) + return pxObsolete(); + + return QVariant(); + } + +private: + MultiDataModel *m_dataModel; +}; + +class SortedContextsModel : public QSortFilterProxyModel +{ +public: + SortedContextsModel(QObject *parent, MultiDataModel *model) : QSortFilterProxyModel(parent), m_dataModel(model) {} + + QVariant headerData(int section, Qt::Orientation orientation, int role) const + { + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) + switch (section - m_dataModel->modelCount()) { + case 0: return QString(); + case 1: return MainWindow::tr("Context"); + case 2: return MainWindow::tr("Items"); + case 3: return MainWindow::tr("Index"); + } + + if (role == Qt::DecorationRole && orientation == Qt::Horizontal && section - 1 < m_dataModel->modelCount()) + return pxObsolete(); + + return QVariant(); + } + +private: + MultiDataModel *m_dataModel; +}; + +class FocusWatcher : public QObject +{ +public: + FocusWatcher(MessageEditor *msgedit, QObject *parent) : QObject(parent), m_messageEditor(msgedit) {} + +protected: + bool eventFilter(QObject *object, QEvent *event); + +private: + MessageEditor *m_messageEditor; +}; + +bool FocusWatcher::eventFilter(QObject *, QEvent *event) +{ + if (event->type() == QEvent::FocusIn) + m_messageEditor->setEditorFocus(-1); + return false; +} + +MainWindow::MainWindow() + : QMainWindow(0, Qt::Window), + m_assistantProcess(0), + m_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) +{ + 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()->setResizeMode(1, QHeaderView::Stretch); + m_contextView->header()->setResizeMode(2, QHeaderView::ResizeToContents); + 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_messageView->setColumnHidden(2, true); + // last visible column auto-stretches + + 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(); + 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; + + 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; +} + +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(); + 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); +} + +void MainWindow::print() +{ + int pageNum = 0; + QPrintDialog dlg(&m_printer, this); + if (dlg.exec()) { + QApplication::setOverrideCursor(Qt::WaitCursor); + m_printer.setDocName(m_dataModel->condensedSrcFileNames(true)); + statusBar()->showMessage(tr("Printing...")); + PrintOut pout(&m_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; + 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(&m_printer, this); + if (dlg.exec()) { + m_printer.setDocName(phraseBook->fileName()); + statusBar()->showMessage(tr("Printing...")); + PrintOut pout(&m_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('\0') << endl; +} + +void MainWindow::about() +{ + QMessageBox box(this); + box.setTextFormat(Qt::RichText); + QString version = tr("Version %1"); +#if QT_EDITION == QT_EDITION_OPENSOURCE + QString open = tr(" Open Source Edition"); + version.append(open); +#endif + version = version.arg(QLatin1String(QT_VERSION_STR)); + + QString edition = +#if QT_EDITION == QT_EDITION_OPENSOURCE + tr("This version of Qt Linguist is part of the Qt Open Source Edition, for use " + "in the development of Open Source applications. " + "Qt is a comprehensive C++ framework for cross-platform application " + "development.<br/><br/>" + "You need a commercial Qt license for development of proprietary (closed " + "source) applications. Please see <tt>http://qtsoftware.com/company/model" + ".html</tt> for an overview of Qt licensing."); +#elif defined(QT_PRODUCT_LICENSE) + tr("This program is licensed to you under the terms of the " + "Qt %1 License Agreement. For details, see the license file " + "that came with this software distribution.").arg(QLatin1String(QT_PRODUCT_LICENSE)); +#else + tr("This program is licensed to you under the terms of the " + "Qt Commercial License Agreement. For details, see the file LICENSE " + "that came with this software distribution."); +#endif + + 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>%2</p>" + "<p>Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)." + "</p><p>The program is provided AS IS with NO WARRANTY OF ANY KIND," + " INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A" + " PARTICULAR PURPOSE.</p>").arg(version).arg(edition)); + + 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) { + 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 = 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 (hasFormPreview(m->fileName())) + m_formPreviewView->setSourceContext(m_currentIndex.model(), m); + updateDanger(m_currentIndex, true); + + if (m->isFinished()) + m_dataModel->setFinished(m_currentIndex, false); + else + m_dataModel->setModified(m_currentIndex.model(), true); +} + +void MainWindow::updateTranslatorComment(const QString &comment) +{ + MessageItem *m = m_dataModel->messageItem(m_currentIndex); + if (!m) + return; + if (comment == m->translatorComment()) + return; + + m->setTranslatorComment(comment); + + m_dataModel->setModified(m_currentIndex.model(), true); +} + +void MainWindow::refreshItemViews() +{ + m_messageModel->blockSignals(false); + m_contextView->update(); + m_messageView->update(); + setWindowModified(m_dataModel->isModified()); + m_modifiedLabel->setVisible(m_dataModel->isModified()); + updateStatistics(); +} + +void MainWindow::doneAndNext() +{ + int model = m_messageEditor->activeModel(); + if (model >= 0 && m_dataModel->isModelWritable(model)) + m_dataModel->setFinished(m_currentIndex, true); + + if (!m_messageEditor->focusNextUnfinished()) + nextUnfinished(); +} + +void MainWindow::toggleFinished(const QModelIndex &index) +{ + if (!index.isValid() || index.column() - 1 >= m_dataModel->modelCount() + || !m_dataModel->isModelWritable(index.column() - 1) || index.parent() == QModelIndex()) + return; + + QModelIndex item = m_sortedMessagesModel->mapToSource(index); + MultiDataIndex dataIndex = m_messageModel->dataIndex(item); + MessageItem *m = m_dataModel->messageItem(dataIndex); + + if (!m || m->message().type() == TranslatorMessage::Obsolete) + return; + + m_dataModel->setFinished(dataIndex, !m->isFinished()); +} + +/* + * Receives a context index in the sorted messages model and returns the next + * logical context index in the same model, based on the sort order of the + * contexts in the sorted contexts model. + */ +QModelIndex MainWindow::nextContext(const QModelIndex &index) const +{ + QModelIndex sortedContextIndex = m_sortedContextsModel->mapFromSource( + m_sortedMessagesModel->mapToSource(index)); + + int nextRow = sortedContextIndex.row() + 1; + if (nextRow >= m_sortedContextsModel->rowCount()) + nextRow = 0; + sortedContextIndex = m_sortedContextsModel->index(nextRow, index.column()); + + return m_sortedMessagesModel->mapFromSource( + m_sortedContextsModel->mapToSource(sortedContextIndex)); +} + +/* + * See nextContext. + */ +QModelIndex MainWindow::prevContext(const QModelIndex &index) const +{ + QModelIndex sortedContextIndex = m_sortedContextsModel->mapFromSource( + m_sortedMessagesModel->mapToSource(index)); + + int prevRow = sortedContextIndex.row() - 1; + if (prevRow < 0) prevRow = m_sortedContextsModel->rowCount() - 1; + sortedContextIndex = m_sortedContextsModel->index(prevRow, index.column()); + + return m_sortedMessagesModel->mapFromSource( + m_sortedContextsModel->mapToSource(sortedContextIndex)); +} + +QModelIndex MainWindow::nextMessage(const QModelIndex ¤tIndex, bool checkUnfinished) const +{ + QModelIndex idx = currentIndex.isValid() ? currentIndex : m_sortedMessagesModel->index(0, 0); + do { + int row = 0; + QModelIndex par = idx.parent(); + if (par.isValid()) { + row = idx.row() + 1; + } else { // In case we are located on a top-level node + par = idx; + } + + if (row >= m_sortedMessagesModel->rowCount(par)) { + par = nextContext(par); + row = 0; + } + idx = m_sortedMessagesModel->index(row, idx.column(), par); + + if (!checkUnfinished) + return idx; + + QModelIndex item = m_sortedMessagesModel->mapToSource(idx); + MultiDataIndex index = m_messageModel->dataIndex(item, -1); + if (m_dataModel->multiMessageItem(index)->isUnfinished()) + return idx; + } while (idx != currentIndex); + return QModelIndex(); +} + +QModelIndex MainWindow::prevMessage(const QModelIndex ¤tIndex, bool checkUnfinished) const +{ + QModelIndex idx = currentIndex.isValid() ? currentIndex : m_sortedMessagesModel->index(0, 0); + do { + int row = idx.row() - 1; + QModelIndex par = idx.parent(); + if (!par.isValid()) { // In case we are located on a top-level node + par = idx; + row = -1; + } + + if (row < 0) { + par = prevContext(par); + row = m_sortedMessagesModel->rowCount(par) - 1; + } + idx = m_sortedMessagesModel->index(row, idx.column(), par); + + if (!checkUnfinished) + return idx; + + QModelIndex item = m_sortedMessagesModel->mapToSource(idx); + MultiDataIndex index = m_messageModel->dataIndex(item, -1); + if (m_dataModel->multiMessageItem(index)->isUnfinished()) + return idx; + } while (idx != currentIndex); + return QModelIndex(); +} + +void MainWindow::nextUnfinished() +{ + if (m_ui.actionNextUnfinished->isEnabled()) { + if (!next(true)) { + // If no Unfinished message is left, the user has finished the job. We + // congratulate on a job well done with this ringing bell. + statusBar()->showMessage(tr("No untranslated translation units left."), MessageMS); + qApp->beep(); + } + } +} + +void MainWindow::prevUnfinished() +{ + if (m_ui.actionNextUnfinished->isEnabled()) { + if (!prev(true)) { + // If no Unfinished message is left, the user has finished the job. We + // congratulate on a job well done with this ringing bell. + statusBar()->showMessage(tr("No untranslated translation units left."), MessageMS); + qApp->beep(); + } + } +} + +void MainWindow::prev() +{ + prev(false); +} + +void MainWindow::next() +{ + next(false); +} + +bool MainWindow::prev(bool checkUnfinished) +{ + QModelIndex index = prevMessage(m_messageView->currentIndex(), checkUnfinished); + if (index.isValid()) + setCurrentMessage(m_sortedMessagesModel->mapToSource(index)); + if (checkUnfinished) + m_messageEditor->setUnfinishedEditorFocus(); + else + m_messageEditor->setEditorFocus(); + return index.isValid(); +} + +bool MainWindow::next(bool checkUnfinished) +{ + QModelIndex index = nextMessage(m_messageView->currentIndex(), checkUnfinished); + if (index.isValid()) + setCurrentMessage(m_sortedMessagesModel->mapToSource(index)); + if (checkUnfinished) + m_messageEditor->setUnfinishedEditorFocus(); + else + m_messageEditor->setEditorFocus(); + return index.isValid(); +} + +void MainWindow::findNext(const QString &text, DataModel::FindLocation where, bool matchCase, bool ignoreAccelerators) +{ + if (text.isEmpty()) + return; + m_findText = text; + m_findWhere = where; + m_findMatchCase = matchCase ? Qt::CaseSensitive : Qt::CaseInsensitive; + m_findIgnoreAccelerators = ignoreAccelerators; + m_ui.actionFindNext->setEnabled(true); + findAgain(); +} + +void MainWindow::revalidate() +{ + for (MultiDataModelIterator it(m_dataModel, -1); it.isValid(); ++it) + updateDanger(it, false); + + if (m_currentIndex.isValid()) + updateDanger(m_currentIndex, true); +} + +QString MainWindow::friendlyString(const QString& str) +{ + QString f = str.toLower(); + f.replace(QRegExp(QString(QLatin1String("[.,:;!?()-]"))), QString(QLatin1String(" "))); + f.remove(QLatin1Char('&')); + return f.simplified(); +} + +void MainWindow::setupMenuBar() +{ + 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.actionCopy->setIcon(QIcon(resourcePrefix() + QLatin1String("/editcopy.png"))); + m_ui.actionCut->setIcon(QIcon(resourcePrefix() + QLatin1String("/editcut.png"))); + m_ui.actionPaste->setIcon(QIcon(resourcePrefix() + QLatin1String("/editpaste.png"))); + m_ui.actionOpen->setIcon(QIcon(resourcePrefix() + QLatin1String("/fileopen.png"))); + m_ui.actionOpenAux->setIcon(QIcon(resourcePrefix() + QLatin1String("/fileopen.png"))); + m_ui.actionSaveAll->setIcon(QIcon(resourcePrefix() + QLatin1String("/filesave.png"))); + m_ui.actionSave->setIcon(QIcon(resourcePrefix() + QLatin1String("/filesave.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.actionPrint->setIcon(QIcon(resourcePrefix() + QLatin1String("/print.png"))); + m_ui.actionRedo->setIcon(QIcon(resourcePrefix() + QLatin1String("/redo.png"))); + m_ui.actionFind->setIcon(QIcon(resourcePrefix() + QLatin1String("/searchfind.png"))); + m_ui.actionUndo->setIcon(QIcon(resourcePrefix() + QLatin1String("/undo.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 (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(); +} + +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(); + + if (m_ui.actionAccelerators->isChecked()) { + bool sk = !QKeySequence::mnemonic(source).isEmpty(); + bool tk = true; + for (int i = 0; i < translations.count() && tk; ++i) { + tk &= !QKeySequence::mnemonic(translations[i]).isEmpty(); + } + + 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 occurence count of the place markers in the map placeMarkerIndexes. + // i.e. the occurence 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() +{ + QString keybase = settingsPrefix(); + QSettings config; + + QRect r(pos(), size()); + restoreGeometry(config.value(keybase + QLatin1String("Geometry/WindowGeometry")).toByteArray()); + restoreState(config.value(keybase + QLatin1String("MainWindowState")).toByteArray()); + + m_ui.actionAccelerators->setChecked( + config.value(keybase + QLatin1String("Validators/Accelerator"), true).toBool()); + m_ui.actionEndingPunctuation->setChecked( + config.value(keybase + QLatin1String("Validators/EndingPunctuation"), true).toBool()); + m_ui.actionPhraseMatches->setChecked( + config.value(keybase + QLatin1String("Validators/PhraseMatch"), true).toBool()); + m_ui.actionPlaceMarkerMatches->setChecked( + config.value(keybase + QLatin1String("Validators/PlaceMarkers"), true).toBool()); + + recentFiles().readConfig(); + + int size = config.beginReadArray(keybase + QLatin1String("OpenedPhraseBooks")); + for (int i = 0; i < size; ++i) { + config.setArrayIndex(i); + openPhraseBook(config.value(QLatin1String("FileName")).toString()); + } + config.endArray(); +} + +void MainWindow::writeConfig() +{ + QString keybase = settingsPrefix(); + QSettings config; + config.setValue(keybase + QLatin1String("Geometry/WindowGeometry"), + saveGeometry()); + config.setValue(keybase + QLatin1String("Validators/Accelerator"), + m_ui.actionAccelerators->isChecked()); + config.setValue(keybase + QLatin1String("Validators/EndingPunctuation"), + m_ui.actionEndingPunctuation->isChecked()); + config.setValue(keybase + QLatin1String("Validators/PhraseMatch"), + m_ui.actionPhraseMatches->isChecked()); + config.setValue(keybase + QLatin1String("Validators/PlaceMarkers"), + m_ui.actionPlaceMarkerMatches->isChecked()); + config.setValue(keybase + QLatin1String("MainWindowState"), + saveState()); + recentFiles().writeConfig(); + + config.beginWriteArray(keybase + QLatin1String("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/tools/linguist/linguist/mainwindow.h b/tools/linguist/linguist/mainwindow.h new file mode 100644 index 0000000..9f6b4d9 --- /dev/null +++ b/tools/linguist/linguist/mainwindow.h @@ -0,0 +1,266 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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> +#include <QtGui/QPrinter> + +QT_BEGIN_NAMESPACE + +class QPixmap; +class QAction; +class QDialog; +class QLabel; +class QMenu; +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; + +const QString &settingsPrefix(); + +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 ¤tIndex, bool checkUnfinished = false) const; + QModelIndex prevMessage(const QModelIndex ¤tIndex, bool checkUnfinished = false) const; + bool next(bool checkUnfinished); + bool prev(bool checkUnfinished); + + void updateStatistics(); + 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); + + // 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/tools/linguist/linguist/mainwindow.ui b/tools/linguist/linguist/mainwindow.ui new file mode 100644 index 0000000..6cc74ac --- /dev/null +++ b/tools/linguist/linguist/mainwindow.ui @@ -0,0 +1,883 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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>30</height> + </rect> + </property> + <widget class="QMenu" name="menuPhrases"> + <property name="title"> + <string>&Phrases</string> + </property> + <widget class="QMenu" name="menuClosePhraseBook"> + <property name="title"> + <string>&Close Phrase Book</string> + </property> + </widget> + <widget class="QMenu" name="menuEditPhraseBook"> + <property name="title"> + <string>&Edit Phrase Book</string> + </property> + </widget> + <widget class="QMenu" name="menuPrintPhraseBook"> + <property name="title"> + <string>&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&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>&View</string> + </property> + <widget class="QMenu" name="menuViewViews"> + <property name="title"> + <string>Vie&ws</string> + </property> + </widget> + <widget class="QMenu" name="menuToolbars"> + <property name="title"> + <string>&Toolbars</string> + </property> + </widget> + <addaction name="actionResetSorting"/> + <addaction name="actionDisplayGuesses"/> + <addaction name="actionStatistics"/> + <addaction name="separator"/> + <addaction name="menuToolbars"/> + <addaction name="menuViewViews"/> + </widget> + <widget class="QMenu" name="menuHelp"> + <property name="title"> + <string>&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>&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>&File</string> + </property> + <widget class="QMenu" name="menuRecentlyOpenedFiles"> + <property name="title"> + <string>Recently Opened &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>&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>&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&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 &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>&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>&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>&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&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>&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>&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 &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>&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 &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>&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>&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&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&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>&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>&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>&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>&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 &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>&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>&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>&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>&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>&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>&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 Trolltech.</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>&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>&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>&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 &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 &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>&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&nly...</string> + </property> + </action> + <action name="actionSaveAll"> + <property name="text"> + <string>&Save All</string> + </property> + <property name="shortcut"> + <string>Ctrl+S</string> + </property> + </action> + <action name="actionReleaseAll"> + <property name="text"> + <string>&Release All</string> + </property> + </action> + <action name="actionClose"> + <property name="text"> + <string>Close</string> + </property> + </action> + <action name="actionCloseAll"> + <property name="text"> + <string>&Close All</string> + </property> + <property name="shortcut"> + <string>Ctrl+W</string> + </property> + </action> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tools/linguist/linguist/messageeditor.cpp b/tools/linguist/linguist/messageeditor.cpp new file mode 100644 index 0000000..d1a251d --- /dev/null +++ b/tools/linguist/linguist/messageeditor.cpp @@ -0,0 +1,865 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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", "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_undoAvail(false), + m_redoAvail(false), + m_cutAvail(false), + m_copyAvail(false), + m_sourceSelected(false), + m_pluralSourceSelected(false), + m_currentSelected(false) +{ + setObjectName(QLatin1String("scroll area")); + + // Use white explicitly as the background color for the editor page. + QPalette p; + p.setColor(QPalette::Active, QPalette::Base, Qt::white); + p.setColor(QPalette::Inactive, QPalette::Base, Qt::white); + p.setColor(QPalette::Disabled, QPalette::Base, Qt::white); + p.setColor(QPalette::Active, QPalette::Window, Qt::white); + p.setColor(QPalette::Inactive, QPalette::Window, Qt::white); + p.setColor(QPalette::Disabled, QPalette::Window, Qt::white); + 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))); + + 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->setObjectName(QLatin1String("editorPage")); + + // Due to CSS being rather broken on the Mac style at the moment, only + // use the border-image on non-Mac systems. + editorPage->setStyleSheet(QLatin1String( +#ifndef Q_WS_MAC + "QFrame#editorPage { border-image: url(:/images/transbox.png) 12 16 16 12 repeat;" + " border-width: 12px 16px 16px 12px; }" +#endif + "QFrame#editorPage { background-color: white; }" + "QLabel { font-weight: bold; }" + )); +#ifdef Q_WS_MAC + editorPage->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); +#endif + 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()), SLOT(selectionChanged())); + + 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()), SLOT(selectionChanged())); + + 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()), SLOT(selectionChanged())); + connect(ed.transCommentText, SIGNAL(textChanged()), SLOT(emitTranslatorCommentChanged())); + connect(ed.transCommentText, SIGNAL(textChanged()), SLOT(resetHoverSelection())); + connect(ed.transCommentText, SIGNAL(cursorPositionChanged()), SLOT(resetHoverSelection())); + 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()->getEditor()->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) +{ + FormWidget *transEditor = new FormWidget(label, true); + QFont font; + font.setPointSize(static_cast<int>(m_editors[model].fontSize)); + transEditor->getEditor()->setFont(font); + transEditor->setEditingEnabled(writable); + transEditor->setHideWhenEmpty(!writable); + if (!m_editors[model].transTexts.isEmpty()) + transEditor->setVisible(false); + static_cast<QBoxLayout *>(m_editors[model].container->layout())->insertWidget( + m_editors[model].transTexts.count(), transEditor); + + transEditor->getEditor()->installEventFilter(this); + connect(transEditor, SIGNAL(selectionChanged()), SLOT(selectionChanged())); + connect(transEditor, SIGNAL(textChanged()), SLOT(emitTranslationChanged())); + connect(transEditor, SIGNAL(textChanged()), SLOT(resetHoverSelection())); + connect(transEditor, SIGNAL(cursorPositionChanged()), SLOT(resetHoverSelection())); + + m_editors[model].transTexts << transEditor; +} + +/*! 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 bool clearFormSelection(FormWidget *fw, FormWidget *te) +{ + if (fw != te) { + QTextEdit *t = fw->getEditor(); + bool oldBlockState = t->blockSignals(true); + QTextCursor c = t->textCursor(); + c.clearSelection(); + t->setTextCursor(c); + t->blockSignals(oldBlockState); + return true; + } + return false; +} + +// Clear the selection for all textedits except the sender +void MessageEditor::selectionChanged() +{ + if (!resetSelection(qobject_cast<FormWidget *>(sender()))) + updateCanCutCopy(); +} + +bool MessageEditor::resetHoverSelection(FormWidget *fw) +{ + if (m_sourceSelected) { + if (clearFormSelection(m_source, fw)) { + updateCanCutCopy(); + return true; + } + } else if (m_pluralSourceSelected) { + if (clearFormSelection(m_pluralSource, fw)) { + updateCanCutCopy(); + return true; + } + } + return false; +} + +bool MessageEditor::resetSelection(FormWidget *fw) +{ + if (resetHoverSelection(fw)) + return true; + if (m_currentSelected) { + MessageEditorData &ed = m_editors[m_currentModel]; + FormWidget *cfw = (m_currentNumerus < 0) ? ed.transCommentText + : ed.transTexts[m_currentNumerus]; + if (clearFormSelection(cfw, fw)) { + updateCanCutCopy(); + return true; + } + } + return false; +} + +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) + if (m_editors[j].transTexts[i]->getEditor()->hasFocus()) { + *model = j; + *numerus = i; + return; + } + if (m_editors[j].transCommentText->getEditor()->hasFocus()) { + *model = j; + *numerus = -1; + return; + } + } + *model = -1; + *numerus = -1; +} + +FormWidget *MessageEditor::activeTranslation() const +{ + if (m_currentNumerus < 0) + return 0; + return m_editors[m_currentModel].transTexts[m_currentNumerus]; +} + +FormWidget *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[0]->getEditor()->isReadOnly()) + return m_editors[i].transTexts[0]; + return 0; + } + return m_editors[m_currentModel].transTexts[m_currentNumerus]; +} + +FormWidget *MessageEditor::activeTransComment() const +{ + if (m_currentModel < 0 || m_currentNumerus >= 0) + return 0; + return m_editors[m_currentModel].transCommentText; +} + +FormWidget *MessageEditor::activeEditor() const +{ + if (FormWidget *fw = activeTransComment()) + return fw; + return activeTranslation(); +} + +FormWidget *MessageEditor::activeOr1stEditor() const +{ + if (FormWidget *fw = activeTransComment()) + return fw; + return activeOr1stTranslation(); +} + +void MessageEditor::setTargetLanguage(int model) +{ + const QStringList &numerusForms = m_dataModel->model(model)->numerusForms(); + const QString &langLocalized = m_dataModel->model(model)->localizedLanguage(); + bool added = false; + 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)); + QWidget *prev; + if (i > 0) + prev = m_editors[model].transTexts[i - 1]->getEditor(); + else if (model) + prev = m_editors[model - 1].transCommentText->getEditor(); + else + prev = this; + setTabOrder(prev, m_editors[model].transTexts[i]->getEditor()); + added = true; + } 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)); + if (added) + setTabOrder(m_editors[model].transTexts.last()->getEditor(), m_editors[model].transCommentText->getEditor()); +} + +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) + if (m_editors[j].transTexts[i]->getEditor() == 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) + med->transTexts[i]->getEditor()->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 (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) { + 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(); + } + } + + return QScrollArea::eventFilter(o, e); +} + +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 (FormWidget *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; + FormWidget *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; + } + FormWidget *transForm = m_editors[latestModel].transTexts[numerus]; + transForm->getEditor()->setFocus(); + transForm->setTranslation(translation, true); + + updateBeginFromSource(); +} + +void MessageEditor::setEditingEnabled(int model, bool enabled) +{ + MessageEditorData &ed = m_editors[model]; + foreach (FormWidget *widget, ed.transTexts) + widget->setEditingEnabled(enabled); + ed.transCommentText->setEditingEnabled(enabled); + + updateCanPaste(); +} + +void MessageEditor::undo() +{ + activeEditor()->getEditor()->document()->undo(); +} + +void MessageEditor::redo() +{ + activeEditor()->getEditor()->document()->redo(); +} + +void MessageEditor::updateUndoRedo() +{ + bool newUndoAvail = false; + bool newRedoAvail = false; + if (FormWidget *fw = activeEditor()) { + QTextDocument *doc = fw->getEditor()->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() +{ + QTextEdit *editor = activeEditor()->getEditor(); + if (editor->textCursor().hasSelection()) + editor->cut(); +} + +void MessageEditor::copy() +{ + QTextEdit *te; + if ((te = m_source->getEditor())->textCursor().hasSelection() + || (te = m_pluralSource->getEditor())->textCursor().hasSelection() + || (te = activeEditor()->getEditor())->textCursor().hasSelection()) + te->copy(); +} + +void MessageEditor::updateCanCutCopy() +{ + bool newCopyState = false; + bool newCutState = false; + + m_sourceSelected = m_source->getEditor()->textCursor().hasSelection(); + m_pluralSourceSelected = m_pluralSource->getEditor()->textCursor().hasSelection(); + m_currentSelected = false; + + if (m_sourceSelected || m_pluralSourceSelected) { + newCopyState = true; + } else if (FormWidget *fw = activeEditor()) { + QTextEdit *te = fw->getEditor(); + if (te->textCursor().hasSelection()) { + m_currentSelected = true; + newCopyState = true; + newCutState = !te->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()->getEditor()->paste(); +} + +void MessageEditor::updateCanPaste() +{ + FormWidget *fw; + emit pasteAvailable(!m_clipboardEmpty + && (fw = activeEditor()) && !fw->getEditor()->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; + FormWidget *fw; + if ((te = m_source->getEditor())->underMouse() + || (te = m_pluralSource->getEditor())->underMouse() + || ((fw = activeEditor()) && (te = fw->getEditor())->hasFocus())) + te->selectAll(); +} + +void MessageEditor::emitTranslationChanged() +{ + updateBeginFromSource(); + updateUndoRedo(); + emit translationChanged(translations(m_currentModel)); +} + +void MessageEditor::emitTranslatorCommentChanged() +{ + updateUndoRedo(); + emit translatorCommentChanged(m_editors[m_currentModel].transCommentText->getTranslation()); +} + +void MessageEditor::updateBeginFromSource() +{ + bool overwrite = false; + if (FormWidget *transForm = activeTranslation()) { + QTextEdit *activeEditor = transForm->getEditor(); + 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 (FormWidget *transForm = activeOr1stEditor()) + transForm->getEditor()->setFocus(); +} + +void MessageEditor::setEditorFocus(int model) +{ + if (m_currentModel != model) { + if (model < 0) { + resetSelection(); + m_currentNumerus = -1; + m_currentModel = -1; + emit activeModelChanged(activeModel()); + updateBeginFromSource(); + updateUndoRedo(); + updateCanPaste(); + } else { + m_editors[model].transTexts[0]->getEditor()->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[0]->getEditor()->setFocus(); + return true; + } + return false; +} + +void MessageEditor::setUnfinishedEditorFocus() +{ + focusNextUnfinished(0); +} + +bool MessageEditor::focusNextUnfinished() +{ + return focusNextUnfinished(m_currentModel + 1); +} + +QT_END_NAMESPACE diff --git a/tools/linguist/linguist/messageeditor.h b/tools/linguist/linguist/messageeditor.h new file mode 100644 index 0000000..de563ec --- /dev/null +++ b/tools/linguist/linguist/messageeditor.h @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MESSAGEEDITOR_H +#define MESSAGEEDITOR_H + +#include "messagemodel.h" + +#include <QtCore/QLocale> + +#include <QtGui/QFrame> +#include <QtGui/QScrollArea> + +QT_BEGIN_NAMESPACE + +class QBoxLayout; +class QMainWindow; +class QTextEdit; + +class MessageEditor; +class FormatTextEdit; +class FormWidget; + +struct MessageEditorData { + QWidget *container; + FormWidget *transCommentText; + QList<FormWidget*> transTexts; + QString invariantForm; + QString firstForm; + float 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); + +private slots: + void selectionChanged(); + bool resetHoverSelection(FormWidget *fw = 0); + void emitTranslationChanged(); + void emitTranslatorCommentChanged(); + void updateCanPaste(); + void clipboardChanged(); + void messageModelAppended(); + void messageModelDeleted(int model); + void allModelsDeleted(); + void setTargetLanguage(int model); + +private: + void setupEditorPage(); + void setEditingEnabled(int model, bool enabled); + bool focusNextUnfinished(int start); + bool resetSelection(FormWidget *fw = 0); + void activeModelAndNumerus(int *model, int *numerus) const; + FormWidget *activeTranslation() const; + FormWidget *activeOr1stTranslation() const; + FormWidget *activeTransComment() const; + FormWidget *activeEditor() const; + FormWidget *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); + QPalette paletteForModel(int model) const; + + MultiDataModel *m_dataModel; + + MultiDataIndex m_currentIndex; + int m_currentModel; + int m_currentNumerus; + + bool m_undoAvail; + bool m_redoAvail; + bool m_cutAvail; + bool m_copyAvail; + bool m_sourceSelected; + bool m_pluralSourceSelected; + bool m_currentSelected; + + bool m_clipboardEmpty; + + QBoxLayout *m_layout; + FormWidget *m_source; + FormWidget *m_pluralSource; + FormWidget *m_commentText; + QList<MessageEditorData> m_editors; +}; + +QT_END_NAMESPACE + +#endif // MESSAGEEDITOR_H diff --git a/tools/linguist/linguist/messageeditorwidgets.cpp b/tools/linguist/linguist/messageeditorwidgets.cpp new file mode 100644 index 0000000..7412571 --- /dev/null +++ b/tools/linguist/linguist/messageeditorwidgets.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "messageeditorwidgets.h" +#include "messagehighlighter.h" + +#include <QAbstractTextDocumentLayout> +#include <QAction> +#include <QApplication> +#include <QClipboard> +#include <QDebug> +#include <QLayout> +#include <QMenu> +#include <QPainter> +#include <QScrollArea> +#include <QTextBlock> +#include <QTextDocumentFragment> +#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) +{ + bool oldBlockState = false; + if (!userAction) { + // Prevent contentsChanged signal + oldBlockState = document()->blockSignals(true); + document()->setUndoRedoEnabled(false); + ExpandingTextEdit::setPlainText(text); + // highlighter is out of sync because of blocked signals + m_highlighter->rehighlight(); + document()->setUndoRedoEnabled(true); + document()->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); + 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->document(), SIGNAL(contentsChanged()), SIGNAL(textChanged())); + connect(m_editor, SIGNAL(selectionChanged()), SIGNAL(selectionChanged())); + connect(m_editor, SIGNAL(cursorPositionChanged()), SIGNAL(cursorPositionChanged())); +} + +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); +} + + +QT_END_NAMESPACE diff --git a/tools/linguist/linguist/messageeditorwidgets.h b/tools/linguist/linguist/messageeditorwidgets.h new file mode 100644 index 0000000..b1609e5 --- /dev/null +++ b/tools/linguist/linguist/messageeditorwidgets.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MESSAGEEDITORWIDGETS_H +#define MESSAGEEDITORWIDGETS_H + +#include <QImage> +#include <QLabel> +#include <QMap> +#include <QTextEdit> +#include <QUrl> +#include <QWidget> + +QT_BEGIN_NAMESPACE + +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(); + void selectionChanged(); + void cursorPositionChanged(); + +private: + QLabel *m_label; + FormatTextEdit *m_editor; + bool m_hideWhenEmpty; +}; + +QT_END_NAMESPACE + +#endif // MESSAGEEDITORWIDGETS_H diff --git a/tools/linguist/linguist/messagehighlighter.cpp b/tools/linguist/linguist/messagehighlighter.cpp new file mode 100644 index 0000000..4965582 --- /dev/null +++ b/tools/linguist/linguist/messagehighlighter.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/messagehighlighter.h b/tools/linguist/linguist/messagehighlighter.h new file mode 100644 index 0000000..0fd061b --- /dev/null +++ b/tools/linguist/linguist/messagehighlighter.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/messagemodel.cpp b/tools/linguist/linguist/messagemodel.cpp new file mode 100644 index 0000000..a7053cf --- /dev/null +++ b/tools/linguist/linguist/messagemodel.cpp @@ -0,0 +1,1403 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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_language, m_country); +} + +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; + } + + QList<TranslatorMessage> dupes = tor.findDuplicates(); + if (!dupes.isEmpty()) { + QString err = tr("<qt>Duplicate messages found in '%1':").arg(Qt::escape(fileName)); + int numdups = 0; + foreach (const TranslatorMessage &msg, dupes) { + 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())); + } + 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); + 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::appendMessageItem(MessageItem *m) +{ + for (int i = 0; i < m_messageLists.count() - 1; ++i) + m_messageLists[i].append(0); + m_messageLists.last().append(m); + m_multiMessageList.append(MultiMessageItem(m)); +} + +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(); + 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); + 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 { + int msgCnt = mc->messageCount(); + m_msgModel->beginInsertRows(m_msgModel->createIndex(mcx, 0, 0), msgCnt, msgCnt); + mc->appendMessageItem(m); + m_msgModel->endInsertRows(); + ++m_numMessages; + } + } + } else { + MultiContextItem item(modelCount() - 1, c, readWrite); + m_msgModel->beginInsertRows(QModelIndex(), contextCount(), contextCount()); + m_multiContextList.append(item); + m_msgModel->endInsertRows(); + m_numMessages += item.messageCount(); + } + } + 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; + int delCol = m_dataModels.count(); + m_msgModel->beginRemoveColumns(QModelIndex(), 1, delCol); + for (int i = m_multiContextList.size(); --i >= 0;) { + m_msgModel->beginRemoveColumns(m_msgModel->createIndex(i, 0, 0), 1, delCol); + m_msgModel->endRemoveColumns(); + } + qDeleteAll(m_dataModels); + m_dataModels.clear(); + m_multiContextList.clear(); + m_msgModel->endRemoveColumns(); + 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 = + qVariantFromValue(QPixmap(QLatin1String(":/images/s_check_on.png"))); + static QVariant pxOff = + qVariantFromValue(QPixmap(QLatin1String(":/images/s_check_off.png"))); + static QVariant pxObsolete = + qVariantFromValue(QPixmap(QLatin1String(":/images/s_check_obsolete.png"))); + static QVariant pxDanger = + qVariantFromValue(QPixmap(QLatin1String(":/images/s_check_danger.png"))); + static QVariant pxWarning = + qVariantFromValue(QPixmap(QLatin1String(":/images/s_check_warning.png"))); + static QVariant pxEmpty = + qVariantFromValue(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/tools/linguist/linguist/messagemodel.h b/tools/linguist/linguist/messagemodel.h new file mode 100644 index 0000000..3b75f7a --- /dev/null +++ b/tools/linguist/linguist/messagemodel.h @@ -0,0 +1,535 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 appendMessageItem(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/tools/linguist/linguist/phrase.cpp b/tools/linguist/linguist/phrase.cpp new file mode 100644 index 0000000..563c72d --- /dev/null +++ b/tools/linguist/linguist/phrase.cpp @@ -0,0 +1,356 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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("&")); + p.replace(QLatin1Char('\"'), QLatin1String(""")); + p.replace(QLatin1Char('>'), QLatin1String(">")); + p.replace(QLatin1Char('<'), QLatin1String("<")); + p.replace(QLatin1Char('\''), QLatin1String("'")); + 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 == QString(QLatin1String("QPH"))) { + m_language = atts.value(QLatin1String("language")); + m_sourceLanguage = atts.value(QLatin1String("sourcelanguage")); + } else if (qName == QString(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 == QString(QLatin1String("source"))) + source = accum; + else if (qName == QString(QLatin1String("target"))) + target = accum; + else if (qName == QString(QLatin1String("definition"))) + definition = accum; + else if (qName == QString(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; + msg.sprintf("Parse error at line %d, column %d (%s).", + exception.lineNumber(), exception.columnNumber(), + exception.message().toLatin1().constData()); + 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://qtsoftware.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/tools/linguist/linguist/phrase.h b/tools/linguist/linguist/phrase.h new file mode 100644 index 0000000..6747c80 --- /dev/null +++ b/tools/linguist/linguist/phrase.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/phrasebookbox.cpp b/tools/linguist/linguist/phrasebookbox.cpp new file mode 100644 index 0000000..d3bb937 --- /dev/null +++ b/tools/linguist/linguist/phrasebookbox.cpp @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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(const QModelIndex &, const 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/tools/linguist/linguist/phrasebookbox.h b/tools/linguist/linguist/phrasebookbox.h new file mode 100644 index 0000000..b34cae5 --- /dev/null +++ b/tools/linguist/linguist/phrasebookbox.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/phrasebookbox.ui b/tools/linguist/linguist/phrasebookbox.ui new file mode 100644 index 0000000..32c51f6 --- /dev/null +++ b/tools/linguist/linguist/phrasebookbox.ui @@ -0,0 +1,236 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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>&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&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>&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>&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>&Remove Entry</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="settingsBut"> + <property name="text"> + <string>Settin&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>&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/tools/linguist/linguist/phrasemodel.cpp b/tools/linguist/linguist/phrasemodel.cpp new file mode 100644 index 0000000..5368978 --- /dev/null +++ b/tools/linguist/linguist/phrasemodel.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/phrasemodel.h b/tools/linguist/linguist/phrasemodel.h new file mode 100644 index 0000000..4013a0b --- /dev/null +++ b/tools/linguist/linguist/phrasemodel.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/phraseview.cpp b/tools/linguist/linguist/phraseview.cpp new file mode 100644 index 0000000..78c9151 --- /dev/null +++ b/tools/linguist/linguist/phraseview.cpp @@ -0,0 +1,271 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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 settingsPrefix() + QLatin1String("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/tools/linguist/linguist/phraseview.h b/tools/linguist/linguist/phraseview.h new file mode 100644 index 0000000..39a5355 --- /dev/null +++ b/tools/linguist/linguist/phraseview.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/printout.cpp b/tools/linguist/linguist/printout.cpp new file mode 100644 index 0000000..924180c --- /dev/null +++ b/tools/linguist/linguist/printout.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 inital 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/tools/linguist/linguist/printout.h b/tools/linguist/linguist/printout.h new file mode 100644 index 0000000..11ffa63 --- /dev/null +++ b/tools/linguist/linguist/printout.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/recentfiles.cpp b/tools/linguist/linguist/recentfiles.cpp new file mode 100644 index 0000000..6fc72f7 --- /dev/null +++ b/tools/linguist/linguist/recentfiles.cpp @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "recentfiles.h" + +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> +#include <QtCore/QSettings> +#include <QtCore/QString> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE + +const QString &settingsPrefix(); + +static QString configKey() +{ + return settingsPrefix() + QLatin1String("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/tools/linguist/linguist/recentfiles.h b/tools/linguist/linguist/recentfiles.h new file mode 100644 index 0000000..d0d25eb --- /dev/null +++ b/tools/linguist/linguist/recentfiles.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/sourcecodeview.cpp b/tools/linguist/linguist/sourcecodeview.cpp new file mode 100644 index 0000000..c948d77 --- /dev/null +++ b/tools/linguist/linguist/sourcecodeview.cpp @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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.isNull()) { + 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/tools/linguist/linguist/sourcecodeview.h b/tools/linguist/linguist/sourcecodeview.h new file mode 100644 index 0000000..99a28c5 --- /dev/null +++ b/tools/linguist/linguist/sourcecodeview.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/statistics.cpp b/tools/linguist/linguist/statistics.cpp new file mode 100644 index 0000000..2cd197f --- /dev/null +++ b/tools/linguist/linguist/statistics.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/statistics.h b/tools/linguist/linguist/statistics.h new file mode 100644 index 0000000..175f2e1 --- /dev/null +++ b/tools/linguist/linguist/statistics.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/statistics.ui b/tools/linguist/linguist/statistics.ui new file mode 100644 index 0000000..b7b2e04 --- /dev/null +++ b/tools/linguist/linguist/statistics.ui @@ -0,0 +1,211 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/translatedialog.cpp b/tools/linguist/linguist/translatedialog.cpp new file mode 100644 index 0000000..537c364 --- /dev/null +++ b/tools/linguist/linguist/translatedialog.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/translatedialog.h b/tools/linguist/linguist/translatedialog.h new file mode 100644 index 0000000..fbed2df --- /dev/null +++ b/tools/linguist/linguist/translatedialog.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/linguist/translatedialog.ui b/tools/linguist/linguist/translatedialog.ui new file mode 100644 index 0000000..89f2138 --- /dev/null +++ b/tools/linguist/linguist/translatedialog.ui @@ -0,0 +1,260 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 &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>&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 &case</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="ckMarkFinished"> + <property name="text"> + <string>Mark new translation as &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/tools/linguist/linguist/translationsettings.ui b/tools/linguist/linguist/translationsettings.ui new file mode 100644 index 0000000..c868360 --- /dev/null +++ b/tools/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/tools/linguist/linguist/translationsettingsdialog.cpp b/tools/linguist/linguist/translationsettingsdialog.cpp new file mode 100644 index 0000000..32f5c58 --- /dev/null +++ b/tools/linguist/linguist/translationsettingsdialog.cpp @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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)); + + for (int i = QLocale::AnyCountry + 1; i < QLocale::LastCountry; ++i) { + QString country = QLocale::countryToString(QLocale::Country(i)); + m_ui.srcCbCountryList->addItem(country, QVariant(i)); + } + m_ui.srcCbCountryList->model()->sort(0, Qt::AscendingOrder); + m_ui.srcCbCountryList->insertItem(0, tr("Any Country"), QVariant(QLocale::AnyCountry)); + + m_ui.tgtCbLanguageList->setModel(m_ui.srcCbLanguageList->model()); + m_ui.tgtCbCountryList->setModel(m_ui.srcCbCountryList->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)); +} + +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/tools/linguist/linguist/translationsettingsdialog.h b/tools/linguist/linguist/translationsettingsdialog.h new file mode 100644 index 0000000..8a633d9 --- /dev/null +++ b/tools/linguist/linguist/translationsettingsdialog.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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(); + +private: + Ui::TranslationSettingsDialog m_ui; + DataModel *m_dataModel; + PhraseBook *m_phraseBook; + +}; + +QT_END_NAMESPACE + +#endif // TRANSLATIONSETTINGSDIALOG_H diff --git a/tools/linguist/lrelease/lrelease.1 b/tools/linguist/lrelease/lrelease.1 new file mode 100644 index 0000000..7e01d68 --- /dev/null +++ b/tools/linguist/lrelease/lrelease.1 @@ -0,0 +1,97 @@ +.TH lrelease 1 "18 October 2001" "Nokia Corporation and/or its subsidiary(-ies)" \" -*- nroff -*- +.\" +.\" Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +\" Contact: Qt Software Information (qt-info@nokia.com) +.\" +.\" This file may be distributed and/or modified under the terms of the +.\" GNU General Public License version 2 as published by the Free Software +.\" Foundation and appearing in the file LICENSE.GPL included in the +.\" packaging of this file. +.\" +.\" This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +.\" WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +.\" +.\" See http://qtsoftware.com/gpl/ for GPL licensing information. +.\" +.\" Contact qt-info@nokia.com if any conditions of this licensing are +.\" not clear to you. +.\" +.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" +Don't 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://doc.trolltech.com/i18n.html diff --git a/tools/linguist/lrelease/lrelease.pro b/tools/linguist/lrelease/lrelease.pro new file mode 100644 index 0000000..03ea0d0 --- /dev/null +++ b/tools/linguist/lrelease/lrelease.pro @@ -0,0 +1,24 @@ +TEMPLATE = app +TARGET = lrelease +DESTDIR = ../../../bin + +QT -= gui + +CONFIG += qt warn_on console +CONFIG -= app_bundle + +build_all:!build_pass { + CONFIG -= build_all + CONFIG += release +} + +DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII +SOURCES += main.cpp + +include(../../../src/qt_professional.pri) +include(../shared/formats.pri) +include(../shared/proparser.pri) +include(../shared/translatortools.pri) + +target.path=$$[QT_INSTALL_BINS] +INSTALLS += target diff --git a/tools/linguist/lrelease/main.cpp b/tools/linguist/lrelease/main.cpp new file mode 100644 index 0000000..0440325 --- /dev/null +++ b/tools/linguist/lrelease/main.cpp @@ -0,0 +1,271 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "translator.h" +#include "profileevaluator.h" + +#include <QtCore/QCoreApplication> +#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 <QtCore/QTranslator> + +static void printOut(const QString & out) +{ + QTextStream stream(stdout); + stream << out; +} + +static void printUsage() +{ + printOut(QCoreApplication::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" + " -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" + " -silent\n" + " Don't 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) { + qWarning("lrelease error: %s\n", qPrintable(cd.error())); + } else { + if (!cd.errors().isEmpty()) + printOut(cd.error()); + const QList<TranslatorMessage> dupes = tor.findDuplicates(); + if (!dupes.isEmpty()) { + qWarning("lrelease error: duplicate messages found in '%s':", + qPrintable(tsFileName)); + foreach (const TranslatorMessage &msg, dupes) { + qWarning("\n* Context: %s\n* Source: %s", + qPrintable(msg.context()), + qPrintable(msg.sourceText())); + if (!msg.comment().isEmpty()) + qWarning("\n* Comment: %s", qPrintable(msg.comment())); + } + ok = false; + } + } + return ok; +} + +static bool releaseTranslator(Translator &tor, const QString &qmFileName, + bool verbose, bool ignoreUnfinished, + bool removeIdentical, TranslatorSaveMode mode) +{ + if (verbose) + printOut(QCoreApplication::tr( "Updating '%1'...\n").arg(qmFileName)); + if (removeIdentical) { + if ( verbose ) + printOut(QCoreApplication::tr( "Removing translations equal to source text in '%1'...\n").arg(qmFileName)); + tor.stripIdenticalSourceTranslations(); + } + + QFile file(qmFileName); + if (!file.open(QIODevice::WriteOnly)) { + qWarning("lrelease error: cannot create '%s': %s\n", + qPrintable(qmFileName), qPrintable(file.errorString())); + return false; + } + + ConversionData cd; + cd.m_verbose = verbose; + cd.m_ignoreUnfinished = ignoreUnfinished; + cd.m_saveMode = mode; + bool ok = tor.release(&file, cd); + file.close(); + + if (!ok) { + qWarning("lrelease error: cannot save '%s': %s\n", + qPrintable(qmFileName), qPrintable(cd.error())); + return false; + } else if (!cd.errors().isEmpty()) { + printOut(cd.error()); + } + return true; +} + +static bool releaseTsFile(const QString& tsFileName, bool verbose, + bool ignoreUnfinished, bool removeIdentical, TranslatorSaveMode mode) +{ + Translator tor; + if (!loadTsFile(tor, tsFileName, verbose)) + 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, verbose, ignoreUnfinished, removeIdentical, mode); +} + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + QStringList args = app.arguments(); + QTranslator translator; + if (translator.load(QLatin1String("lrelease_") + QLocale::system().name())) + app.installTranslator(&translator); + + bool verbose = true; // the default is true starting with Qt 4.2 + bool ignoreUnfinished = false; + // the default mode is SaveEverything starting with Qt 4.2 + TranslatorSaveMode mode = SaveEverything; + bool removeIdentical = false; + Translator tor; + QString outputFile; + int numFiles = 0; + + for (int i = 1; i < argc; ++i) { + if (args[i] == QLatin1String("-compress")) { + mode = SaveStripped; + continue; + } else if (args[i] == QLatin1String("-nocompress")) { + mode = SaveEverything; + continue; + } else if (args[i] == QLatin1String("-removeidentical")) { + removeIdentical = true; + continue; + } else if (args[i] == QLatin1String("-nounfinished")) { + ignoreUnfinished = true; + continue; + } else if (args[i] == QLatin1String("-silent")) { + verbose = false; + continue; + } else if (args[i] == QLatin1String("-verbose")) { + verbose = true; + continue; + } else if (args[i] == QLatin1String("-version")) { + printOut(QCoreApplication::tr( "lrelease version %1\n").arg(QLatin1String(QT_VERSION_STR)) ); + return 0; + } else if (args[i] == QLatin1String("-qm")) { + if (i == argc - 1) { + printUsage(); + return 1; + } + i++; + outputFile = args[i]; + } else if (args[i] == QLatin1String("-help")) { + printUsage(); + return 0; + } else if (args[i][0] == QLatin1Char('-')) { + printUsage(); + return 1; + } else { + numFiles++; + } + } + + if (numFiles == 0) { + printUsage(); + return 1; + } + + for (int i = 1; i < argc; ++i) { + if (args[i][0] == QLatin1Char('-') || args[i] == outputFile) + continue; + + if (args[i].endsWith(QLatin1String(".pro"), Qt::CaseInsensitive) + || args[i].endsWith(QLatin1String(".pri"), Qt::CaseInsensitive)) { + QHash<QByteArray, QStringList> varMap; + bool ok = evaluateProFile(args[i], verbose, &varMap ); + if (ok) { + QStringList translations = varMap.value("TRANSLATIONS"); + if (translations.isEmpty()) { + qWarning("lrelease warning: Met no 'TRANSLATIONS' entry in" + " project file '%s'\n", + qPrintable(args[i])); + } else { + foreach (const QString &trans, translations) + if (!releaseTsFile(trans, verbose, ignoreUnfinished, removeIdentical, mode)) + return 1; + } + } else { + qWarning("error: lrelease encountered project file functionality that is currently not supported.\n" + "You might want to consider using .ts files as input instead of a project file.\n" + "Try the following syntax:\n" + " lrelease [options] ts-files [-qm qm-file]\n"); + } + } else { + if (outputFile.isEmpty()) { + if (!releaseTsFile(args[i], verbose, ignoreUnfinished, removeIdentical, mode)) + return 1; + } else { + if (!loadTsFile(tor, args[i], verbose)) + return 1; + } + } + } + + if (!outputFile.isEmpty()) + return releaseTranslator(tor, outputFile, verbose, ignoreUnfinished, + removeIdentical, mode) ? 0 : 1; + + return 0; +} diff --git a/tools/linguist/lupdate/cpp.cpp b/tools/linguist/lupdate/cpp.cpp new file mode 100644 index 0000000..31f1361 --- /dev/null +++ b/tools/linguist/lupdate/cpp.cpp @@ -0,0 +1,1816 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lupdate.h" + +#include <translator.h> + +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> +#include <QtCore/QStack> +#include <QtCore/QString> +#include <QtCore/QTextCodec> +#include <QtCore/QTextStream> + +#include <ctype.h> // for isXXX() + +QT_BEGIN_NAMESPACE + +/* qmake ignore Q_OBJECT */ + +static const char MagicComment[] = "TRANSLATOR "; + +static const int yyIdentMaxLen = 128; +static const int yyCommentMaxLen = 65536; +static const int yyStringMaxLen = 65536; + +#define STRINGIFY_INTERNAL(x) #x +#define STRINGIFY(x) STRINGIFY_INTERNAL(x) +#define STRING(s) static QString str##s(QLatin1String(STRINGIFY(s))) + +//#define DIAGNOSE_RETRANSLATABILITY // FIXME: should make a runtime option of this + +uint qHash(const QStringList &qsl) +{ + uint hash = 0; + foreach (const QString &qs, qsl) { + hash ^= qHash(qs) ^ 0xa09df22f; + hash = (hash << 13) | (hash >> 19); + } + return hash; +} + +struct Namespace { + + Namespace() : + isClass(false), + hasTrFunctions(false), needsTrFunctions(false), complained(false) + {} + + QString name; + QMap<QString, Namespace *> children; + QMap<QString, QStringList> aliases; + QSet<QStringList> usings; + + int fileId; + + bool isClass; + + bool hasTrFunctions; + bool needsTrFunctions; + bool complained; // ... that tr functions are missing. +}; + +typedef QList<Namespace *> NamespaceList; + +struct ParseResults { + + ParseResults() + { + static int nextFileId; + rootNamespace.fileId = nextFileId++; + tor = 0; + } + bool detachNamespace(Namespace **that); + Namespace *include(Namespace *that, const Namespace *other); + void unite(const ParseResults *other); + + Namespace rootNamespace; + Translator *tor; + QSet<QString> allIncludes; +}; + +typedef QHash<QString, const ParseResults *> ParseResultHash; + +class CppFiles { + +public: + static const ParseResults *getResults(const QString &cleanFile); + static void setResults(const QString &cleanFile, const ParseResults *results); + static bool isBlacklisted(const QString &cleanFile); + static void setBlacklisted(const QString &cleanFile); + +private: + static ParseResultHash &parsedFiles(); + 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) { results->tor = tor; } + void parse(const QString &initialContext, ConversionData &cd, QSet<QString> &inclusions); + void parseInternal(ConversionData &cd, QSet<QString> &inclusions); + const ParseResults *getResults() const { return results; } + void deleteResults() { delete results; } + +private: + struct SavedState { + QStringList namespaces; + QStack<int> namespaceDepths; + QStringList functionContext; + QString functionContextUnresolved; + QString pendingContext; + }; + + struct IfdefState { + IfdefState() {} + IfdefState(int _braceDepth, int _parenDepth) : + braceDepth(_braceDepth), + parenDepth(_parenDepth), + elseLine(-1) + {} + + SavedState state; + int braceDepth, braceDepth1st; + int parenDepth, parenDepth1st; + int elseLine; + }; + + uint getChar(); + uint getToken(); + bool match(uint t); + bool matchString(QString *s); + bool matchEncoding(bool *utf8); + bool matchInteger(qlonglong *number); + 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, 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); + void modifyNamespace(NamespaceList *namespaces); + NamespaceList resolveNamespaces(const QStringList &segments); + bool qualifyOne(const NamespaceList &namespaces, int nsIdx, const QString &segment, + NamespaceList *resolved); + bool fullyQualify(const NamespaceList &namespaces, const QStringList &segments, + bool isDeclaration, + NamespaceList *resolved, QStringList *unresolved); + void enterNamespace(NamespaceList *namespaces, const QString &name); + void truncateNamespaces(NamespaceList *namespaces, int lenght); + + enum { + Tok_Eof, Tok_class, Tok_friend, Tok_namespace, Tok_using, Tok_return, + Tok_tr = 10, Tok_trUtf8, Tok_translate, Tok_translateUtf8, + Tok_Q_OBJECT = 20, Tok_Q_DECLARE_TR_FUNCTIONS, + Tok_Ident, Tok_Comment, Tok_String, Tok_Arrow, Tok_Colon, Tok_ColonColon, + Tok_Equals, + Tok_LeftBrace = 30, Tok_RightBrace, Tok_LeftParen, Tok_RightParen, Tok_Comma, Tok_Semicolon, + Tok_Integer = 40, + Tok_QuotedInclude = 50, Tok_AngledInclude, + Tok_Other = 99 + }; + + // Tokenizer state + QString yyFileName; + int yyCh; + bool yyAtNewline; + bool yyCodecIsUtf8; + bool yyForceUtf8; + QString yyIdent; + QString yyComment; + QString yyString; + qlonglong yyInteger; + QStack<IfdefState> yyIfdefStack; + int yyBraceDepth; + int yyParenDepth; + int yyLineNo; + int yyCurLineNo; + int yyBraceLineNo; + int yyParenLineNo; + + // the string to read from and current position in the string + QTextCodec *yySourceCodec; + bool yySourceIsUnicode; + QString yyInStr; + int yyInPos; + + // Parser state + uint yyTok; + + NamespaceList namespaces; + QStack<int> namespaceDepths; + NamespaceList functionContext; + QString functionContextUnresolved; + QString prospectiveContext; + QString pendingContext; + ParseResults *results; + bool directInclude; + + SavedState savedState; + int yyMinBraceDepth; + bool inDefine; +}; + +CppParser::CppParser(ParseResults *_results) +{ + if (_results) { + results = _results; + directInclude = true; + } else { + results = new ParseResults; + directInclude = false; + } + yyInPos = 0; + yyBraceDepth = 0; + yyParenDepth = 0; + yyCurLineNo = 1; + yyBraceLineNo = 1; + yyParenLineNo = 1; + yyAtNewline = true; + yyMinBraceDepth = 0; + inDefine = false; +} + +void CppParser::setInput(const QString &in) +{ + yyInStr = in; + yyFileName = QString(); + yySourceCodec = 0; + yySourceIsUnicode = true; + yyForceUtf8 = true; +} + +void CppParser::setInput(QTextStream &ts, const QString &fileName) +{ + yyInStr = ts.readAll(); + yyFileName = fileName; + yySourceCodec = ts.codec(); + yySourceIsUnicode = yySourceCodec->name().startsWith("UTF-"); + 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() +{ + forever { + if (yyInPos >= yyInStr.size()) + return EOF; + uint c = yyInStr[yyInPos++].unicode(); + if (c == '\\' && yyInPos < yyInStr.size() && yyInStr[yyInPos].unicode() == '\n') { + ++yyCurLineNo; + ++yyInPos; + continue; + } + if (c == '\n') { + ++yyCurLineNo; + yyAtNewline = true; + } else if (c != ' ' && c != '\t' && c != '#') { + yyAtNewline = false; + } + return c; + } +} + +uint CppParser::getToken() +{ + restart: + yyIdent.clear(); + yyComment.clear(); + yyString.clear(); + + 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(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; + forever { + yyCh = getChar(); + if (yyCh == EOF || yyCh == '\n') + break; + if (yyCh == tChar) { + yyCh = getChar(); + break; + } + yyString += yyCh; + } + 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 (yyBraceDepth != is.braceDepth1st || yyParenDepth != is.parenDepth1st) + qWarning("%s:%d: Parenthesis/brace mismatch between " + "#if and #else branches; using #if branch\n", + qPrintable(yyFileName), is.elseLine); + } else { + is.braceDepth1st = yyBraceDepth; + is.parenDepth1st = yyParenDepth; + saveState(&is.state); + } + is.elseLine = yyLineNo; + 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 (yyBraceDepth != is.braceDepth1st || yyParenDepth != is.parenDepth1st) + qWarning("%s:%d: Parenthesis/brace mismatch between " + "#if and #else branches; using #if branch\n", + qPrintable(yyFileName), is.elseLine); + 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) { + qWarning("%s:%d: Unterminated C++ comment\n", + qPrintable(yyFileName), yyLineNo); + 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 (isalpha(yyCh) || yyCh == '_') { + do { + yyIdent += yyCh; + yyCh = getChar(); + } while (isalnum(yyCh) || yyCh == '_'); + + //qDebug() << "IDENT: " << yyIdent; + + switch (yyIdent.at(0).unicode()) { + case 'Q': + if (yyIdent == QLatin1String("Q_OBJECT")) + return Tok_Q_OBJECT; + if (yyIdent == QLatin1String("Q_DECLARE_TR_FUNCTIONS")) + return Tok_Q_DECLARE_TR_FUNCTIONS; + if (yyIdent == QLatin1String("QT_TR_NOOP")) + return Tok_tr; + if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP")) + return Tok_translate; + if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3")) + return Tok_translate; + if (yyIdent == QLatin1String("QT_TR_NOOP_UTF8")) + return Tok_trUtf8; + if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP_UTF8")) + return Tok_translateUtf8; + if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3_UTF8")) + return Tok_translateUtf8; + break; + case 'T': + // TR() for when all else fails + if (yyIdent.compare(QLatin1String("TR"), Qt::CaseInsensitive) == 0) { + return Tok_tr; + } + break; + case 'c': + if (yyIdent == QLatin1String("class")) + return Tok_class; + break; + case 'f': + /* + QTranslator::findMessage() has the same parameters as + QApplication::translate(). + */ + if (yyIdent == QLatin1String("findMessage")) + return Tok_translate; + if (yyIdent == QLatin1String("friend")) + return Tok_friend; + break; + case 'n': + if (yyIdent == QLatin1String("namespace")) + return Tok_namespace; + break; + case 'r': + if (yyIdent == QLatin1String("return")) + return Tok_return; + break; + case 's': + if (yyIdent == QLatin1String("struct")) + return Tok_class; + break; + case 't': + if (yyIdent == QLatin1String("tr")) { + return Tok_tr; + } + if (yyIdent == QLatin1String("trUtf8")) { + return Tok_trUtf8; + } + if (yyIdent == QLatin1String("translate")) { + return Tok_translate; + } + break; + case 'u': + if (yyIdent == QLatin1String("using")) + 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 == '/') { + do { + yyCh = getChar(); + if (yyCh == EOF) + break; + yyComment.append(yyCh); + } while (yyCh != '\n'); + } else if (yyCh == '*') { + bool metAster = false; + + forever { + yyCh = getChar(); + if (yyCh == EOF) { + qWarning("%s:%d: Unterminated C++ comment\n", + qPrintable(yyFileName), yyLineNo); + return Tok_Comment; + } + yyComment.append(yyCh); + + if (yyCh == '*') + metAster = true; + else if (metAster && yyCh == '/') + break; + else + metAster = false; + } + yyCh = getChar(); + yyComment.chop(2); + } + return Tok_Comment; + case '"': + yyCh = getChar(); + while (yyCh != EOF && yyCh != '\n' && yyCh != '"') { + if (yyCh == '\\') { + yyCh = getChar(); + if (yyCh == EOF || yyCh == '\n') + break; + if (yyString.size() < yyStringMaxLen) { + yyString.append(QLatin1Char('\\')); + yyString.append(yyCh); + } + } else { + if (yyString.size() < yyStringMaxLen) + yyString.append(yyCh); + } + yyCh = getChar(); + } + + if (yyCh != '"') + qWarning("%s:%d: Unterminated C++ string\n", + qPrintable(yyFileName), yyLineNo); + 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') { + qWarning("%s:%d: Unterminated C++ character\n", + qPrintable(yyFileName), yyLineNo); + 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) + qWarning("%s:%d: Excess closing brace in C++ code" + " (or abuse of the C++ preprocessor)\n", + qPrintable(yyFileName), yyCurLineNo); + // 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) + qWarning("%s:%d: Excess closing parenthesis in C++ code" + " (or abuse of the C++ preprocessor)\n", + qPrintable(yyFileName), yyCurLineNo); + else + yyParenDepth--; + yyCh = getChar(); + return Tok_RightParen; + case ',': + yyCh = getChar(); + return Tok_Comma; + case ';': + yyCh = getChar(); + return Tok_Semicolon; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + QByteArray ba; + ba += yyCh; + yyCh = getChar(); + bool hex = yyCh == 'x'; + if (hex) { + ba += yyCh; + yyCh = getChar(); + } + while (hex ? isxdigit(yyCh) : isdigit(yyCh)) { + ba += yyCh; + yyCh = getChar(); + } + bool ok; + yyInteger = ba.toLongLong(&ok); + if (ok) + return Tok_Integer; + break; + } + 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 = stringListifyNamespace(namespaces); + state->namespaceDepths = namespaceDepths; + state->functionContext = stringListifyNamespace(functionContext); + state->functionContextUnresolved = functionContextUnresolved; + state->pendingContext = pendingContext; +} + +void CppParser::loadState(const SavedState *state) +{ + namespaces = resolveNamespaces(state->namespaces); + namespaceDepths = state->namespaceDepths; + functionContext = resolveNamespaces(state->functionContext); + functionContextUnresolved = state->functionContextUnresolved; + pendingContext = state->pendingContext; +} + +bool ParseResults::detachNamespace(Namespace **that) +{ + if ((*that)->fileId != rootNamespace.fileId) { + Namespace *newThat = new Namespace; + *newThat = **that; + newThat->fileId = rootNamespace.fileId; + *that = newThat; + return true; + } + return false; +} + +Namespace *ParseResults::include(Namespace *that, const Namespace *other) +{ + Namespace *origThat = that; + foreach (Namespace *otherSub, other->children) { + if (Namespace *thisSub = that->children.value(otherSub->name)) { + // Don't make these cause a detach - it's best + // (though not necessary) if they are shared + thisSub->isClass |= otherSub->isClass; + thisSub->hasTrFunctions |= otherSub->hasTrFunctions; + thisSub->needsTrFunctions |= otherSub->needsTrFunctions; + thisSub->complained |= otherSub->complained; + + if (Namespace *newSub = include(thisSub, otherSub)) { + thisSub = newSub; + detachNamespace(&that); + that->children[thisSub->name] = thisSub; + } + } else { + detachNamespace(&that); + that->children[otherSub->name] = otherSub; + } + } + if ((that->aliases != other->aliases && !other->aliases.isEmpty()) + || (that->usings != other->usings && !other->usings.isEmpty())) { + detachNamespace(&that); + that->aliases.unite(other->aliases); + that->usings.unite(other->usings); + } + return (that != origThat) ? that : 0; +} + +void ParseResults::unite(const ParseResults *other) +{ + allIncludes.unite(other->allIncludes); + include(&rootNamespace, &other->rootNamespace); +} + +void CppParser::modifyNamespace(NamespaceList *namespaces) +{ + Namespace *pns = 0; + int i = namespaces->count(); + forever { + --i; + Namespace *ns = namespaces->at(i); + bool detached = results->detachNamespace(&ns); + if (pns) + ns->children[pns->name] = pns; + if (!detached) // Known to be true for root namespace + return; + pns = ns; + namespaces->replace(i, 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)->name; + } + return ret; +} + +QStringList CppParser::stringListifyNamespace(const NamespaceList &namespaces) +{ + QStringList ret; + for (int i = 1; i < namespaces.count(); ++i) + ret << namespaces.at(i)->name; + return ret; +} + +// This function is called only with known-existing namespaces +NamespaceList CppParser::resolveNamespaces(const QStringList &segments) +{ + NamespaceList ret; + Namespace *ns = &results->rootNamespace; + ret << ns; + foreach (const QString &seg, segments) { + ns = ns->children.value(seg); + ret << ns; + } + return ret; +} + +bool CppParser::qualifyOne(const NamespaceList &namespaces, int nsIdx, const QString &segment, + NamespaceList *resolved) +{ + const Namespace *ns = namespaces.at(nsIdx); + QMap<QString, Namespace *>::ConstIterator cnsi = ns->children.constFind(segment); + if (cnsi != ns->children.constEnd()) { + *resolved = namespaces.mid(0, nsIdx + 1); + *resolved << *cnsi; + return true; + } + QMap<QString, QStringList>::ConstIterator nsai = ns->aliases.constFind(segment); + if (nsai != ns->aliases.constEnd()) { + *resolved = resolveNamespaces(*nsai); + return true; + } + foreach (const QStringList &use, ns->usings) { + NamespaceList usedNs = resolveNamespaces(use); + if (qualifyOne(usedNs, usedNs.count() - 1, segment, resolved)) + return true; + } + return false; +} + +bool CppParser::fullyQualify(const NamespaceList &namespaces, const QStringList &segments, + bool isDeclaration, + NamespaceList *resolved, QStringList *unresolved) +{ + int nsIdx; + int initSegIdx; + + if (segments.first().isEmpty()) { + // fully qualified + if (segments.count() == 1) { + resolved->clear(); + *resolved << &results->rootNamespace; + return true; + } + initSegIdx = 1; + nsIdx = 0; + } else { + initSegIdx = 0; + nsIdx = namespaces.count() - 1; + } + + do { + if (qualifyOne(namespaces, nsIdx, segments[initSegIdx], resolved)) { + int segIdx = initSegIdx; + while (++segIdx < segments.count()) { + if (!qualifyOne(*resolved, resolved->count() - 1, segments[segIdx], resolved)) { + if (unresolved) + *unresolved = segments.mid(segIdx); + return false; + } + } + return true; + } + } while (!isDeclaration && --nsIdx >= 0); + resolved->clear(); + *resolved << &results->rootNamespace; + if (unresolved) + *unresolved = segments.mid(initSegIdx); + return false; +} + +void CppParser::enterNamespace(NamespaceList *namespaces, const QString &name) +{ + Namespace *ns = namespaces->last()->children.value(name); + if (!ns) { + ns = new Namespace; + ns->fileId = results->rootNamespace.fileId; + ns->name = name; + modifyNamespace(namespaces); + namespaces->last()->children[name] = ns; + } + *namespaces << ns; +} + +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; +} + +QSet<QString> &CppFiles::blacklistedFiles() +{ + static QSet<QString> blacklisted; + + return blacklisted; +} + +const ParseResults *CppFiles::getResults(const QString &cleanFile) +{ + ParseResultHash::ConstIterator it = parsedFiles().find(cleanFile); + if (it == parsedFiles().constEnd()) + return 0; + return *it; +} + +void CppFiles::setResults(const QString &cleanFile, const ParseResults *results) +{ + parsedFiles().insert(cleanFile, results); +} + +bool CppFiles::isBlacklisted(const QString &cleanFile) +{ + return blacklistedFiles().contains(cleanFile); +} + +void CppFiles::setBlacklisted(const QString &cleanFile) +{ + blacklistedFiles().insert(cleanFile); +} + +void CppParser::processInclude(const QString &file, ConversionData &cd, + QSet<QString> &inclusions) +{ + QString cleanFile = QDir::cleanPath(file); + + if (inclusions.contains(cleanFile)) { + qWarning("%s:%d: circular inclusion of %s\n", + qPrintable(yyFileName), yyLineNo, qPrintable(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)) { + QString fileExt = QFileInfo(cleanFile).suffix(); + if (fileExt.isEmpty() || fileExt.startsWith(QLatin1Char('h'), Qt::CaseInsensitive)) { + + if (results->allIncludes.contains(cleanFile)) + return; + results->allIncludes.insert(cleanFile); + + if (const ParseResults *res = CppFiles::getResults(cleanFile)) { + results->unite(res); + return; + } + + isIndirect = true; + } + } + + QFile f(cleanFile); + if (!f.open(QIODevice::ReadOnly)) { + qWarning("%s:%d: Cannot open %s: %s\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(cleanFile), qPrintable(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); + CppFiles::setResults(cleanFile, parser.getResults()); + results->unite(parser.results); + } else { + CppParser parser(results); + parser.namespaces = namespaces; + parser.functionContext = functionContext; + parser.functionContextUnresolved = functionContextUnresolved; + parser.pendingContext = pendingContext; + parser.setInput(ts, cleanFile); + parser.parseInternal(cd, inclusions); + // Don't wreak havoc if not enough braces were found. + truncateNamespaces(&parser.namespaces, namespaces.count()); + truncateNamespaces(&parser.functionContext, functionContext.count()); + // Copy them back - the pointers might have changed. + namespaces = parser.namespaces; + functionContext = parser.functionContext; + // 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 = (yyTok == Tok_String); + s->clear(); + while (yyTok == Tok_String) { + *s += yyString; + yyTok = getToken(); + } + return matches; +} + +bool CppParser::matchEncoding(bool *utf8) +{ + STRING(QApplication); + STRING(QCoreApplication); + STRING(UnicodeUTF8); + STRING(DefaultCodec); + STRING(CodecForTr); + + if (yyTok != Tok_Ident) + return false; + if (yyIdent == strQApplication || yyIdent == strQCoreApplication) { + yyTok = getToken(); + if (yyTok == Tok_ColonColon) + yyTok = getToken(); + } + if (yyIdent == strUnicodeUTF8) { + *utf8 = true; + yyTok = getToken(); + return true; + } + if (yyIdent == strDefaultCodec || yyIdent == strCodecForTr) { + *utf8 = false; + yyTok = getToken(); + return true; + } + return false; +} + +bool CppParser::matchInteger(qlonglong *number) +{ + bool matches = (yyTok == Tok_Integer); + if (matches) { + yyTok = getToken(); + *number = yyInteger; + } + return matches; +} + +bool CppParser::matchStringOrNull(QString *s) +{ + bool matches = matchString(s); + qlonglong num = 0; + if (!matches) + matches = matchInteger(&num); + return matches && num == 0; +} + +/* + * 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_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"; + const QString in = (!utf8 || yySourceIsUnicode) + ? str : QString::fromUtf8(yySourceCodec->fromUnicode(str).data()); + QString out; + + out.reserve(in.length()); + for (int i = 0; i < in.length();) { + ushort c = in[i++].unicode(); + if (c == '\\') { + if (i >= in.length()) + break; + c = in[i++].unicode(); + + if (c == '\n') + continue; + + if (c == 'x') { + QByteArray hex; + while (i < in.length() && isxdigit((c = in[i].unicode()))) { + 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].unicode()) >= '0' && c < '8') { + i++; + n++; + oct += c; + } + out += oct.toUInt(0, 8); + } else { + const char *p = strchr(tab, c); + out += QChar(QLatin1Char(!p ? c : backTab[p - tab])); + } + } else { + out += c; + } + } + return out; +} + +void CppParser::recordMessage( + int line, const QString &context, const QString &text, const QString &comment, + const QString &extracomment, 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)); + if ((utf8 || yyForceUtf8) && !yyCodecIsUtf8 && msg.needs8Bit()) + msg.setUtf8(true); + results->tor->append(msg); +} + +void CppParser::parse(const QString &initialContext, ConversionData &cd, + QSet<QString> &inclusions) +{ + if (results->tor) + yyCodecIsUtf8 = (results->tor->codecName() == "UTF-8"); + + namespaces << &results->rootNamespace; + 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 prefix; +#ifdef DIAGNOSE_RETRANSLATABILITY + QString functionName; +#endif + int line; + bool utf8; + bool yyTokColonSeen = false; // Start of c'tor's initializer list + + yyCh = getChar(); + yyTok = getToken(); + while (yyTok != Tok_Eof) { + //qDebug() << "TOKEN: " << yyTok; + switch (yyTok) { + case Tok_QuotedInclude: { + text = QDir(QFileInfo(yyFileName).absolutePath()).absoluteFilePath(yyString); + if (QFileInfo(text).isFile()) { + processInclude(text, cd, inclusions); + yyTok = getToken(); + break; + } + } + /* fall through */ + case Tok_AngledInclude: { + QStringList cSources = cd.m_allCSources.values(yyString); + 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(yyString); + if (QFileInfo(text).isFile()) { + processInclude(text, cd, inclusions); + goto incOk; + } + } + incOk: + yyTok = getToken(); + break; + } + case Tok_friend: + yyTok = getToken(); + // Ensure that these don't end up being interpreted as forward declarations + // (they are forwards, but with different namespacing). + 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) { + QStringList 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'. + */ + fct = QStringList(yyIdent); + yyTok = getToken(); + } while (yyTok == Tok_Ident); + while (yyTok == Tok_ColonColon) { + yyTok = getToken(); + if (yyTok != Tok_Ident) + break; // Oops ... + fct += yyIdent; + yyTok = getToken(); + } + if (fct.count() > 1) { + // Forward-declared class definitions can be namespaced + NamespaceList nsl; + if (!fullyQualify(namespaces, fct, true, &nsl, 0)) { + qWarning("%s:%d: Ignoring definition of undeclared qualified class\n", + qPrintable(yyFileName), yyLineNo); + break; + } + namespaceDepths.push(namespaces.count()); + namespaces = nsl; + } else { + namespaceDepths.push(namespaces.count()); + enterNamespace(&namespaces, fct.first()); + } + namespaces.last()->isClass = true; + + while (yyTok == Tok_Comment) + yyTok = getToken(); + if (yyTok == Tok_Colon) { + // Skip any token until '{' since lupdate might do things wrong if it finds + // a '::' token here. + do { + yyTok = getToken(); + } while (yyTok != Tok_LeftBrace && yyTok != Tok_Eof); + } else { + if (yyTok != Tok_LeftBrace) { + // Obviously a forward decl + truncateNamespaces(&namespaces, namespaceDepths.pop()); + break; + } + } + + functionContext = namespaces; + functionContextUnresolved.clear(); // Pointless + prospectiveContext.clear(); + pendingContext.clear(); + } + break; + case Tok_namespace: + yyTokColonSeen = false; + yyTok = getToken(); + if (yyTok == Tok_Ident) { + QString ns = yyIdent; + yyTok = getToken(); + if (yyTok == Tok_LeftBrace) { + namespaceDepths.push(namespaces.count()); + enterNamespace(&namespaces, ns); + yyTok = getToken(); + } else if (yyTok == Tok_Equals) { + // e.g. namespace Is = OuterSpace::InnerSpace; + QStringList fullName; + yyTok = getToken(); + if (yyTok == Tok_ColonColon) + fullName.append(QString()); + while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { + if (yyTok == Tok_Ident) + fullName.append(yyIdent); + yyTok = getToken(); + } + if (fullName.isEmpty()) + break; + NamespaceList nsl; + if (fullyQualify(namespaces, fullName, false, &nsl, 0)) { + modifyNamespace(&namespaces); + namespaces.last()->aliases.insert(ns, stringListifyNamespace(nsl)); + } + } + } else if (yyTok == Tok_LeftBrace) { + // Anonymous namespace + namespaceDepths.push(namespaces.count()); + yyTok = getToken(); + } + break; + case Tok_using: + yyTok = getToken(); + if (yyTok == Tok_namespace) { + QStringList fullName; + yyTok = getToken(); + if (yyTok == Tok_ColonColon) + fullName.append(QString()); + while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { + if (yyTok == Tok_Ident) + fullName.append(yyIdent); + yyTok = getToken(); + } + NamespaceList nsl; + QStringList unresolved; + if (fullyQualify(namespaces, fullName, false, &nsl, &unresolved)) { + modifyNamespace(&namespaces); + namespaces.last()->usings.insert(stringListifyNamespace(nsl)); + } + } else { + QStringList fullName; + if (yyTok == Tok_ColonColon) + fullName.append(QString()); + while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { + if (yyTok == Tok_Ident) + fullName.append(yyIdent); + yyTok = getToken(); + } + if (fullName.isEmpty()) + break; + NamespaceList nsl; + if (fullyQualify(namespaces, fullName, false, &nsl, 0)) { + modifyNamespace(&namespaces); + namespaces.last()->aliases.insert(nsl.last()->name, stringListifyNamespace(nsl)); + } + } + break; + case Tok_tr: + case Tok_trUtf8: + if (!results->tor) + goto case_default; + 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()) { + QStringList unresolved; + if (!fullyQualify(namespaces, pendingContext.split(strColons), true, + &functionContext, &unresolved)) { + functionContextUnresolved = unresolved.join(strColons); + qWarning("%s:%d: Qualifying with unknown namespace/class %s::%s\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(stringifyNamespace(functionContext)), + qPrintable(unresolved.first())); + } + pendingContext.clear(); + } + if (prefix.isEmpty()) { + if (functionContextUnresolved.isEmpty()) { + int idx = functionContext.length(); + if (idx < 2) { + qWarning("%s:%d: tr() cannot be called without context\n", + qPrintable(yyFileName), yyLineNo); + break; + } + while (!functionContext.at(idx - 1)->hasTrFunctions) { + if (idx == 1 || !functionContext.at(idx - 2)->isClass) { + idx = functionContext.length(); + if (!functionContext.last()->complained) { + qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(stringifyNamespace(functionContext))); + functionContext.last()->complained = true; + } + break; + } + --idx; + } + context.clear(); + for (int i = 1;;) { + context += functionContext.at(i)->name; + if (++i == idx) + break; + context += strColons; + } + } 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) { + qWarning("%s::%d: It is not recommended to call tr() from within a constructor '%s::%s' ", + qPrintable(yyFileName), yyLineNo, + className.constData(), functionName.constData()); + } +#endif + prefix.chop(2); + NamespaceList nsl; + QStringList unresolved; + if (fullyQualify(functionContext, prefix.split(strColons), false, &nsl, &unresolved)) { + if (!nsl.last()->hasTrFunctions && !nsl.last()->complained) { + qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(stringifyNamespace(nsl))); + nsl.last()->complained = true; + } + context = stringifyNamespace(nsl); + } else { + context = (stringListifyNamespace(nsl) + unresolved).join(strColons); + } + prefix.clear(); + } + + recordMessage(line, context, text, comment, extracomment, utf8, plural); + } + extracomment.clear(); + break; + case Tok_translateUtf8: + case Tok_translate: + if (!results->tor) + goto case_default; + 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, utf8, plural); + } + extracomment.clear(); + break; + case Tok_Q_DECLARE_TR_FUNCTIONS: + case Tok_Q_OBJECT: + namespaces.last()->hasTrFunctions = true; + yyTok = getToken(); + break; + case Tok_Ident: + prefix += yyIdent; + yyTok = getToken(); + if (yyTok != Tok_ColonColon) { + prefix.clear(); + if (yyTok == Tok_Ident && !yyParenDepth) + prospectiveContext.clear(); + } + break; + case Tok_Comment: + if (!results->tor) + goto case_default; + if (yyComment.startsWith(QLatin1Char(':'))) { + yyComment.remove(0, 1); + extracomment.append(yyComment); + } else { + comment = yyComment.simplified(); + if (comment.startsWith(QLatin1String(MagicComment))) { + comment.remove(0, sizeof(MagicComment) - 1); + int k = comment.indexOf(QLatin1Char(' ')); + if (k == -1) { + context = comment; + } else { + context = comment.left(k); + comment.remove(0, k + 1); + recordMessage(yyLineNo, context, QString(), comment, extracomment, false, false); + } + } + } + yyTok = getToken(); + break; + case Tok_Arrow: + yyTok = getToken(); + if (yyTok == Tok_tr || yyTok == Tok_trUtf8) + qWarning("%s:%d: Cannot invoke tr() like this\n", + qPrintable(yyFileName), yyLineNo); + 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 = yyIdent; +#endif + break; + case Tok_RightBrace: + if (yyBraceDepth + 1 == namespaceDepths.count()) { + // class or namespace + Namespace *ns = namespaces.last(); + if (ns->needsTrFunctions && !ns->hasTrFunctions && !ns->complained) { + qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(stringifyNamespace(namespaces))); + ns->complained = true; + } + 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(); + extracomment.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(); + case_default: + yyTok = getToken(); + break; + } + } + + if (yyBraceDepth != 0) + qWarning("%s:%d: Unbalanced opening brace in C++ code" + " (or abuse of the C++ preprocessor)\n", + qPrintable(yyFileName), yyBraceLineNo); + else if (yyParenDepth != 0) + qWarning("%s:%d: Unbalanced opening parenthesis in C++ code" + " (or abuse of the C++ preprocessor)\n", + qPrintable(yyFileName), yyParenLineNo); +} + +/* + 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(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + continue; + } + + CppParser parser; + QTextStream ts(&file); + ts.setCodec(codec); + ts.setAutoDetectUnicode(true); + if (ts.codec()->name() == "UTF-16") + translator.setCodecName("System"); + parser.setInput(ts, filename); + parser.setTranslator(new Translator); + QSet<QString> inclusions; + parser.parse(cd.m_defaultContext, cd, inclusions); + CppFiles::setResults(filename, parser.getResults()); + } + + foreach (const QString filename, filenames) + if (!CppFiles::isBlacklisted(filename)) + if (Translator *tor = CppFiles::getResults(filename)->tor) + foreach (const TranslatorMessage &msg, tor->messages()) + translator.extend(msg); +} + +QT_END_NAMESPACE diff --git a/tools/linguist/lupdate/java.cpp b/tools/linguist/lupdate/java.cpp new file mode 100644 index 0000000..c8dbe5b --- /dev/null +++ b/tools/linguist/lupdate/java.cpp @@ -0,0 +1,646 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 <ctype.h> + +QT_BEGIN_NAMESPACE + +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 }; + +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; + +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; + } + } + 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 ) { + qFatal( "%s: Unterminated Java comment starting at" + " line %d\n", + qPrintable(yyFileName), yyLineNo ); + + 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) { + qFatal( "%s:%d: Invalid Unicode", + qPrintable(yyFileName), yyLineNo ); + } + 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('"') ) + qFatal( "%s:%d: Unterminated string", + qPrintable(yyFileName), yyLineNo ); + + 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 { + qWarning( "%s:%d: String used in translation can only contain strings" + " concatenated with other strings, not expressions or numbers.", + qPrintable(yyFileName), yyLineNo ); + return false; + } + yyTok = getToken(); + } + return true; +} + +static bool matchInteger( qlonglong *number) +{ + bool matches = (yyTok == Tok_Integer); + if (matches) { + yyTok = getToken(); + *number = yyInteger; + } + return matches; +} + +static bool matchStringOrNull(QString &s) +{ + bool matches = matchString(s); + qlonglong num = 0; + if (!matches) matches = matchInteger(&num); + return matches && num == 0; +} + +/* + * 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 { + qFatal( "%s:%d: Class must be followed by a classname", + qPrintable(yyFileName), yyLineNo ); + } + 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() ) { + qFatal( "%s:%d: Unbalanced right brace in Java code\n", + qPrintable(yyFileName), yyLineNo ); + } + 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: + qFatal( "%s:%d: Package keyword should be followed by com.package.name;", + qPrintable(yyFileName), yyLineNo ); + break; + } + yyTok = getToken(); + } + break; + + default: + yyTok = getToken(); + } + } + + if ( !yyScope.isEmpty() ) + qFatal( "%s:%d: Unbalanced braces in Java code\n", + qPrintable(yyFileName), yyScope.top()->line ); + else if ( yyParenDepth != 0 ) + qFatal( "%s:%d: Unbalanced parentheses in Java code\n", + qPrintable(yyFileName), yyParenLineNo ); +} + + +bool loadJava(Translator &translator, const QString &filename, ConversionData &cd) +{ + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("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; + yyCh = getChar(); + + 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/tools/linguist/lupdate/lupdate.1 b/tools/linguist/lupdate/lupdate.1 new file mode 100644 index 0000000..e953273 --- /dev/null +++ b/tools/linguist/lupdate/lupdate.1 @@ -0,0 +1,132 @@ +.TH lupdate 1 "18 October 2001" "Nokia Corporation and/or its subsidiary(-ies)" \" -*- nroff -*- +.\" +.\" Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +.\" Contact: Qt Software Information (qt-info@nokia.com) +.\" +.\" This file may be distributed and/or modified under the terms of the +.\" GNU General Public License version 2 as published by the Free Software +.\" Foundation and appearing in the file LICENSE.GPL included in the +.\" packaging of this file. +.\" +.\" This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +.\" WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +.\" +.\" See http://qtsoftware.com/gpl/ for GPL licensing information. +.\" +.\" Contact qt-info@nokia.com if any conditions of this licensing are +.\" not clear to you. +.\" +.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" +Don't 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://doc.trolltech.com/i18n.html diff --git a/tools/linguist/lupdate/lupdate.exe.manifest b/tools/linguist/lupdate/lupdate.exe.manifest new file mode 100644 index 0000000..e0b2f73 --- /dev/null +++ b/tools/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/tools/linguist/lupdate/lupdate.h b/tools/linguist/lupdate/lupdate.h new file mode 100644 index 0000000..2f98643 --- /dev/null +++ b/tools/linguist/lupdate/lupdate.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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); + +QT_END_NAMESPACE + +#endif diff --git a/tools/linguist/lupdate/lupdate.pro b/tools/linguist/lupdate/lupdate.pro new file mode 100644 index 0000000..ccc2d47 --- /dev/null +++ b/tools/linguist/lupdate/lupdate.pro @@ -0,0 +1,45 @@ +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) + +SOURCES += \ + main.cpp \ + merge.cpp \ + ../shared/simtexth.cpp \ + \ + cpp.cpp \ + java.cpp \ + qscript.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/tools/linguist/lupdate/main.cpp b/tools/linguist/lupdate/main.cpp new file mode 100644 index 0000000..7d79024 --- /dev/null +++ b/tools/linguist/lupdate/main.cpp @@ -0,0 +1,542 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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> + +static QString m_defaultExtensions; + +static void printOut(const QString & out) +{ + qWarning("%s", qPrintable(out)); +} + +static void recursiveFileInfoList(const QDir &dir, + const QStringList &nameFilters, QDir::Filters filter, bool recursive, + QFileInfoList *fileinfolist) +{ + if (recursive) + filter |= QDir::AllDirs; + QFileInfoList entries = dir.entryInfoList(nameFilters, filter); + + QFileInfoList::iterator it; + for (it = entries.begin(); it != entries.end(); ++it) { + QString fname = it->fileName(); + if (fname != QLatin1String(".") && fname != QLatin1String("..")) { + if (it->isDir()) + recursiveFileInfoList(QDir(it->absoluteFilePath()), nameFilters, filter, recursive, fileinfolist); + else + fileinfolist->append(*it); + } + } +} + +static void printUsage() +{ + printOut(QObject::tr( + "Usage:\n" + " lupdate [options] [project-file]\n" + " lupdate [options] [source-file|path]... -ts ts-files\n\n" + "lupdate is part of Qt's Linguist tool chain. It can be used as a\n" + "stand-alone tool to create XML based translations files in the .ts\n" + "format from translatable messages in C++ and Java source code.\n\n" + "lupdate can also merge such messages 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\n" + " file syntax but different file suffix\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" + " -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" + " -version\n" + " Display the version of lupdate and exit.\n" + ).arg(m_defaultExtensions)); +} + +static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFileNames, + const QByteArray &codecForTr, 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"))) { + printOut(cd.error()); + *fail = true; + continue; + } + cd.clearErrors(); + if (!codecForTr.isEmpty() && codecForTr != tor.codecName()) + qWarning("lupdate warning: Codec for tr() '%s' disagrees with " + "existing file's codec '%s'. Expect trouble.", + codecForTr.constData(), tor.codecName().constData()); + if (!targetLanguage.isEmpty() && targetLanguage != tor.languageCode()) + qWarning("lupdate warning: Specified target language '%s' disagrees with " + "existing file's language '%s'. Ignoring.", + qPrintable(targetLanguage), qPrintable(tor.languageCode())); + if (!sourceLanguage.isEmpty() && sourceLanguage != tor.sourceLanguageCode()) + qWarning("lupdate warning: Specified source language '%s' disagrees with " + "existing file's language '%s'. Ignoring.", + qPrintable(sourceLanguage), qPrintable(tor.sourceLanguageCode())); + } else { + if (!codecForTr.isEmpty()) + tor.setCodecName(codecForTr); + if (!targetLanguage.isEmpty()) + tor.setLanguageCode(targetLanguage); + 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(QObject::tr("Updating '%1'...\n").arg(fn)); + + if (tor.locationsType() == Translator::NoLocations) // Could be set from file + options |= NoLocations; + Translator out = merge(tor, fetchedTor, options, err); + if (!codecForTr.isEmpty()) + out.setCodecName(codecForTr); + + if ((options & Verbose) && !err.isEmpty()) { + printOut(err); + err.clear(); + } + if (options & PluralOnly) { + if (options & Verbose) + printOut(QObject::tr("Stripping non plural forms in '%1'...\n").arg(fn)); + out.stripNonPluralForms(); + } + if (options & NoObsolete) + out.stripObsoleteMessages(); + out.stripEmptyContexts(); + + if (!out.save(fileName, cd, QLatin1String("auto"))) { + printOut(cd.error()); + *fail = true; + } + } +} + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + m_defaultExtensions = QLatin1String("ui,c,c++,cc,cpp,cxx,ch,h,h++,hh,hpp,hxx"); + + QStringList args = app.arguments(); + QString defaultContext; // This was QLatin1String("@default") before. + Translator fetchedTor; + QByteArray codecForTr; + QByteArray codecForSource; + QStringList tsFileNames; + QStringList proFiles; + QMultiHash<QString, QString> allCSources; + QSet<QString> projectRoots; + QStringList sourceFiles; + QStringList includePath; + QString targetLanguage; + QString sourceLanguage; + + UpdateOptions options = + Verbose | // verbose is on by default starting with Qt 4.2 + HeuristicSameText | HeuristicSimilarText | HeuristicNumber; + int numFiles = 0; + bool standardSyntax = true; + bool metTsFlag = false; + bool recursiveScan = true; + + QString extensions = m_defaultExtensions; + QStringList extensionsNameFilters; + + for (int i = 1; i < argc; ++i) { + if (args.at(i) == QLatin1String("-ts")) + standardSyntax = false; + } + + 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) { + qWarning("The option -target-language requires a parameter."); + return 1; + } + targetLanguage = args[i]; + continue; + } else if (arg == QLatin1String("-source-language")) { + ++i; + if (i == argc) { + qWarning("The option -source-language requires a parameter."); + return 1; + } + sourceLanguage = args[i]; + continue; + } else if (arg == QLatin1String("-disable-heuristic")) { + ++i; + if (i == argc) { + qWarning("The option -disable-heuristic requires a parameter."); + 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 { + qWarning("Invalid heuristic name passed to -disable-heuristic."); + return 1; + } + continue; + } else if (arg == QLatin1String("-locations")) { + ++i; + if (i == argc) { + qWarning("The option -locations requires a parameter."); + 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 { + qWarning("Invalid parameter passed to -locations."); + 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("-ts")) { + metTsFlag = true; + continue; + } else if (arg == QLatin1String("-extensions")) { + ++i; + if (i == argc) { + qWarning("The -extensions option should be followed by an extension list."); + return 1; + } + extensions = args[i]; + continue; + } else if (arg == QLatin1String("-pro")) { + ++i; + if (i == argc) { + qWarning("The -pro option should be followed by a filename of .pro file."); + return 1; + } + proFiles += args[i]; + numFiles++; + continue; + } else if (arg.startsWith(QLatin1String("-I"))) { + if (arg.length() == 2) { + ++i; + if (i == argc) { + qWarning("The -I option should be followed by a path."); + return 1; + } + includePath += args[i]; + } else { + includePath += args[i].mid(2); + } + continue; + } else if (arg.startsWith(QLatin1String("-")) && arg != QLatin1String("-")) { + qWarning("Unrecognized option '%s'", qPrintable(arg)); + return 1; + } + + numFiles++; + + QString fullText; + + codecForTr.clear(); + codecForSource.clear(); + + if (metTsFlag) { + bool found = false; + foreach (const Translator::FileFormat &fmt, Translator::registeredFileFormats()) { + if (arg.endsWith(QLatin1Char('.') + fmt.extension, Qt::CaseInsensitive)) { + QFileInfo fi(arg); + if (!fi.exists() || fi.isWritable()) { + tsFileNames.append(QFileInfo(arg).absoluteFilePath()); + } else { + qWarning("lupdate warning: For some reason, '%s' is not writable.\n", + qPrintable(arg)); + } + found = true; + break; + } + } + if (!found) { + qWarning("lupdate error: File '%s' has no recognized extension\n", + qPrintable(arg)); + return 1; + } + } else if (arg.endsWith(QLatin1String(".pro"), Qt::CaseInsensitive) + || arg.endsWith(QLatin1String(".pri"), Qt::CaseInsensitive)) { + proFiles << arg; + } else { + QFileInfo fi(arg); + if (!fi.exists()) { + qWarning("lupdate error: File '%s' does not exists\n", qPrintable(arg)); + return 1; + } else if (fi.isDir()) { + if (options & Verbose) + printOut(QObject::tr("Scanning directory '%1'...").arg(arg)); + 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); + ext.insert(0, QLatin1String("*.")); + extensionsNameFilters << ext; + } + } + QDir::Filters filters = QDir::Files | QDir::NoSymLinks; + QFileInfoList fileinfolist; + recursiveFileInfoList(dir, extensionsNameFilters, filters, + recursiveScan, &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(".ui")) + && !fn.endsWith(QLatin1String(".js")) + && !fn.endsWith(QLatin1String(".qs"))) { + 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());; + } + } + } // for args + + foreach (const QString &proFile, proFiles) + projectRoots.insert(QDir::cleanPath(QFileInfo(proFile).absolutePath()) + QLatin1Char('/')); + + bool firstPass = true; + bool fail = false; + while (firstPass || !proFiles.isEmpty()) { + ConversionData cd; + cd.m_defaultContext = defaultContext; + cd.m_noUiLines = options & NoUiLines; + cd.m_projectRoots = projectRoots; + cd.m_includePath = includePath; + cd.m_allCSources = allCSources; + + QStringList tsFiles = tsFileNames; + if (proFiles.count() > 0) { + QFileInfo pfi(proFiles.takeFirst()); + QHash<QByteArray, QStringList> variables; + + ProFileEvaluator visitor; + visitor.setVerbose(options & Verbose); + + ProFile pro(pfi.absoluteFilePath()); + if (!visitor.queryProFile(&pro)) + return 2; + if (!visitor.accept(&pro)) + return 2; + + if (visitor.templateType() == ProFileEvaluator::TT_Subdirs) { + 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()) + proFiles << (subPro + QLatin1Char('/') + + subInfo.fileName() + QLatin1String(".pro")); + else + proFiles << subPro; + } + continue; + } + + cd.m_includePath += visitor.values(QLatin1String("INCLUDEPATH")); + + evaluateProFile(visitor, &variables); + + sourceFiles = variables.value("SOURCES"); + + QStringList tmp = variables.value("CODECFORTR"); + if (!tmp.isEmpty() && !tmp.first().isEmpty()) { + codecForTr = tmp.first().toLatin1(); + fetchedTor.setCodecName(codecForTr); + } + tmp = variables.value("CODECFORSRC"); + if (!tmp.isEmpty() && !tmp.first().isEmpty()) { + codecForSource = tmp.first().toLatin1(); + if (!QTextCodec::codecForName(codecForSource)) + qWarning("lupdate warning: Codec for source '%s' is invalid. Falling back to codec for tr().", + codecForSource.constData()); + else + cd.m_codecForSource = codecForSource; + } + + tsFiles += variables.value("TRANSLATIONS"); + } + + QStringList sourceFilesCpp; + for (QStringList::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)) + loadUI(fetchedTor, *it, cd); + else if (it->endsWith(QLatin1String(".js"), Qt::CaseInsensitive) + || it->endsWith(QLatin1String(".qs"), Qt::CaseInsensitive)) + loadQScript(fetchedTor, *it, cd); + else + sourceFilesCpp << *it; + } + loadCPP(fetchedTor, sourceFilesCpp, cd); + if (!cd.error().isEmpty()) + printOut(cd.error()); + + tsFiles.sort(); + tsFiles.removeDuplicates(); + + if (!tsFiles.isEmpty()) + updateTsFiles(fetchedTor, tsFiles, codecForTr, sourceLanguage, targetLanguage, options, &fail); + + firstPass = false; + } + + if (numFiles == 0) { + printUsage(); + return 1; + } + + return fail ? 1 : 0; +} diff --git a/tools/linguist/lupdate/merge.cpp b/tools/linguist/lupdate/merge.cpp new file mode 100644 index 0000000..46140de --- /dev/null +++ b/tools/linguist/lupdate/merge.cpp @@ -0,0 +1,505 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lupdate.h" + +#include "simtexth.h" +#include "translator.h" + +#include <QtCore/QDebug> +#include <QtCore/QMap> +#include <QtCore/QStringList> +#include <QtCore/QTextCodec> +#include <QtCore/QVector> + +typedef QList<TranslatorMessage> TML; +typedef QMap<QString, TranslatorMessage> TMM; + + +QT_BEGIN_NAMESPACE + +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 += QString(QLatin1String(" {")) + newNumbers[k] + QString(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 += QString(QLatin1String(" {")) + newNumbers[k] + QString(QLatin1String(" or ")) + + newNumbers[ell] + QString(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) +{ + TMM translated, untranslated; + TMM::Iterator t, u; + TML all = tor.messages(); + TML::Iterator it; + int inserted = 0; + + for (it = all.begin(); it != all.end(); ++it) { + bool hasTranslation = it->isTranslated(); + if (it->type() == TranslatorMessage::Unfinished) { + if (!hasTranslation) + untranslated.insert(it->context() + QLatin1Char('\n') + + it->sourceText() + QLatin1Char('\n') + + it->comment(), *it); + } else if (hasTranslation && it->translations().count() == 1) { + translated.insert(zeroKey(it->sourceText()), *it); + } + } + + for (u = untranslated.begin(); u != untranslated.end(); ++u) { + t = translated.find(zeroKey((*u).sourceText())); + if (t != translated.end() && !t.key().isEmpty() + && t->sourceText() != u->sourceText()) { + TranslatorMessage m = *u; + m.setTranslation(translationAttempt(t->translation(), t->sourceText(), + u->sourceText())); + tor.replace(m); + 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) +{ + TMM translated; + TMM avoid; + TMM::Iterator t; + TML untranslated; + TML::Iterator u; + TML all = tor.messages(); + TML::Iterator it; + int inserted = 0; + + for (it = all.begin(); it != all.end(); ++it) { + if (!it->isTranslated()) { + if (it->type() == TranslatorMessage::Unfinished) + untranslated.append(*it); + } else { + QString key = it->sourceText(); + t = translated.find(key); + if (t != translated.end()) { + /* + The same source text is translated at least two + different ways. Do nothing then. + */ + if (t->translations() != it->translations()) { + translated.remove(key); + avoid.insert(key, *it); + } + } else if (!avoid.contains(key)) { + translated.insert(key, *it); + } + } + } + + for (u = untranslated.begin(); u != untranslated.end(); ++u) { + QString key = u->sourceText(); + t = translated.find(key); + if (t != translated.end()) { + TranslatorMessage m = *u; + m.setTranslations(t->translations()); + tor.replace(m); + ++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()) { + // context/file comment + TranslatorMessage mv = virginTor.find(m.context()); + if (!mv.isNull()) + m.setComment(mv.comment()); + } else { + TranslatorMessage mv = virginTor.find(m.context(), m.sourceText(), m.comment()); + if (mv.isNull()) { + if (!(options & HeuristicSimilarText)) { + 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 + newType = TranslatorMessage::Obsolete; + if (m.type() != TranslatorMessage::Obsolete) + obsoleted++; + m.clearReferences(); + } 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++; + + 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")); + } + m.setReferences(mv.allReferences()); // Update secondary references + m.setPlural(mv.isPlural()); + m.setUtf8(mv.isUtf8()); + m.setExtraComment(mv.extraComment()); + } else { + // The virgin and vernacular sourceTexts are so + // different that we could not find it. + newType = TranslatorMessage::Obsolete; + if (m.type() != TranslatorMessage::Obsolete) + obsoleted++; + m.clearReferences(); + } + } + } + } 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. + m.setReferences(mv.allReferences()); + m.setPlural(mv.isPlural()); + m.setUtf8(mv.isUtf8()); + m.setExtraComment(mv.extraComment()); + } + } + + 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()) { + if (tor.contains(mv.context())) + continue; + } else { + if (tor.contains(mv.context(), mv.sourceText(), mv.comment())) + 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()) + ++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 += QObject::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 += QObject::tr(" Removed %n obsolete entries\n", 0, obsoleted); + } else { + err += QObject::tr(" Kept %n obsolete entries\n", 0, obsoleted); + } + } + + if (sameNumberHeuristicCount) + err += QObject::tr(" Number heuristic provided %n translation(s)\n", + 0, sameNumberHeuristicCount); + if (sameTextHeuristicCount) + err += QObject::tr(" Same-text heuristic provided %n translation(s)\n", + 0, sameTextHeuristicCount); + if (similarTextHeuristicCount) + err += QObject::tr(" Similar-text heuristic provided %n translation(s)\n", + 0, similarTextHeuristicCount); + } + return outTor; +} + +QT_END_NAMESPACE diff --git a/tools/linguist/lupdate/qscript.cpp b/tools/linguist/lupdate/qscript.cpp new file mode 100644 index 0000000..64adde6 --- /dev/null +++ b/tools/linguist/lupdate/qscript.cpp @@ -0,0 +1,2391 @@ +// This file was generated by qlalr - DO NOT EDIT! +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +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}; + + +#define Q_SCRIPT_REGEXPLITERAL_RULE1 7 + +#define Q_SCRIPT_REGEXPLITERAL_RULE2 8 + +#include <translator.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qnumeric.h> +#include <QtCore/qstring.h> +#include <QtCore/qtextcodec.h> +#include <QtCore/qvariant.h> + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +QT_BEGIN_NAMESPACE + +static void recordMessage( + Translator *tor, const QString &context, const QString &text, const QString &comment, + const QString &extracomment, bool plural, const QString &fileName, int lineNo) +{ + TranslatorMessage msg( + context, text, comment, QString(), + fileName, lineNo, QStringList(), + TranslatorMessage::Unfinished, plural); + msg.setExtraComment(extracomment.simplified()); + tor->replace(msg); +} + + +namespace QScript +{ + +class Lexer +{ +public: + Lexer(); + ~Lexer(); + + void setCode(const QString &c, int lineno); + int lex(); + + 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: + 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(); + + 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; +}; + +} // 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() + : + 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) +{ + // 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, int lineno) +{ + errmsg = QString(); + 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); + state = InSingleLineComment; + } else if (current == '/' && next1 == '*') { + recordStartPos(); + shift(1); + 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 = QLatin1String("Illegal character"); + } + } + break; + case InString: + if (current == stringType) { + shift(1); + setDone(String); + } else if (current == 0 || isLineTerminator()) { + setDone(Bad); + err = UnclosedStringLiteral; + errmsg = QLatin1String("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 = QLatin1String("Illegal escape squence"); + } + } 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 = QLatin1String("Illegal unicode escape sequence"); + } + break; + case InSingleLineComment: + if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + yycolumn = 0; + terminator = true; + bol = true; + if (restrKeyword) { + token = QScriptGrammar::T_SEMICOLON; + setDone(Other); + } else + state = Start; + } else if (current == 0) { + setDone(Eof); + } + break; + case InMultiLineComment: + if (current == 0) { + setDone(Bad); + err = UnclosedComment; + errmsg = QLatin1String("Unclosed comment at end of file"); + } else if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + } else if (current == '*' && next1 == '/') { + state = Start; + shift(1); + } + 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 = QLatin1String("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 = QLatin1String("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 = QLatin1String("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; + } +} + + +class Translator; + +class QScriptParser: protected QScriptGrammar +{ +public: + QVariant val; + + struct Location { + int startLine; + int startColumn; + int endLine; + int endColumn; + }; + +public: + QScriptParser(); + ~QScriptParser(); + + bool parse(QScript::Lexer *lexer, + const QString &fileName, + Translator *translator); + + 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]; } + +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; +}; + +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) +{ +} + +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; +} + +bool QScriptParser::parse(QScript::Lexer *lexer, + const QString &fileName, + Translator *translator) +{ + 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"))) { + QVariantList args = sym(2).toList(); + if (args.size() < 2) { + qWarning("%s:%d: %s() requires at least two arguments", + qPrintable(fileName), identLineNo, qPrintable(name)); + } else { + if ((args.at(0).type() != QVariant::String) + || (args.at(1).type() != QVariant::String)) { + qWarning("%s:%d: %s(): both arguments must be literal strings", + qPrintable(fileName), identLineNo, qPrintable(name)); + } else { + QString context = args.at(0).toString(); + QString text = args.at(1).toString(); + QString comment = args.value(2).toString(); + QString extracomment; + bool plural = (args.size() > 4); + recordMessage(translator, context, text, comment, extracomment, + plural, fileName, identLineNo); + } + } + } else if ((name == QLatin1String("qsTr")) || (name == QLatin1String("QT_TR_NOOP"))) { + QVariantList args = sym(2).toList(); + if (args.size() < 1) { + qWarning("%s:%d: %s() requires at least one argument", + qPrintable(fileName), identLineNo, qPrintable(name)); + } else { + if (args.at(0).type() != QVariant::String) { + qWarning("%s:%d: %s(): text to translate must be a literal string", + qPrintable(fileName), identLineNo, qPrintable(name)); + } else { + QString context = QFileInfo(fileName).baseName(); + QString text = args.at(0).toString(); + QString comment = args.value(1).toString(); + QString extracomment; + bool plural = (args.size() > 2); + recordMessage(translator, context, text, comment, extracomment, + plural, fileName, identLineNo); + } + } + } +} 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; + + } // 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 "); + 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; +} + + +bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd) +{ + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("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(); + QScript::Lexer lexer; + lexer.setCode(code, /*lineNumber=*/1); + QScriptParser parser; + if (!parser.parse(&lexer, filename, &translator)) { + qWarning("%s:%d: %s", qPrintable(filename), parser.errorLineNumber(), + qPrintable(parser.errorMessage())); + 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/tools/linguist/lupdate/qscript.g b/tools/linguist/lupdate/qscript.g new file mode 100644 index 0000000..563974a --- /dev/null +++ b/tools/linguist/lupdate/qscript.g @@ -0,0 +1,2026 @@ +---------------------------------------------------------------------------- +-- +-- Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +-- Contact: Qt Software Information (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 either Technology Preview License Agreement or the +-- Beta Release License Agreement. +-- +-- 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.0, included in the file LGPL_EXCEPTION.txt in this +-- package. +-- +-- GNU General Public License Usage +-- Alternatively, this file may be used under the terms of the GNU +-- General Public License version 3.0 as published by the Free Software +-- Foundation and appearing in the file LICENSE.GPL included in the +-- packaging of this file. Please review the following information to +-- ensure the GNU General Public License version 3.0 requirements will be +-- met: http://www.gnu.org/copyleft/gpl.html. +-- +-- If you are unsure which license is appropriate for your use, please +-- contact the sales department at qt-sales@nokia.com. +-- $QT_END_LICENSE$ +-- +-- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +-- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +-- +---------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- 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/qdebug.h> +#include <QtCore/qnumeric.h> +#include <QtCore/qstring.h> +#include <QtCore/qtextcodec.h> +#include <QtCore/qvariant.h> + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +QT_BEGIN_NAMESPACE + +static void recordMessage( + Translator *tor, const QString &context, const QString &text, const QString &comment, + const QString &extracomment, bool plural, const QString &fileName, int lineNo) +{ + TranslatorMessage msg( + context, text, comment, QString(), + fileName, lineNo, QStringList(), + TranslatorMessage::Unfinished, plural); + msg.setExtraComment(extracomment.simplified()); + tor->replace(msg); +} + + +namespace QScript +{ + +class Lexer +{ +public: + Lexer(); + ~Lexer(); + + void setCode(const QString &c, int lineno); + int lex(); + + 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: + 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(); + + 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; +}; + +} // 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() + : + 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) +{ + // 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, int lineno) +{ + errmsg = QString(); + 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); + state = InSingleLineComment; + } else if (current == '/' && next1 == '*') { + recordStartPos(); + shift(1); + 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 = QLatin1String("Illegal character"); + } + } + break; + case InString: + if (current == stringType) { + shift(1); + setDone(String); + } else if (current == 0 || isLineTerminator()) { + setDone(Bad); + err = UnclosedStringLiteral; + errmsg = QLatin1String("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 = QLatin1String("Illegal escape squence"); + } + } 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 = QLatin1String("Illegal unicode escape sequence"); + } + break; + case InSingleLineComment: + if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + yycolumn = 0; + terminator = true; + bol = true; + if (restrKeyword) { + token = QScriptGrammar::T_SEMICOLON; + setDone(Other); + } else + state = Start; + } else if (current == 0) { + setDone(Eof); + } + break; + case InMultiLineComment: + if (current == 0) { + setDone(Bad); + err = UnclosedComment; + errmsg = QLatin1String("Unclosed comment at end of file"); + } else if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + } else if (current == '*' && next1 == '/') { + state = Start; + shift(1); + } + 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 = QLatin1String("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 = QLatin1String("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 = QLatin1String("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; + } +} + + +class Translator; + +class QScriptParser: protected $table +{ +public: + QVariant val; + + struct Location { + int startLine; + int startColumn; + int endLine; + int endColumn; + }; + +public: + QScriptParser(); + ~QScriptParser(); + + bool parse(QScript::Lexer *lexer, + const QString &fileName, + Translator *translator); + + 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]; } + +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; +}; + +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) +{ +} + +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; +} + +bool QScriptParser::parse(QScript::Lexer *lexer, + const QString &fileName, + Translator *translator) +{ + 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"))) { + QVariantList args = sym(2).toList(); + if (args.size() < 2) { + qWarning("%s:%d: %s() requires at least two arguments", + qPrintable(fileName), identLineNo, qPrintable(name)); + } else { + if ((args.at(0).type() != QVariant::String) + || (args.at(1).type() != QVariant::String)) { + qWarning("%s:%d: %s(): both arguments must be literal strings", + qPrintable(fileName), identLineNo, qPrintable(name)); + } else { + QString context = args.at(0).toString(); + QString text = args.at(1).toString(); + QString comment = args.value(2).toString(); + QString extracomment; + bool plural = (args.size() > 4); + recordMessage(translator, context, text, comment, extracomment, + plural, fileName, identLineNo); + } + } + } else if ((name == QLatin1String("qsTr")) || (name == QLatin1String("QT_TR_NOOP"))) { + QVariantList args = sym(2).toList(); + if (args.size() < 1) { + qWarning("%s:%d: %s() requires at least one argument", + qPrintable(fileName), identLineNo, qPrintable(name)); + } else { + if (args.at(0).type() != QVariant::String) { + qWarning("%s:%d: %s(): text to translate must be a literal string", + qPrintable(fileName), identLineNo, qPrintable(name)); + } else { + QString context = QFileInfo(fileName).baseName(); + QString text = args.at(0).toString(); + QString comment = args.value(1).toString(); + QString extracomment; + bool plural = (args.size() > 2); + recordMessage(translator, context, text, comment, extracomment, + plural, fileName, identLineNo); + } + } + } +} 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 ; +Statement: VariableStatement ; +Statement: EmptyStatement ; +Statement: ExpressionStatement ; +Statement: IfStatement ; +Statement: IterationStatement ; +Statement: ContinueStatement ; +Statement: BreakStatement ; +Statement: ReturnStatement ; +Statement: WithStatement ; +Statement: LabelledStatement ; +Statement: SwitchStatement ; +Statement: ThrowStatement ; +Statement: TryStatement ; +Statement: DebuggerStatement ; + +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 "); + 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; +} + + +bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd) +{ + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("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(); + QScript::Lexer lexer; + lexer.setCode(code, /*lineNumber=*/1); + QScriptParser parser; + if (!parser.parse(&lexer, filename, &translator)) { + qWarning("%s:%d: %s", qPrintable(filename), parser.errorLineNumber(), + qPrintable(parser.errorMessage())); + 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/tools/linguist/lupdate/ui.cpp b/tools/linguist/lupdate/ui.cpp new file mode 100644 index 0000000..6b47969 --- /dev/null +++ b/tools/linguist/lupdate/ui.cpp @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lupdate.h" + +#include <translator.h> + +#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 UiReader : public QXmlDefaultHandler +{ +public: + UiReader(Translator &translator, ConversionData &cd) + : m_translator(translator), m_cd(cd), m_lineNumber(-1), + 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; + 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")) { + flush(); + if (!atts.value(QLatin1String("text")).isEmpty()) + m_source = atts.value(QLatin1String("text")); + } 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")); + } else { + m_isTrString = false; + } + } + if (m_isTrString && !m_cd.m_noUiLines) + m_lineNumber = m_locator->lineNumber(); + 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")) { + if (m_context.isEmpty()) + m_context = m_accum; + } else if (qName == QLatin1String("string") && m_isTrString) { + m_source = m_accum; + } else if (qName == QLatin1String("comment")) { + m_comment = m_accum; + flush(); + } else if (qName == QLatin1String("function")) { + 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; + msg.sprintf("XML error: Parse error at line %d, column %d (%s).", + exception.lineNumber(), exception.columnNumber(), + exception.message().toLatin1().data()); + 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()); + if (m_needUtf8 && msg.needs8Bit()) + msg.setUtf8(true); + m_translator.extend(msg); + } + m_source.clear(); + m_comment.clear(); +} + +bool loadUI(Translator &translator, const QString &filename, ConversionData &cd) +{ + cd.m_sourceFileName = filename; + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("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(QLatin1String("Parse error in UI file")); + reader.setContentHandler(0); + reader.setErrorHandler(0); + return result; +} + +QT_END_NAMESPACE diff --git a/tools/linguist/lupdate/winmanifest.rc b/tools/linguist/lupdate/winmanifest.rc new file mode 100644 index 0000000..45c4935 --- /dev/null +++ b/tools/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/tools/linguist/phrasebooks/danish.qph b/tools/linguist/phrasebooks/danish.qph new file mode 100644 index 0000000..fb06e6f --- /dev/null +++ b/tools/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'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/tools/linguist/phrasebooks/dutch.qph b/tools/linguist/phrasebooks/dutch.qph new file mode 100644 index 0000000..f2db4e0 --- /dev/null +++ b/tools/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'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/tools/linguist/phrasebooks/finnish.qph b/tools/linguist/phrasebooks/finnish.qph new file mode 100644 index 0000000..caef197 --- /dev/null +++ b/tools/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'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/tools/linguist/phrasebooks/french.qph b/tools/linguist/phrasebooks/french.qph new file mode 100644 index 0000000..f244013 --- /dev/null +++ b/tools/linguist/phrasebooks/french.qph @@ -0,0 +1,1104 @@ +<!DOCTYPE QPH><QPH language="fr"> +<phrase> + <source>About</source> + <target>A propos</target> +</phrase> +<phrase> + <source>access key</source> + <target>touche d'accès rapide</target> +</phrase> +<phrase> + <source>accessibility</source> + <target>accessibilité</target> +</phrase> +<phrase> + <source>action handle</source> + <target>handle d'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'é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'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'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'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'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'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'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'é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>Annuler Annuler</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'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'é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'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's This?</source> + <target>Qu'est-ce que c'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> +</QPH> diff --git a/tools/linguist/phrasebooks/german.qph b/tools/linguist/phrasebooks/german.qph new file mode 100644 index 0000000..2be8c77 --- /dev/null +++ b/tools/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 & 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 & 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 & 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 & 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'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/tools/linguist/phrasebooks/italian.qph b/tools/linguist/phrasebooks/italian.qph new file mode 100644 index 0000000..0a4ea1d --- /dev/null +++ b/tools/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'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'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'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'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'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/tools/linguist/phrasebooks/japanese.qph b/tools/linguist/phrasebooks/japanese.qph new file mode 100644 index 0000000..3ecd9e3 --- /dev/null +++ b/tools/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'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/tools/linguist/phrasebooks/norwegian.qph b/tools/linguist/phrasebooks/norwegian.qph new file mode 100644 index 0000000..9fda2ad --- /dev/null +++ b/tools/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'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/tools/linguist/phrasebooks/polish.qph b/tools/linguist/phrasebooks/polish.qph new file mode 100644 index 0000000..1553696 --- /dev/null +++ b/tools/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ć <a href="http://qtsoftware.com/company/model.html">qtsoftware.com/company/model.html</a> 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/tools/linguist/phrasebooks/russian.qph b/tools/linguist/phrasebooks/russian.qph new file mode 100644 index 0000000..0b06cea --- /dev/null +++ b/tools/linguist/phrasebooks/russian.qph @@ -0,0 +1,982 @@ +<!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>Back</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>dock</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>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>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>tooltip</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'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> +</QPH> diff --git a/tools/linguist/phrasebooks/spanish.qph b/tools/linguist/phrasebooks/spanish.qph new file mode 100644 index 0000000..dacb6b5 --- /dev/null +++ b/tools/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 &quot;Cerrar&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 &quot;Restaurar&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 &quot;Inicio&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'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/tools/linguist/phrasebooks/swedish.qph b/tools/linguist/phrasebooks/swedish.qph new file mode 100644 index 0000000..a88735e --- /dev/null +++ b/tools/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'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/tools/linguist/qdoc.conf b/tools/linguist/qdoc.conf new file mode 100644 index 0000000..a89fb64 --- /dev/null +++ b/tools/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 = Trolltech +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/tools/linguist/shared/abstractproitemvisitor.h b/tools/linguist/shared/abstractproitemvisitor.h new file mode 100644 index 0000000..b108e7e --- /dev/null +++ b/tools/linguist/shared/abstractproitemvisitor.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTPROITEMVISITOR +#define ABSTRACTPROITEMVISITOR + +#include "proitems.h" + +QT_BEGIN_NAMESPACE + +struct AbstractProItemVisitor +{ + virtual ~AbstractProItemVisitor() {} + virtual bool visitBeginProBlock(ProBlock *block) = 0; + virtual bool visitEndProBlock(ProBlock *block) = 0; + + virtual bool visitBeginProVariable(ProVariable *variable) = 0; + virtual bool visitEndProVariable(ProVariable *variable) = 0; + + virtual bool visitBeginProFile(ProFile *value) = 0; + virtual bool visitEndProFile(ProFile *value) = 0; + + virtual bool visitProValue(ProValue *value) = 0; + virtual bool visitProFunction(ProFunction *function) = 0; + virtual bool visitProOperator(ProOperator *function) = 0; + virtual bool visitProCondition(ProCondition *function) = 0; +}; + +QT_END_NAMESPACE + +#endif // ABSTRACTPROITEMVISITOR + diff --git a/tools/linguist/shared/formats.pri b/tools/linguist/shared/formats.pri new file mode 100644 index 0000000..985f6db --- /dev/null +++ b/tools/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/tools/linguist/shared/numerus.cpp b/tools/linguist/shared/numerus.cpp new file mode 100644 index 0000000..f3a29cc --- /dev/null +++ b/tools/linguist/shared/numerus.cpp @@ -0,0 +1,377 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "translator.h" + +#include <QtCore/QCoreApplication> +#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 irishStyleRules[] = + { Q_EQ, 1, Q_NEWRULE, + Q_EQ, 2 }; +static const uchar czechRules[] = + { Q_MOD_100 | Q_EQ, 1, Q_NEWRULE, + Q_MOD_100 | Q_BETWEEN, 2, 4 }; +static const uchar slovakRules[] = + { 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_EQ, 2, 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_NEQ, 0 }; + +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 latvianForms[] = { "Singular", "Plural", "Nullar", 0 }; +static const char * const irishStyleForms[] = { "Singular", "Dual", "Plural", 0 }; +static const char * const czechForms[] = { "Singular", "Dual", "Plural", 0 }; +static const char * const slovakForms[] = { "Singular", "Dual", "Plural", 0 }; +static const char * const macedonianForms[] = { "Singular", "Dual", "Plural", 0 }; +static const char * const lithuanianForms[] = { "Singular", "Dual", "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", "Plural Form for 2 to 19", "Plural", 0 }; +static const char * const slovenianForms[] = { "Singular", "Dual", "Trial", "Plural", 0 }; +static const char * const malteseForms[] = + { "Singular", "Plural Form for 2 to 10", "Plural Form for 11 to 19", "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 Form for 100, 200, ...", 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::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::Catalan, + 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::Icelandic, + 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::Tagalog, + QLocale::Tajik, + QLocale::Tamil, + QLocale::Tatar, + QLocale::Telugu, + QLocale::TongaLanguage, + QLocale::Tsonga, + QLocale::Turkish, + QLocale::Turkmen, + QLocale::Twi, + QLocale::Uigur, + 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 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 czechLanguage[] = { QLocale::Czech, EOL }; +static const QLocale::Language slovakLanguage[] = { QLocale::Slovak, 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::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; +}; + +static const NumerusTableEntry numerusTable[] = { + { 0, 0, japaneseStyleForms, japaneseStyleLanguages, 0 }, + { englishStyleRules, sizeof(englishStyleRules), englishStyleForms, englishStyleLanguages, 0 }, + { frenchStyleRules, sizeof(frenchStyleRules), frenchStyleForms, frenchStyleLanguages, + frenchStyleCountries }, + { latvianRules, sizeof(latvianRules), latvianForms, latvianLanguage, 0 }, + { irishStyleRules, sizeof(irishStyleRules), irishStyleForms, irishStyleLanguages, 0 }, + { czechRules, sizeof(czechRules), czechForms, czechLanguage, 0 }, + { slovakRules, sizeof(slovakRules), slovakForms, slovakLanguage, 0 }, + { macedonianRules, sizeof(macedonianRules), macedonianForms, macedonianLanguage, 0 }, + { lithuanianRules, sizeof(lithuanianRules), lithuanianForms, lithuanianLanguage, 0 }, + { russianStyleRules, sizeof(russianStyleRules), russianStyleForms, russianStyleLanguages, 0 }, + { polishRules, sizeof(polishRules), polishForms, polishLanguage, 0 }, + { romanianRules, sizeof(romanianRules), romanianForms, romanianLanguages, 0 }, + { slovenianRules, sizeof(slovenianRules), slovenianForms, slovenianLanguage, 0 }, + { malteseRules, sizeof(malteseRules), malteseForms, malteseLanguage, 0 }, + { welshRules, sizeof(welshRules), welshForms, welshLanguage, 0 }, + { arabicRules, sizeof(arabicRules), arabicForms, arabicLanguage, 0 } +}; + +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) +{ + 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 (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/tools/linguist/shared/po.cpp b/tools/linguist/shared/po.cpp new file mode 100644 index 0000000..e9375e9 --- /dev/null +++ b/tools/linguist/shared/po.cpp @@ -0,0 +1,662 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "translator.h" + +#include <QtCore/QDebug> +#include <QtCore/QIODevice> +#include <QtCore/QHash> +#include <QtCore/QString> +#include <QtCore/QTextStream> + +#include <ctype.h> + +#define MAGIC_OBSOLETE_REFERENCE "Obsolete_PO_entries" + +// 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: + QString id; + QString context; + QString tscomment; + QString oldTscomment; + QString lineNumber; + QString fileName; + QString references; + QString translatorComments; + QString automaticComments; + QString msgId; + QString oldMsgId; + QStringList msgStr; + bool isPlural; + bool isFuzzy; + QHash<QString, QString> extra; +}; + + +static bool isTranslationLine(const QString &line) +{ + return line.startsWith(QLatin1String("#~ msgstr")) + || line.startsWith(QLatin1String("msgstr")); +} + +static QString slurpEscapedString(const QStringList &lines, int & l, + int offset, const QString &prefix, ConversionData &cd) +{ + QString msg; + int stoff; + + for (; l < lines.size(); ++l) { + const QString &line = lines.at(l); + if (line.isEmpty() || !line.startsWith(prefix)) + break; + while (line[offset].isSpace()) // No length check, as string has no trailing spaces. + offset++; + if (line[offset].unicode() != '"') + break; + offset++; + forever { + if (offset == line.length()) + goto premature_eol; + ushort c = line[offset++].unicode(); + if (c == '"') { + if (offset == line.length()) + break; + while (line[offset].isSpace()) + offset++; + if (line[offset++].unicode() != '"') { + 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++].unicode(); + switch (c) { + case 'r': + msg += QLatin1Char('\r'); // Maybe just throw it away? + break; + case 'n': + msg += QLatin1Char('\n'); + break; + case 't': + msg += QLatin1Char('\t'); + break; + case 'v': + msg += QLatin1Char('\v'); + break; + case 'a': + msg += QLatin1Char('\a'); + break; + case 'b': + msg += QLatin1Char('\b'); + break; + case 'f': + msg += QLatin1Char('\f'); + break; + case '"': + msg += QLatin1Char('"'); + break; + case '\\': + msg += QLatin1Char('\\'); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + stoff = offset - 1; + while ((c = line[offset].unicode()) >= '0' && c <= '7') + if (++offset == line.length()) + goto premature_eol; + msg += QChar(line.mid(stoff, offset - stoff).toUInt(0, 8)); + break; + case 'x': + stoff = offset; + while (isxdigit(line[offset].unicode())) + if (++offset == line.length()) + goto premature_eol; + msg += QChar(line.mid(stoff, offset - stoff).toUInt(0, 16)); + break; + default: + cd.appendError(QString::fromLatin1( + "PO parsing error: invalid escape '\\%1' (line %2).") + .arg(QChar(c)).arg(l + 1)); + msg += QLatin1Char('\\'); + msg += QChar(c); + break; + } + } else { + msg += QChar(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 QString(); +} + +static void slurpComment(QString &msg, const QStringList &lines, int & l) +{ + const QChar newline = QLatin1Char('\n'); + QString prefix = lines.at(l); + for (int i = 1; ; i++) { + if (prefix.at(i).unicode() != ' ') { + prefix.truncate(i); + break; + } + } + for (; l < lines.size(); ++l) { + const QString &line = lines.at(l); + if (line.startsWith(prefix)) + msg += line.mid(prefix.size()); + else if (line != QLatin1String("#")) + break; + msg += newline; + } + --l; +} + +bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd) +{ + const QChar quote = QLatin1Char('"'); + const QChar newline = QLatin1Char('\n'); + QTextStream in(&dev); + 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. + QStringList lines; + while (!in.atEnd()) + lines.append(in.readLine().trimmed()); + lines.append(QString()); + + int l = 0; + PoItem item; + for (; l != lines.size(); ++l) { + QString line = lines.at(l); + if (line.isEmpty()) + continue; + if (isTranslationLine(line)) { + bool isObsolete = line.startsWith(QLatin1String("#~ msgstr")); + const QString prefix = QLatin1String(isObsolete ? "#~ " : ""); + while (true) { + int idx = line.indexOf(QLatin1Char(' '), prefix.length()); + item.msgStr.append(slurpEscapedString(lines, l, idx, prefix, cd)); + if (l + 1 >= lines.size() || !isTranslationLine(lines.at(l + 1))) + break; + ++l; + line = lines.at(l); + } + if (item.msgId.isEmpty()) { + QRegExp rx(QLatin1String("\\bX-Language: ([^\n]*)\n")); + int idx = rx.indexIn(item.msgStr.first()); + if (idx >= 0) { + translator.setLanguageCode(rx.cap(1)); + item.msgStr.first().remove(idx, rx.matchedLength()); + } + QRegExp rx2(QLatin1String("\\bX-Source-Language: ([^\n]*)\n")); + int idx2 = rx2.indexIn(item.msgStr.first()); + if (idx2 >= 0) { + translator.setSourceLanguageCode(rx2.cap(1)); + item.msgStr.first().remove(idx2, rx2.matchedLength()); + } + if (item.msgStr.first().indexOf( + QRegExp(QLatin1String("\\bX-Virgin-Header:[^\n]*\n"))) >= 0) { + item = PoItem(); + continue; + } + } + // build translator message + TranslatorMessage msg; + msg.setContext(item.context); + if (!item.references.isEmpty()) { + foreach (const QString &ref, + item.references.split(QRegExp(QLatin1String("\\s")), + QString::SkipEmptyParts)) { + int pos = ref.lastIndexOf(QLatin1Char(':')); + if (pos != -1) + msg.addReference(ref.left(pos), ref.mid(pos + 1).toInt()); + } + } else if (isObsolete) { + msg.setFileName(QLatin1String(MAGIC_OBSOLETE_REFERENCE)); + } + msg.setId(item.id); + msg.setSourceText(item.msgId); + msg.setOldSourceText(item.oldMsgId); + msg.setComment(item.tscomment); + msg.setOldComment(item.oldTscomment); + msg.setExtraComment(item.automaticComments); + msg.setTranslatorComment(item.translatorComments); + msg.setPlural(item.isPlural || item.msgStr.size() > 1); + msg.setTranslations(item.msgStr); + if (isObsolete) + msg.setType(TranslatorMessage::Obsolete); + else if (item.isFuzzy) + 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(QLatin1Char('#'))) { + switch(line.size() < 2 ? 0 : line.at(1).unicode()) { + case ':': + item.references += line.mid(3); + item.references += newline; + break; + case ',': { + QStringList flags = + line.mid(2).split(QRegExp(QLatin1String("[, ]")), + QString::SkipEmptyParts); + if (flags.removeOne(QLatin1String("fuzzy"))) + item.isFuzzy = true; + 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 += newline; + break; + case ' ': + slurpComment(item.translatorComments, lines, l); + break; + case '.': + if (line.startsWith(QLatin1String("#. ts-context "))) { + item.context = line.mid(14); + } else if (line.startsWith(QLatin1String("#. ts-id "))) { + item.id = line.mid(9); + } else { + item.automaticComments += line.mid(3); + item.automaticComments += newline; + } + break; + case '|': + if (line.startsWith(QLatin1String("#| msgid "))) { + item.oldMsgId = slurpEscapedString(lines, l, 9, QLatin1String("#| "), cd); + } else if (line.startsWith(QLatin1String("#| msgid_plural "))) { + QString extra = slurpEscapedString(lines, l, 16, QLatin1String("#| "), cd); + if (extra != item.oldMsgId) + item.extra[QLatin1String("po-old_msgid_plural")] = extra; + } else if (line.startsWith(QLatin1String("#| msgctxt "))) { + item.oldTscomment = slurpEscapedString(lines, l, 11, QLatin1String("#| "), cd); + } else { + cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'\n")) + .arg(l + 1).arg(lines[l])); + error = true; + } + break; + case '~': + if (line.startsWith(QLatin1String("#~ msgid "))) { + item.msgId = slurpEscapedString(lines, l, 9, QLatin1String("#~ "), cd); + } else if (line.startsWith(QLatin1String("#~ msgid_plural "))) { + QString extra = slurpEscapedString(lines, l, 16, QLatin1String("#~ "), cd); + if (extra != item.msgId) + item.extra[QLatin1String("po-msgid_plural")] = extra; + item.isPlural = true; + } else if (line.startsWith(QLatin1String("#~ msgctxt "))) { + item.tscomment = slurpEscapedString(lines, l, 11, QLatin1String("#~ "), cd); + } else { + cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'\n")) + .arg(l + 1).arg(lines[l])); + error = true; + } + break; + default: + cd.appendError(QString(QLatin1String("PO-format parse error in line %1: '%2'\n")) + .arg(l + 1).arg(lines[l])); + error = true; + break; + } + } else if (line.startsWith(QLatin1String("msgctxt "))) { + item.tscomment = slurpEscapedString(lines, l, 8, QString(), cd); + } else if (line.startsWith(QLatin1String("msgid "))) { + item.msgId = slurpEscapedString(lines, l, 6, QString(), cd); + } else if (line.startsWith(QLatin1String("msgid_plural "))) { + QString extra = slurpEscapedString(lines, l, 13, QString(), cd); + if (extra != item.msgId) + item.extra[QLatin1String("po-msgid_plural")] = extra; + item.isPlural = true; + } else { + cd.appendError(QString(QLatin1String("PO-format error in line %1: '%2'\n")) + .arg(l + 1).arg(lines[l])); + error = true; + } + } + return !error && cd.errors().isEmpty(); +} + +bool savePO(const Translator &translator, QIODevice &dev, ConversionData &cd) +{ + bool ok = true; + QTextStream out(&dev); + //qDebug() << "OUT CODEC: " << out.codec()->name(); + + bool first = true; + if (translator.messages().isEmpty() || !translator.messages().first().sourceText().isEmpty()) { + out << + "# SOME DESCRIPTIVE TITLE.\n" + "# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n" + "# This file is distributed under the same license as the PACKAGE package.\n" + "# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n" + "#\n" + "#, fuzzy\n" + "msgid \"\"\n" + "msgstr \"\"\n" + "\"X-Virgin-Header: remove this line if you change anything in the header.\\n\"\n"; + if (!translator.languageCode().isEmpty()) + out << "\"X-Language: " << translator.languageCode() << "\\n\"\n"; + if (!translator.sourceLanguageCode().isEmpty()) + out << "\"X-Source-Language: " << translator.sourceLanguageCode() << "\\n\"\n"; + first = false; + } + foreach (const TranslatorMessage &msg, translator.messages()) { + if (!first) + out << endl; + + if (!msg.translatorComment().isEmpty()) + out << poEscapedLines(QLatin1String("#"), true, msg.translatorComment()); + + if (!msg.extraComment().isEmpty()) + out << poEscapedLines(QLatin1String("#."), true, msg.extraComment()); + + if (!msg.context().isEmpty()) + out << QLatin1String("#. ts-context ") << msg.context() << '\n'; + if (!msg.id().isEmpty()) + out << QLatin1String("#. ts-id ") << msg.id() << '\n'; + + if (!msg.fileName().isEmpty() && msg.fileName() != QLatin1String(MAGIC_OBSOLETE_REFERENCE)) { + QStringList refs; + foreach (const TranslatorMessage::Reference &ref, msg.allReferences()) + refs.append(QString(QLatin1String("%2:%1")) + .arg(ref.lineNumber()).arg(ref.fileName())); + out << poWrappedEscapedLines(QLatin1String("#:"), true, refs.join(QLatin1String(" "))); + } + + bool noWrap = false; + QStringList flags; + if (msg.type() == TranslatorMessage::Unfinished) + flags.append(QLatin1String("fuzzy")); + TranslatorMessage::ExtraData::const_iterator itr = + msg.extras().find(QLatin1String("po-flags")); + if (itr != msg.extras().end()) { + if (itr->split(QLatin1String(", ")).contains(QLatin1String("no-wrap"))) + noWrap = true; + flags.append(*itr); + } + if (!flags.isEmpty()) + out << "#, " << flags.join(QLatin1String(", ")) << '\n'; + + QString prefix = QLatin1String("#| "); + if (!msg.oldComment().isEmpty()) + out << poEscapedString(prefix, QLatin1String("msgctxt"), noWrap, msg.oldComment()); + 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.comment().isEmpty()) + out << poEscapedString(prefix, QLatin1String("msgctxt"), noWrap, msg.comment()); + out << poEscapedString(prefix, QLatin1String("msgid"), noWrap, msg.sourceText()); + if (!msg.isPlural()) { + QString transl = msg.translation(); + if (first) { + transl.remove(QRegExp(QLatin1String("\\bX-Language:[^\n]*\n"))); + if (!translator.languageCode().isEmpty()) + transl += QLatin1String("X-Language: ") + translator.languageCode() + QLatin1Char('\n'); + } + 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); + QStringList translations = translator.normalizedTranslations(msg, cd, &ok); + for (int i = 0; i != translations.size(); ++i) { + out << poEscapedString(prefix, QString::fromLatin1("msgstr[%1]").arg(i), noWrap, + translations.at(i)); + } + } + first = false; + } + return ok; +} + +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); + return 1; +} + +Q_CONSTRUCTOR_FUNCTION(initPO) + +QT_END_NAMESPACE diff --git a/tools/linguist/shared/profileevaluator.cpp b/tools/linguist/shared/profileevaluator.cpp new file mode 100644 index 0000000..b9984cc --- /dev/null +++ b/tools/linguist/shared/profileevaluator.cpp @@ -0,0 +1,2357 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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> +#elif defined(Q_OS_WIN32) +#include <Windows.h> +#endif +#include <stdio.h> +#include <stdlib.h> + +#ifdef Q_OS_WIN32 +#define QT_POPEN _popen +#else +#define QT_POPEN popen +#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; + + /////////////// Evaluating pro file contents + + // implementation of AbstractProItemVisitor + bool visitBeginProBlock(ProBlock *block); + bool visitEndProBlock(ProBlock *block); + bool visitBeginProVariable(ProVariable *variable); + bool visitEndProVariable(ProVariable *variable); + bool visitBeginProFile(ProFile *value); + bool visitEndProFile(ProFile *value); + bool visitProValue(ProValue *value); + bool visitProFunction(ProFunction *function); + bool visitProOperator(ProOperator *oper); + bool 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 expandPattern(const QString &pattern); + void expandPatternHelper(const QString &relName, const QString &absName, + QStringList &sources_out); + QStringList expandVariableReferences(const QString &value); + QStringList evaluateExpandFunction(const QString &function, const QString &arguments); + QString format(const char *format) const; + + QString currentFileName() const; + QString currentDirectory() const; + ProFile *currentProFile() const; + + bool evaluateConditionalFunction(const QString &function, const QString &arguments, bool *result); + bool evaluateFile(const QString &fileName, bool *result); + bool evaluateFeatureFile(const QString &fileName, bool *result); + + QStringList qmakeFeaturePaths(); + + enum { ConditionTrue, ConditionFalse, ConditionElse }; + int m_condition; + int m_prevCondition; + bool m_updateCondition; + bool m_invertNext; + 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' + + // 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; + + int m_prevLineNo; // Checking whether we're assigning the same TARGET + ProFile *m_prevProFile; // See m_prevLineNo +}; + +ProFileEvaluator::Private::Private(ProFileEvaluator *q_) + : q(q_) +{ + m_prevLineNo = 0; + m_prevProFile = 0; + m_verbose = true; + m_block = 0; + m_commentItem = 0; + m_syntaxError = 0; + m_lineNo = 0; + m_contNextLine = false; + m_cumulative = true; + m_updateCondition = false; + m_condition = ConditionFalse; + m_invertNext = false; + m_skipLevel = 0; + m_isFirstVariableValue = true; +} + +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; + } + + m_syntaxError = false; + m_lineNo = 1; + 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; + + ushort quote = 0; + int parens = 0; + bool contNextLine = false; + QString line = line0.simplified(); + + for (int i = 0; !m_syntaxError && i < line.length(); ++i) { + ushort c = line.at(i).unicode(); + if (quote && c == quote) + quote = 0; + else if (c == '(') + ++parens; + else if (c == ')') + --parens; + else if (c == '"' && (i == 0 || line.at(i - 1).unicode() != '\\')) + quote = c; + else if (!parens && !quote) { + if (c == '#') { + insertComment(line.mid(i + 1)); + contNextLine = m_contNextLine; + break; + } + if (c == '\\' && i >= line.count() - 1) { + updateItem(); + contNextLine = true; + continue; + } + 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_contNextLine = contNextLine; + + if (!m_syntaxError) { + updateItem(); + if (!m_contNextLine) + finalizeBlock(); + } + return !m_syntaxError; +} + +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; + + 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(); +} + + +bool ProFileEvaluator::Private::visitBeginProBlock(ProBlock *block) +{ + if (block->blockKind() == ProBlock::ScopeKind) { + m_updateCondition = true; + if (!m_skipLevel) { + m_prevCondition = m_condition; + m_condition = ConditionFalse; + } else { + Q_ASSERT(m_condition != ConditionTrue); + } + } else if (block->blockKind() & ProBlock::ScopeContentsKind) { + m_updateCondition = false; + if (m_condition != ConditionTrue) + ++m_skipLevel; + else + Q_ASSERT(!m_skipLevel); + } + return true; +} + +bool ProFileEvaluator::Private::visitEndProBlock(ProBlock *block) +{ + if (block->blockKind() & ProBlock::ScopeContentsKind) { + if (m_skipLevel) { + Q_ASSERT(m_condition != ConditionTrue); + --m_skipLevel; + } else { + // 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_condition = ConditionTrue; + } + } + return true; +} + +bool ProFileEvaluator::Private::visitBeginProVariable(ProVariable *variable) +{ + m_lastVarName = variable->variable(); + m_variableOperator = variable->variableOperator(); + m_isFirstVariableValue = true; + m_tempValuemap = m_valuemap; + m_tempFilevaluemap = m_filevaluemap; + return true; +} + +bool ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable) +{ + Q_UNUSED(variable); + m_valuemap = m_tempValuemap; + m_filevaluemap = m_tempFilevaluemap; + m_lastVarName.clear(); + return true; +} + +bool ProFileEvaluator::Private::visitProOperator(ProOperator *oper) +{ + m_invertNext = (oper->operatorKind() == ProOperator::NotOperator); + return true; +} + +bool ProFileEvaluator::Private::visitProCondition(ProCondition *cond) +{ + if (!m_skipLevel) { + if (cond->text().toLower() == QLatin1String("else")) { + // The state ConditionElse makes sure that subsequential elses are ignored. + // That's braindead, but qmake is like that. + if (m_prevCondition == ConditionTrue) + m_condition = ConditionElse; + else if (m_prevCondition == ConditionFalse) + m_condition = ConditionTrue; + } else if (m_condition == ConditionFalse) { + if (isActiveConfig(cond->text(), true) ^ m_invertNext) + m_condition = ConditionTrue; + } + } + m_invertNext = false; + return true; +} + +bool ProFileEvaluator::Private::visitBeginProFile(ProFile * pro) +{ + PRE(pro); + bool ok = true; + 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"), &ok); + evaluateFile(mkspecDirectory + QLatin1String("/features/default_pre.prf"), &ok); + m_cumulative = cumulative; + } + + ok = QDir::setCurrent(pro->directoryName()); + } + + return ok; +} + +bool ProFileEvaluator::Private::visitEndProFile(ProFile * pro) +{ + PRE(pro); + bool ok = true; + 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"), &ok); + + 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); + evaluateFile(mkspecDirectory + QLatin1String("/features/") + + config + QLatin1String(".prf"), &ok); + if (ok) { + finished = false; + break; + } + } + } + if (finished) + break; + } + + m_cumulative = cumulative; + } + + m_profileStack.pop(); + ok = QDir::setCurrent(m_oldPath); + } + return ok; +} + +static void replaceInList(QStringList *varlist, + const QRegExp ®exp, 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; + } + } +} + +bool 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(); + + // The following two blocks fix bug 180128 by making all "interesting" + // file name absolute in each .pro file, not just the top most one + if (varName == QLatin1String("SOURCES") + || varName == QLatin1String("HEADERS") + || varName == QLatin1String("INTERFACES") + || varName == QLatin1String("FORMS") + || varName == QLatin1String("FORMS3") + || varName == QLatin1String("RESOURCES")) { + // matches only existent files, expand certain(?) patterns + QStringList vv; + for (int i = v.count(); --i >= 0; ) + vv << expandPattern(v[i]); + v = vv; + } + + if (varName == QLatin1String("TRANSLATIONS")) { + // also matches non-existent files, but does not expand pattern + QString dir = QFileInfo(currentFileName()).absolutePath(); + dir += QLatin1Char('/'); + for (int i = v.count(); --i >= 0; ) + v[i] = QFileInfo(dir, v[i]).absoluteFilePath(); + } + + 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] + + // FIXME: qmake variable-expands val first. + if (val.length() < 4 || val[0] != QLatin1Char('s')) { + q->logMessage(format("the ~= operator can handle only the s/// function.")); + return false; + } + 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.")); + return false; + } + + 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; + return true; +} + +bool ProFileEvaluator::Private::visitProFunction(ProFunction *func) +{ + if (!m_updateCondition || m_condition == ConditionFalse) { + 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(); + bool result; + if (!evaluateConditionalFunction(funcName.trimmed(), arguments, &result)) { + m_invertNext = false; + return false; + } + if (!m_skipLevel && (result ^ m_invertNext)) + m_condition = ConditionTrue; + } + m_invertNext = false; + return true; +} + + +QStringList ProFileEvaluator::Private::qmakeFeaturePaths() +{ + QStringList concat; + { + const QString base_concat = QDir::separator() + QString(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() + QString(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(); +} + +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::evaluateExpandFunction(const QString &func, const QString &arguments) +{ + QStringList argumentsList = split_arg_list(arguments); + QStringList args; + for (int i = 0; i < argumentsList.count(); ++i) + args += expandVariableReferences(argumentsList[i]); + + 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 = 0; + if (!expands) { + expands = new QHash<QString, int>; + 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].toLower() == QLatin1String("true")); + + 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].toLower() == QLatin1String("true")); + 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); + } + } + 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].toLower() == QLatin1String("true") || 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; +} + +bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &function, + const QString &arguments, bool *result) +{ + QStringList argumentsList = split_arg_list(arguments); + 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 }; + + static QHash<QString, int> *functions = 0; + if (!functions) { + functions = new QHash<QString, int>; + 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 + } + + bool cond = false; + bool ok = true; + + TestFunc func_t = (TestFunc)functions->value(function); + + switch (func_t) { +#if 0 + case T_INFILE: + case T_REQUIRES: + case T_GREATERTHAN: + case T_LESSTHAN: + case T_EQUALS: + case T_EXPORT: + case T_CLEAR: + case T_UNSET: + case T_EVAL: + case T_IF: + case T_RETURN: + case T_BREAK: + case T_NEXT: + case T_DEFINED: +#endif + case T_CONFIG: { + if (args.count() < 1 || args.count() > 2) { + q->logMessage(format("CONFIG(config) requires one or two arguments.")); + ok = false; + break; + } + if (args.count() == 1) { + //cond = isActiveConfig(args.first()); XXX + break; + } + 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()) { + cond = (configs[i] == args[0]); + break; + } + } + } + break; + } + case T_CONTAINS: { + if (args.count() < 2 || args.count() > 3) { + q->logMessage(format("contains(var, val) requires two or three arguments.")); + ok = false; + break; + } + + 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]) { + cond = true; + break; + } + } + } 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()) { + cond = (regx.exactMatch(val) || val == args[1]); + break; + } + } + } + } + + break; + } + case T_COUNT: { + if (args.count() != 2 && args.count() != 3) { + q->logMessage(format("count(var, count, op=\"equals\") requires two or three arguments.")); + ok = false; + break; + } + if (args.count() == 3) { + QString comp = args[2]; + if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) { + cond = values(args.first()).count() > args[1].toInt(); + } else if (comp == QLatin1String(">=")) { + cond = values(args.first()).count() >= args[1].toInt(); + } else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) { + cond = values(args.first()).count() < args[1].toInt(); + } else if (comp == QLatin1String("<=")) { + cond = values(args.first()).count() <= args[1].toInt(); + } else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual") || comp == QLatin1String("=") || comp == QLatin1String("==")) { + cond = values(args.first()).count() == args[1].toInt(); + } else { + ok = false; + q->logMessage(format("unexpected modifier to count(%2)").arg(comp)); + } + break; + } + cond = values(args.first()).count() == args[1].toInt(); + break; + } + case T_INCLUDE: { + if (m_skipLevel && !m_cumulative) + break; + QString parseInto; + if (args.count() == 2) { + parseInto = args[1]; + } else if (args.count() != 1) { + q->logMessage(format("include(file) requires one or two arguments.")); + ok = false; + break; + } + 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)); + ok = evaluateFile(fileName, &ok); + break; + } + case T_LOAD: { + if (m_skipLevel && !m_cumulative) + break; + QString parseInto; + bool ignore_error = false; + if (args.count() == 2) { + QString sarg = args[1]; + ignore_error = (sarg.toLower() == QLatin1String("true") || sarg.toInt()); + } else if (args.count() != 1) { + q->logMessage(format("load(feature) requires one or two arguments.")); + ok = false; + break; + } + ok = evaluateFeatureFile( args.first(), &cond); + break; + } + case T_DEBUG: + // Yup - do nothing. Nothing is going to enable debug output anyway. + break; + case T_MESSAGE: { + if (args.count() != 1) { + q->logMessage(format("%1(message) requires one argument.").arg(function)); + ok = false; + break; + } + QString msg = fixEnvVariables(args.first()); + if (function == QLatin1String("error")) { + QStringList parents; + foreach (ProFile *proFile, m_profileStack) + parents.append(proFile->fileName()); + if (!parents.isEmpty()) + parents.takeLast(); + if (parents.isEmpty()) + q->fileMessage(format("Project ERROR: %1").arg(msg)); + else + q->fileMessage(format("Project ERROR: %1. File was included from: '%2'") + .arg(msg).arg(parents.join(QLatin1String("', '")))); + } else { + q->fileMessage(format("Project MESSAGE: %1").arg(msg)); + } + break; + } +#if 0 // Way too dangerous to enable. + case T_SYSTEM: { + if (args.count() != 1) { + q->logMessage(format("system(exec) requires one argument.")); + ok = false; + break; + } + ok = system(args.first().toLatin1().constData()) == 0; + break; + } +#endif + case T_ISEMPTY: { + if (args.count() != 1) { + q->logMessage(format("isEmpty(var) requires one argument.")); + ok = false; + break; + } + QStringList sl = values(args.first()); + if (sl.count() == 0) { + cond = true; + } else if (sl.count() > 0) { + QString var = sl.first(); + cond = (var.isEmpty()); + } + break; + } + case T_EXISTS: { + if (args.count() != 1) { + q->logMessage(format("exists(file) requires one argument.")); + ok = false; + break; + } + QString file = args.first(); + file = Option::fixPathToLocalOS(file); + + if (QFile::exists(file)) { + cond = true; + break; + } + //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('?'))) + cond = QDir(dirstr).entryList(QStringList(file)).count(); + + break; + } + case 0: + // This is too chatty currently (missing defineTest and defineReplace) + //q->logMessage(format("'%1' is not a recognized test function").arg(function)); + break; + default: + q->logMessage(format("Function '%1' is not implemented").arg(function)); + break; + } + + if (result) + *result = cond; + + return ok; +} + +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; + TCHAR name[1024]; + if (GetComputerName(name, &name_length)) + ret = QString::fromUtf16((ushort*)name, name_length); + } 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()) { + ProFile *pro = new ProFile(fi.absoluteFilePath()); + if (d->read(pro)) + return pro; + delete pro; + } + return 0; +} + +void ProFileEvaluator::releaseParsedProFile(ProFile *proFile) +{ + delete proFile; +} + +bool ProFileEvaluator::Private::evaluateFile(const QString &fileName, bool *result) +{ + bool ok = true; + ProFile *pro = q->parsedProFile(fileName); + if (pro) { + m_profileStack.push(pro); + ok = (currentProFile() ? pro->Accept(this) : false); + m_profileStack.pop(); + q->releaseParsedProFile(pro); + + if (result) + *result = true; + } else { + if (result) + *result = false; + } +/* if (ok && readFeatures) { + QStringList configs = values("CONFIG"); + QSet<QString> processed; + foreach (const QString &fn, configs) { + if (!processed.contains(fn)) { + processed.insert(fn); + evaluateFeatureFile(fn, 0); + } + } + } */ + + return ok; +} + +bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName, bool *result) +{ + 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, result); + m_cumulative = cumulative; + return ok; +} + +void ProFileEvaluator::Private::expandPatternHelper(const QString &relName, const QString &absName, + QStringList &sources_out) +{ + const QStringList vpaths = values(QLatin1String("VPATH")) + + values(QLatin1String("QMAKE_ABSOLUTE_SOURCE_PATH")) + + values(QLatin1String("DEPENDPATH")) + + values(QLatin1String("VPATH_SOURCES")); + + QFileInfo fi(absName); + bool found = fi.exists(); + // Search in all vpaths + if (!found) { + foreach (const QString &vpath, vpaths) { + fi.setFile(vpath + QDir::separator() + relName); + if (fi.exists()) { + found = true; + break; + } + } + } + + if (found) { + sources_out += fi.absoluteFilePath(); // Not resolving symlinks + } else { + QString val = relName; + QString dir; + QString wildcard = val; + QString real_dir; + if (wildcard.lastIndexOf(QLatin1Char('/')) != -1) { + dir = wildcard.left(wildcard.lastIndexOf(QLatin1Char('/')) + 1); + real_dir = dir; + wildcard = wildcard.right(wildcard.length() - dir.length()); + } + + if (real_dir.isEmpty() || QFileInfo(real_dir).exists()) { + QStringList files = QDir(real_dir).entryList(QStringList(wildcard)); + if (files.isEmpty()) { + q->logMessage(format("Failure to find %1").arg(val)); + } else { + QString a; + for (int i = files.count() - 1; i >= 0; --i) { + if (files[i] == QLatin1String(".") || files[i] == QLatin1String("..")) + continue; + a = dir + files[i]; + sources_out += a; + } + } + } else { + q->logMessage(format("Cannot match %1/%2, as %3 does not exist.") + .arg(real_dir).arg(wildcard).arg(real_dir)); + } + } +} + + +/* + * Lookup of files are done in this order: + * 1. look in pwd + * 2. look in vpaths + * 3. expand wild card files relative from the profiles folder + **/ + +// FIXME: This code supports something that I'd consider a flaw in .pro file syntax +// which is not even documented. So arguably this can be ditched completely... +QStringList ProFileEvaluator::Private::expandPattern(const QString& pattern) +{ + if (!currentProFile()) + return QStringList(); + + QStringList sources_out; + const QString absName = QDir::cleanPath(QDir::current().absoluteFilePath(pattern)); + + expandPatternHelper(pattern, absName, sources_out); + return sources_out; +} + +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)); +} + +ProFileEvaluator::TemplateType ProFileEvaluator::templateType() +{ + QStringList templ = values(QLatin1String("TEMPLATE")); + if (templ.count() >= 1) { + QString t = templ.last().toLower(); + if (t == QLatin1String("app")) + return TT_Application; + if (t == QLatin1String("lib")) + return TT_Library; + if (t == QLatin1String("script")) + return TT_Script; + if (t == QLatin1String("subdirs")) + 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) + qWarning("%s", qPrintable(message)); +} + +void ProFileEvaluator::fileMessage(const QString &message) +{ + if (!d->m_skipLevel) + qWarning("%s", qPrintable(message)); +} + +void ProFileEvaluator::errorMessage(const QString &message) +{ + if (!d->m_skipLevel) + qWarning("%s", 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; +} + +void evaluateProFile(const ProFileEvaluator &visitor, QHash<QByteArray, QStringList> *varMap) +{ + QStringList sourceFiles; + QString codecForTr; + QString codecForSource; + QStringList tsFileNames; + + // app/lib template + sourceFiles += visitor.values(QLatin1String("SOURCES")); + sourceFiles += visitor.values(QLatin1String("HEADERS")); + tsFileNames = visitor.values(QLatin1String("TRANSLATIONS")); + + QStringList trcodec = visitor.values(QLatin1String("CODEC")) + + visitor.values(QLatin1String("DEFAULTCODEC")) + + visitor.values(QLatin1String("CODECFORTR")); + if (!trcodec.isEmpty()) + codecForTr = trcodec.last(); + + QStringList srccodec = visitor.values(QLatin1String("CODECFORSRC")); + if (!srccodec.isEmpty()) + codecForSource = srccodec.last(); + + QStringList forms = visitor.values(QLatin1String("INTERFACES")) + + visitor.values(QLatin1String("FORMS")) + + visitor.values(QLatin1String("FORMS3")); + sourceFiles << forms; + + sourceFiles.sort(); + sourceFiles.removeDuplicates(); + tsFileNames.sort(); + tsFileNames.removeDuplicates(); + + varMap->insert("SOURCES", sourceFiles); + varMap->insert("CODECFORTR", QStringList() << codecForTr); + varMap->insert("CODECFORSRC", QStringList() << codecForSource); + varMap->insert("TRANSLATIONS", tsFileNames); +} + +bool evaluateProFile(const QString &fileName, bool verbose, QHash<QByteArray, QStringList> *varMap) +{ + QFileInfo fi(fileName); + if (!fi.exists()) + return false; + + ProFile pro(fi.absoluteFilePath()); + + ProFileEvaluator visitor; + visitor.setVerbose(verbose); + + if (!visitor.queryProFile(&pro)) + return false; + + if (!visitor.accept(&pro)) + return false; + + evaluateProFile(visitor, varMap); + + return true; +} + +QT_END_NAMESPACE diff --git a/tools/linguist/shared/profileevaluator.h b/tools/linguist/shared/profileevaluator.h new file mode 100644 index 0000000..ae1422a --- /dev/null +++ b/tools/linguist/shared/profileevaluator.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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> + +QT_BEGIN_NAMESPACE + +class ProFile; +class ProFileEvaluator; + +void evaluateProFile(const ProFileEvaluator &visitor, QHash<QByteArray, QStringList> *varMap); +bool evaluateProFile(const QString &fileName, bool verbose, QHash<QByteArray, QStringList> *varMap); + +class ProFileEvaluator +{ +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; + 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: + class Private; + Private *d; +}; + +QT_END_NAMESPACE + +#endif // PROFILEEVALUATOR_H diff --git a/tools/linguist/shared/proitems.cpp b/tools/linguist/shared/proitems.cpp new file mode 100644 index 0000000..1895852 --- /dev/null +++ b/tools/linguist/shared/proitems.cpp @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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; +} + +ProBlock::~ProBlock() +{ + qDeleteAll(m_proitems); +} + +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; +} + +bool ProBlock::Accept(AbstractProItemVisitor *visitor) +{ + visitor->visitBeginProBlock(this); + foreach (ProItem *item, m_proitems) { + if (!item->Accept(visitor)) + return false; + } + return visitor->visitEndProBlock(this); +} + +// --------------- 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; +} + +bool ProVariable::Accept(AbstractProItemVisitor *visitor) +{ + visitor->visitBeginProVariable(this); + foreach (ProItem *item, m_proitems) { + if (!item->Accept(visitor)) + return false; + } + return visitor->visitEndProVariable(this); +} + +// --------------- 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; +} + +bool ProValue::Accept(AbstractProItemVisitor *visitor) +{ + return visitor->visitProValue(this); +} + +// --------------- 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; +} + +bool 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; +} + +bool ProCondition::Accept(AbstractProItemVisitor *visitor) +{ + return visitor->visitProCondition(this); +} + +// --------------- 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; +} + +bool ProOperator::Accept(AbstractProItemVisitor *visitor) +{ + return visitor->visitProOperator(this); +} + +// --------------- 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; +} + +bool ProFile::Accept(AbstractProItemVisitor *visitor) +{ + visitor->visitBeginProFile(this); + foreach (ProItem *item, m_proitems) { + if (!item->Accept(visitor)) + return false; + } + return visitor->visitEndProFile(this); +} + +QT_END_NAMESPACE diff --git a/tools/linguist/shared/proitems.h b/tools/linguist/shared/proitems.h new file mode 100644 index 0000000..befaa88 --- /dev/null +++ b/tools/linguist/shared/proitems.h @@ -0,0 +1,236 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PROITEMS_H +#define PROITEMS_H + +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE + +struct AbstractProItemVisitor; + +class ProItem +{ +public: + enum ProItemKind { + ValueKind, + FunctionKind, + ConditionKind, + OperatorKind, + BlockKind + }; + + ProItem() : m_lineNumber(0) {} + virtual ~ProItem() {} + + virtual ProItemKind kind() const = 0; + + void setComment(const QString &comment); + QString comment() const; + + virtual bool 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, + SingleLine = 0x10 + }; + + 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; + + ProItem::ProItemKind kind() const; + + virtual bool Accept(AbstractProItemVisitor *visitor); +protected: + QList<ProItem *> m_proitems; +private: + ProBlock *m_parent; + int m_blockKind; +}; + +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 bool 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 bool 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 bool 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 bool 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 bool Accept(AbstractProItemVisitor *visitor); +private: + OperatorKind m_operatorKind; +}; + +class ProFile : public QObject, public ProBlock +{ + Q_OBJECT + +public: + explicit ProFile(const QString &fileName); + ~ProFile(); + + QString displayFileName() const; + QString fileName() const; + QString directoryName() const; + + void setModified(bool modified); + bool isModified() const; + + virtual bool 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/tools/linguist/shared/proparser.pri b/tools/linguist/shared/proparser.pri new file mode 100644 index 0000000..372247e --- /dev/null +++ b/tools/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/tools/linguist/shared/proparserutils.h b/tools/linguist/shared/proparserutils.h new file mode 100644 index 0000000..c27c3c0 --- /dev/null +++ b/tools/linguist/shared/proparserutils.h @@ -0,0 +1,299 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PROPARSERUTILS_H +#define PROPARSERUTILS_H + +#include <QtCore/QDir> +#include <QtCore/QLibraryInfo> + +QT_BEGIN_NAMESPACE + +// 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 ®exp, + 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() + QString(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/tools/linguist/shared/qm.cpp b/tools/linguist/shared/qm.cpp new file mode 100644 index 0000000..c197e2b --- /dev/null +++ b/tools/linguist/shared/qm.cpp @@ -0,0 +1,717 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "translator.h" + +#include <QtCore/QCoreApplication> +#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 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); + } + + TranslatorMessage findMessage(const QString &context, + const QString &sourceText, const QString &comment, + const QString &fileName = QString(), int lineNumber = -1) const; + + bool save(QIODevice *iod); + + void insert(const TranslatorMessage &); + void remove(const TranslatorMessage &); + + bool contains(const QString &context, const QString &sourceText, + const QString & comment) const; + + bool contains(const QString &context, const QString &comment, + const QString &fileName, int lineNumber) const; + + void squeeze(TranslatorSaveMode mode); + + QList<TranslatorMessage> messages() const; + + bool isEmpty() const; + + 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; + + Prefix commonPrefix(const TranslatorMessage &m1, const TranslatorMessage &m2) const; + + uint msgHash(const TranslatorMessage &msg) const; + + void writeMessage(const TranslatorMessage & 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<TranslatorMessage, 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 TranslatorMessage &msg) const +{ + return elfHash(originalBytes(msg.sourceText() + msg.comment(), msg.isUtf8())); +} + +Prefix Releaser::commonPrefix(const TranslatorMessage &m1, const TranslatorMessage &m2) const +{ + 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 TranslatorMessage & msg, QDataStream & stream, + TranslatorSaveMode mode, Prefix prefix) const +{ + for (int i = 0; i < msg.translations().count(); ++i) { + QString str = msg.translations().at(i); + str.replace(QChar(Translator::DefaultVariantSeparator), + QChar(Translator::InternalVariantSeparator)); + stream << quint8(Tag_Translation) << str; + } + + if (mode == SaveEverything) + prefix = HashContextSourceTextComment; + + // lrelease produces "wrong" .qm files for QByteArrays that are .isNull(). + switch (prefix) { + default: + case HashContextSourceTextComment: + stream << quint8(Tag_Comment) << originalBytes(msg.comment(), msg.isUtf8()); + // fall through + case HashContextSourceText: + stream << quint8(Tag_SourceText) << originalBytes(msg.sourceText(), msg.isUtf8()); + // fall through + case HashContext: + stream << quint8(Tag_Context) << originalBytes(msg.context(), msg.isUtf8()); + ; + } + + 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<TranslatorMessage, 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<TranslatorMessage, 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<QString, 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, QString> hashMap; + QMap<QString, int>::const_iterator c; + for (c = contextSet.constBegin(); c != contextSet.constEnd(); ++c) + hashMap.insert(elfHash(originalBytes(c.key(), false /*FIXME*/)) % 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, QString>::const_iterator entry = hashMap.constBegin(); + while (entry != hashMap.constEnd()) { + int i = entry.key(); + hTable[i] = quint16(upto >> 1); + + do { + QString context = entry.value(); + QByteArray ba = context.toUtf8(); + const char *con = ba.data(); + uint len = uint(qstrlen(con)); + 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(); + } + } +} + +bool Releaser::contains(const QString &context, const QString &sourceText, + const QString &comment) const +{ + return !findMessage(context, sourceText, comment).translation().isNull(); +} + +bool Releaser::contains(const QString &context, const QString &comment, + const QString &fileName, int lineNumber) const +{ + return !findMessage(context, QString(), comment, fileName, lineNumber).isNull(); +} + +void Releaser::insert(const TranslatorMessage &message) +{ + m_messages.insert(message, 0); +} + +void Releaser::remove(const TranslatorMessage &message) +{ + m_messages.remove(message); +} + + +TranslatorMessage Releaser::findMessage(const QString &context, + const QString &sourceText, const QString &comment, + const QString &fileName, int lineNumber) const +{ + if (m_messages.isEmpty()) + return TranslatorMessage(); + + QMap<TranslatorMessage, void *>::const_iterator it; + + // Either we want to find an item that matches context, sourcetext + // (and optionally comment) Or we want to find an item that + // matches context, filename, linenumber (and optionally comment) + TranslatorMessage msg(context, sourceText, comment, QString(), fileName, lineNumber); + it = m_messages.constFind(msg); + if (it != m_messages.constEnd()) + return it.key(); + + if (!comment.isEmpty()) { + it = m_messages.constFind(TranslatorMessage(context, sourceText, QString(), QString(), fileName, lineNumber)); + if (it != m_messages.constEnd()) + return it.key(); + } + + it = m_messages.constFind(TranslatorMessage(context, QString(), comment, QString(), fileName, lineNumber)); + if (it != m_messages.constEnd()) + return it.key(); + if (comment.isEmpty()) + return TranslatorMessage(); + + it = m_messages.constFind(TranslatorMessage(context, QString(), QString(), QString(), fileName, lineNumber)); + if (it != m_messages.constEnd()) + return it.key(); + return TranslatorMessage(); +} + +bool Releaser::isEmpty() const +{ + return m_messageArray.isEmpty() && m_offsetArray.isEmpty() + && m_contextArray.isEmpty() && m_messages.isEmpty(); +} + +void Releaser::setNumerusRules(const QByteArray &rules) +{ + m_numerusRules = rules; +} + +QList<TranslatorMessage> Releaser::messages() const +{ + return m_messages.keys(); +} + +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]); +} + + +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; + + TranslatorMessage msg; + + // FIXME: that's just a guess, the original locale data is lost... + QTextCodec *codec = QTextCodec::codecForLocale(); + + 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::fromUtf16((const ushort *)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)); + } + str.replace(QChar(Translator::InternalVariantSeparator), + QChar(Translator::DefaultVariantSeparator)); + msg.appendTranslation(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); + msg.setSourceText(codec->toUnicode(QByteArray((const char*)m, len))); + m += len; + break; + } + case Tag_Context: { + quint32 len = read32(m); + m += 4; + //qDebug() << "CONTEXT LEN: " << len; + //qDebug() << "CONTEXT: " << QByteArray((const char*)m, len); + msg.setContext(codec->toUnicode(QByteArray((const char*)m, len))); + m += len; + break; + } + case Tag_Comment: { + quint32 len = read32(m); + m += 4; + //qDebug() << "COMMENT LEN: " << len; + //qDebug() << "COMMENT: " << QByteArray((const char*)m, len); + msg.setComment(codec->toUnicode(QByteArray((const char*)m, len))); + m += len; + break; + } + default: + //qDebug() << "UNKNOWN TAG" << tag; + break; + } + } + end:; + msg.setType(TranslatorMessage::Finished); + translator.append(msg); + //qDebug() << "\nHASH:" << hash << msg.sourceText() << msg.context(); + msg.setTranslations(QStringList()); + } + return ok; +} + + + +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)) + releaser.setNumerusRules(rules); + releaser.setCodecName(translator.codecName()); + + int finished = 0; + int unfinished = 0; + int untranslated = 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 (typ == TranslatorMessage::Unfinished) { + if (msg.translation().isEmpty()) + ++untranslated; + else + ++unfinished; + } else { + ++finished; + } + QString context = msg.context(); + QString sourceText = msg.sourceText(); + QString comment = msg.comment(); + QStringList translations = msg.translations(); + + if (!cd.ignoreUnfinished() || typ != TranslatorMessage::Unfinished) { + /* + 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). + */ + if (comment.isEmpty() + || context.isEmpty() + || translator.contains(context, sourceText, QString()) + || !releaser.findMessage(context, sourceText, QString()).translation() + .isNull() ) { + releaser.insert(msg); + } else { + TranslatorMessage tm(context, sourceText, QString(), + QString(), QString(), -1, translations); + //filename and lineNumbers will be ignored from now. + releaser.insert(tm); + } + } + } + } + + 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)\n", 0, + QCoreApplication::CodecForTr, generatedCount).arg(finished).arg(unfinished)); + if (untranslated) + cd.appendError(QCoreApplication::translate("LRelease", + " Ignored %n untranslated source text(s)\n", 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/tools/linguist/shared/qph.cpp b/tools/linguist/shared/qph.cpp new file mode 100644 index 0000000..6982f09 --- /dev/null +++ b/tools/linguist/shared/qph.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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, 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; + + 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; + } 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")) { + TranslatorMessage msg; + msg.setSourceText(m_currentSource); + msg.setTranslation(m_currentTarget); + msg.setTranslatorComment(m_currentDefinition); + translator.append(msg); + m_currentSource.clear(); + m_currentTarget.clear(); + m_currentDefinition.clear(); + } + } + return true; +} + +static bool loadQPH(Translator &translator, QIODevice &dev, ConversionData &cd) +{ + translator.setLocationsType(Translator::NoLocations); + QPHReader reader(dev, cd); + return reader.read(translator); +} + +static bool saveQPH(const Translator &translator, QIODevice &dev, ConversionData &) +{ + QTextStream t(&dev); + t << "<!DOCTYPE QPH><QPH>\n"; + foreach (const TranslatorMessage &msg, translator.messages()) { + t << "<phrase>\n"; + t << " <source>" << msg.sourceText() << "</source>\n"; + t << " <target>" << msg.translations().join(QLatin1String("@")) + << "</target>\n"; + if (!msg.context().isEmpty() || !msg.comment().isEmpty()) + t << " <definition>" << msg.context() << 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/tools/linguist/shared/simtexth.cpp b/tools/linguist/shared/simtexth.cpp new file mode 100644 index 0000000..0556ed6 --- /dev/null +++ b/tools/linguist/shared/simtexth.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/shared/simtexth.h b/tools/linguist/shared/simtexth.h new file mode 100644 index 0000000..e3cad91 --- /dev/null +++ b/tools/linguist/shared/simtexth.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/tools/linguist/shared/translator.cpp b/tools/linguist/shared/translator.cpp new file mode 100644 index 0000000..3b5e8f3 --- /dev/null +++ b/tools/linguist/shared/translator.cpp @@ -0,0 +1,559 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "translator.h" + +#include "simtexth.h" + +#include <stdio.h> + +#include <QtCore/QCoreApplication> +#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 + +Translator::Translator() : + m_codecName("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::replace(const TranslatorMessage &msg) +{ + int index = m_messages.indexOf(msg); + if (index == -1) + m_messages.append(msg); + else + m_messages[index] = msg; +} + +void Translator::replaceSorted(const TranslatorMessage &msg) +{ + int index = m_messages.indexOf(msg); + if (index == -1) + appendSorted(msg); + else + m_messages[index] = msg; +} + +void Translator::extend(const TranslatorMessage &msg) +{ + int index = m_messages.indexOf(msg); + if (index == -1) { + m_messages.append(msg); + } else { + m_messages[index].addReferenceUniq(msg.fileName(), msg.lineNumber()); + if (!msg.extraComment().isEmpty()) { + QString cmt = m_messages[index].extraComment(); + if (!cmt.isEmpty()) + cmt.append(QLatin1String("\n----------\n")); + cmt.append(msg.extraComment()); + m_messages[index].setExtraComment(cmt); + } + } +} + +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(); + 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("-")) { + 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("-")) { + 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; +} + +bool Translator::contains(const QString &context, + const QString &sourceText, const QString &comment) const +{ + return m_messages.contains(TranslatorMessage(context, sourceText, comment, + QString(), QString(), 0)); +} + +TranslatorMessage Translator::find(const QString &context, + const QString &sourceText, const QString &comment) const +{ + TranslatorMessage needle(context, sourceText, comment, QString(), QString(), 0); + int index = m_messages.indexOf(needle); + return index == -1 ? TranslatorMessage() : m_messages.at(index); +} + +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()) + return true; + return false; +} + +TranslatorMessage Translator::find(const QString &context) const +{ + foreach (const TranslatorMessage &msg, m_messages) + if (msg.context() == context && msg.sourceText().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()); + } +} + +QList<TranslatorMessage> Translator::findDuplicates() const +{ + QHash<TranslatorMessage, int> dups; + foreach (const TranslatorMessage &msg, m_messages) + dups[msg]++; + QList<TranslatorMessage> ret; + QHash<TranslatorMessage, int>::ConstIterator it = dups.constBegin(), end = dups.constEnd(); + for (; it != end; ++it) + if (it.value() > 1) + ret.append(it.key()); + return ret; +} + +// 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, + QLocale::Language language, QLocale::Country country) +{ + QStringList translations = msg.translations(); + int numTranslations = 1; + if (msg.isPlural() && language != QLocale::C) { + QStringList forms; + if (getNumerusInfo(language, country, 0, &forms)) + numTranslations = forms.count(); // includes singular + } + + // 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; +} + +QStringList Translator::normalizedTranslations(const TranslatorMessage &msg, + ConversionData &cd, bool *ok) const +{ + QLocale::Language l; + QLocale::Country c; + languageAndCountry(languageCode(), &l, &c); + QStringList translns = normalizedTranslations(msg, l, c); + if (msg.translations().size() > translns.size() && ok) { + 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.\n")); + *ok = false; + } + return translns; +} + +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()) + qWarning("No QTextCodec for %s available. Using Latin1\n", name.constData()); + m_codecName.clear(); + } else { + m_codecName = codec->name(); + } +} + +void Translator::dump() const +{ + for (int i = 0; i != messageCount(); ++i) + message(i).dump(); +} + +QT_END_NAMESPACE diff --git a/tools/linguist/shared/translator.h b/tools/linguist/shared/translator.h new file mode 100644 index 0000000..de98e9d --- /dev/null +++ b/tools/linguist/shared/translator.h @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 + +Q_DECLARE_TYPEINFO(TranslatorMessage, Q_MOVABLE_TYPE); + +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_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.join(QLatin1String("\n")); } + QStringList errors() const { return m_errors; } + void clearErrors() { m_errors.clear(); } + +public: + QString m_defaultContext; + QByteArray m_codecForSource; // CPP 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; + 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; + + bool contains(const QString &context, const QString &sourceText, + const QString &comment) const; + TranslatorMessage find(const QString &context, + const QString &sourceText, const QString &comment) 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 replace(const TranslatorMessage &msg); + 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(); + QList<TranslatorMessage> findDuplicates() const; + void makeFileNamesAbsolute(const QDir &originalPath); + + void setCodecName(const QByteArray &name); + QByteArray codecName() const { return m_codecName; } + + QString languageCode() const { return m_language; } + QString sourceLanguageCode() const { return m_sourceLanguage; } + + enum LocationsType { 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, + QLocale::Language lang, QLocale::Country country); + 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> ®isteredFileFormats(); + + enum VariantSeparators { + DefaultVariantSeparator = 0x2762, // some weird character nobody ever heard of :-D + InternalVariantSeparator = 0x9c // unicode "STRING TERMINATOR" + }; + +private: + typedef QList<TranslatorMessage> TMM; // int stores the sequence position. + + TMM m_messages; + QByteArray m_codecName; + 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); + +/* + 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/tools/linguist/shared/translatormessage.cpp b/tools/linguist/shared/translatormessage.cpp new file mode 100644 index 0000000..ab4301f --- /dev/null +++ b/tools/linguist/shared/translatormessage.cpp @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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_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_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::operator==(const TranslatorMessage& m) const +{ + static QString msgIdPlural = QLatin1String("po-msgid_plural"); + + // Special treatment for context comments (empty source). + return (m_context == m.m_context) + && m_sourcetext == m.m_sourcetext + && m_extra[msgIdPlural] == m.m_extra[msgIdPlural] + && (m_sourcetext.isEmpty() || m_comment == m.m_comment); +} + + +bool TranslatorMessage::operator<(const TranslatorMessage& 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; +} + +int qHash(const TranslatorMessage &msg) +{ + return + qHash(msg.context()) ^ + qHash(msg.sourceText()) ^ + qHash(msg.extra(QLatin1String("po-msgid_plural"))) ^ + qHash(msg.comment()); +} + +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/tools/linguist/shared/translatormessage.h b/tools/linguist/shared/translatormessage.h new file mode 100644 index 0000000..fa37ff5 --- /dev/null +++ b/tools/linguist/shared/translatormessage.h @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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; + } + + bool operator==(const TranslatorMessage& m) const; + bool operator<(const TranslatorMessage& m) const; + + 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 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_plural; +}; + +int qHash(const TranslatorMessage &msg); + +QT_END_NAMESPACE + +#endif // QT_NO_TRANSLATION + +#endif // TRANSLATORMESSAGE_H diff --git a/tools/linguist/shared/ts.cpp b/tools/linguist/shared/ts.cpp new file mode 100644 index 0000000..2e7d40f --- /dev/null +++ b/tools/linguist/shared/ts.cpp @@ -0,0 +1,755 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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::DefaultVariantSeparator); + result += readContents(); + } else { + handleError(); + break; + } + } + return result; + } else { + return readContents(); + } +} + +bool TSReader::read(Translator &translator) +{ + 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; + + 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> + translator.setCodecName(readElementText().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); + msg.setUtf8(attributes().value(strutf8) == 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/> + QXmlStreamAttributes atts = attributes(); + QString fileName = atts.value(strfilename).toString(); + if (fileName.isEmpty()) { + fileName = currentMsgFile; + } else { + if (refs.isEmpty()) + currentFile = fileName; + currentMsgFile = fileName; + } + const QString lin = atts.value(strline).toString(); + if (lin.isEmpty()) { + translator.setLocationsType(Translator::RelativeLocations); + 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); + translator.setLocationsType(Translator::RelativeLocations); + } else { + translator.setLocationsType(Translator::AbsoluteLocations); + } + 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(); + } + } // </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("""); + break; + case '&': + result += QLatin1String("&"); + break; + case '>': + result += QLatin1String(">"); + break; + case '<': + result += QLatin1String("<"); + break; + case '\'': + result += QLatin1String("'"); + 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::DefaultVariantSeparator))) >= 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::DefaultVariantSeparator), 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(); + + t << " <message"; + if (!msg.id().isEmpty()) + t << " id=\"" << msg.id() << "\""; + if (format == 11 && !trIsUtf8 && msg.isUtf8()) + t << " encoding=\"UTF-8\""; + if (format == 20 && !trIsUtf8 && msg.isUtf8()) + 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(), msg.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(), msg.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 << ">"; + QStringList translns = translator.normalizedTranslations(msg, cd, &result); + for (int j = 0; j < qMax(1, 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"; + } + t << "</context>\n"; + } + + t << "</TS>\n"; + return result; +} + +bool loadTS(Translator &translator, QIODevice &dev, ConversionData &cd) +{ + translator.setLocationsType(Translator::NoLocations); + 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/tools/linguist/shared/ts.dtd b/tools/linguist/shared/ts.dtd new file mode 100644 index 0000000..ab77f64 --- /dev/null +++ b/tools/linguist/shared/ts.dtd @@ -0,0 +1,113 @@ +<!-- + ! + ! 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. + ! The source and translation elements are optional starting with version 3.0 + ! (Qt 4.6) to support S60 blank messages. + ! + --> +<!-- + ! 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; > +<!-- If "no", then the context nesting is for informational puposes only --> +<!ATTLIST name + nest (yes|no) "yes"> +<!-- 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. + --> +<!ATTLIST message + id CDATA #IMPLIED + utf8 (true|false) "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 + plurality (nullar|singular|dual|trial|paucal|greaterpaucal|plural|greaterplural) #IMPLIED> + variants (yes|no) "no"> +<!ELEMENT lengthvariant %evilstring; > +<!-- + ! The translation variants have a priority between 1 ("highest") and 9 ("lowest") + ! Typically longer translations get a higher priority. + ! If omitted, the order of appearance of the variants in the .ts files is used. + --> +<!ATTLIST lengthvariant + priority (1|2|3|4|5|6|7|8|9) #IMPLIED> + diff --git a/tools/linguist/shared/xliff.cpp b/tools/linguist/shared/xliff.cpp new file mode 100644 index 0000000..6acf67a --- /dev/null +++ b/tools/linguist/shared/xliff.cpp @@ -0,0 +1,828 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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> + + +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("""); + break; + case '&': + result += QLatin1String("&"); + break; + case '>': + result += QLatin1String(">"); + break; + case '<': + result += QLatin1String("<"); + break; + case '\'': + result += QLatin1String("'"); + 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, + const Translator &translator, ConversionData &cd, bool *ok) +{ + static int msgid; + QString msgidstr = !msg.id().isEmpty() ? msg.id() : QString::fromAscii("_msg%1").arg(++msgid); + + QStringList translns = translator.normalizedTranslations(msg, cd, ok); + 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; + ++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, + const Translator &translator, ConversionData &cd, bool *ok) +{ + 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, translator, cd, ok); + --indent; + writeIndent(ts, indent); + ts << "</group>\n"; + } else { + writeTransUnits(ts, msg, drops, indent, translator, cd, ok); + } +} + + +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_sourceLanguage = atts.value(QLatin1String("source-language")); + } 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)) + 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; + } + 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()) { + QHash<QString, QList<TranslatorMessage> > &file = messageOrder[msg.fileName()]; + if (file.isEmpty()) + fileOrder.append(msg.fileName()); + QList<TranslatorMessage> &context = file[msg.context()]; + if (context.isEmpty()) + contextOrder[msg.fileName()].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); + foreach (const QString &fn, fileOrder) { + writeIndent(ts, indent); + ts << "<file original=\"" << fn << "\"" + << " datatype=\"" << dataType(messageOrder[fn].begin()->first()) << "\"" + << " source-language=\"" + << (translator.sourceLanguageCode().isEmpty() ? + QByteArray("en") : translator.sourceLanguageCode().toLatin1()) << "\"" + << " target-language=\"" << translator.languageCode() << "\"" + << "><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, translator, cd, &ok); + + 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/tools/linguist/tests/data/main.cpp b/tools/linguist/tests/data/main.cpp new file mode 100644 index 0000000..ebbda0a --- /dev/null +++ b/tools/linguist/tests/data/main.cpp @@ -0,0 +1,35 @@ + +#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/tools/linguist/tests/data/test.pro b/tools/linguist/tests/data/test.pro new file mode 100644 index 0000000..90e5704 --- /dev/null +++ b/tools/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/tools/linguist/tests/tests.pro b/tools/linguist/tests/tests.pro new file mode 100644 index 0000000..a67725c --- /dev/null +++ b/tools/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/tools/linguist/tests/tst_linguist.cpp b/tools/linguist/tests/tst_linguist.cpp new file mode 100644 index 0000000..199ddbb --- /dev/null +++ b/tools/linguist/tests/tst_linguist.cpp @@ -0,0 +1,4 @@ +#include "tst_linguist.h" +#include "moc_tst_linguist.cpp" + +QTEST_MAIN(tst_linguist) diff --git a/tools/linguist/tests/tst_linguist.h b/tools/linguist/tests/tst_linguist.h new file mode 100644 index 0000000..27a53bb --- /dev/null +++ b/tools/linguist/tests/tst_linguist.h @@ -0,0 +1,22 @@ +#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/tools/linguist/tests/tst_lupdate.cpp b/tools/linguist/tests/tst_lupdate.cpp new file mode 100644 index 0000000..043e653 --- /dev/null +++ b/tools/linguist/tests/tst_lupdate.cpp @@ -0,0 +1,165 @@ + +/**************************************************************************** + ** + ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#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" << "Á"; + + // 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/tools/linguist/tests/tst_simtexth.cpp b/tools/linguist/tests/tst_simtexth.cpp new file mode 100644 index 0000000..81a7f19 --- /dev/null +++ b/tools/linguist/tests/tst_simtexth.cpp @@ -0,0 +1,43 @@ + +/**************************************************************************** + ** + ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#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; +} |