~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to tools/linguist/shared/translator.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
 
4
**
 
5
** This file is part of the linguist application of the Qt Toolkit.
 
6
**
 
7
** This file may be distributed under the terms of the Q Public License
 
8
** as defined by Trolltech AS of Norway and appearing in the file
 
9
** LICENSE.QPL included in the packaging of this file.
 
10
**
 
11
** This file may be distributed and/or modified under the terms of the
 
12
** GNU General Public License version 2 as published by the Free Software
 
13
** Foundation and appearing in the file LICENSE.GPL included in the
 
14
** packaging of this file.
 
15
**
 
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
 
17
**   information about Qt Commercial License Agreements.
 
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
 
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
 
20
**
 
21
** Contact info@trolltech.com if any conditions of this licensing are
 
22
** not clear to you.
 
23
**
 
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
26
**
 
27
****************************************************************************/
 
28
 
 
29
#include "qplatformdefs.h"
 
30
 
 
31
#include "translator.h"
 
32
 
 
33
#ifndef QT_NO_TRANSLATION
 
34
 
 
35
#include "qfileinfo.h"
 
36
#include "qstring.h"
 
37
#include "qcoreapplication.h"
 
38
#include "qdatastream.h"
 
39
#include "qfile.h"
 
40
#include "qmap.h"
 
41
#include "qalgorithms.h"
 
42
#include "qhash.h"
 
43
#include "qglobal.h"
 
44
 
 
45
#if defined(Q_OS_UNIX)
 
46
#define QT_USE_MMAP
 
47
#endif
 
48
 
 
49
// most of the headers below are already included in qplatformdefs.h
 
50
// also this lacks Large File support but that's probably irrelevant
 
51
#if defined(QT_USE_MMAP)
 
52
// for mmap
 
53
#include <sys/mman.h>
 
54
#include <errno.h>
 
55
#endif
 
56
 
 
57
#include <stdlib.h>
 
58
 
 
59
/*
 
60
$ mcookie
 
61
3cb86418caef9c95cd211cbf60a1bddd
 
62
$
 
63
*/
 
64
 
 
65
// magic number for the file
 
66
static const int MagicLength = 16;
 
67
static const uchar magic[MagicLength] = {
 
68
    0x3c, 0xb8, 0x64, 0x18, 0xca, 0xef, 0x9c, 0x95,
 
69
    0xcd, 0x21, 0x1c, 0xbf, 0x60, 0xa1, 0xbd, 0xdd
 
70
};
 
71
 
 
72
static bool match(const char* found, const char* target)
 
73
{
 
74
    // 0 means anything, "" means empty
 
75
    return found == 0 || qstrcmp(found, target) == 0;
 
76
}
 
77
 
 
78
#if defined(Q_C_CALLBACKS)
 
79
extern "C" {
 
80
#endif
 
81
 
 
82
/*
 
83
  Yes, unfortunately, we have code here that depends on endianness.
 
84
  The candidate is big endian (it comes from a .qm file) whereas the
 
85
  target endianness depends on the system Qt is running on.
 
86
*/
 
87
#ifdef Q_OS_TEMP
 
88
static int __cdecl cmp_uint32_little(const void* target, const void* candidate)
 
89
#else
 
90
static int cmp_uint32_little(const void* target, const void* candidate)
 
91
#endif
 
92
{
 
93
    const uchar* t = (const uchar*) target;
 
94
    const uchar* c = (const uchar*) candidate;
 
95
    return t[3] != c[0] ? (int) t[3] - (int) c[0]
 
96
         : t[2] != c[1] ? (int) t[2] - (int) c[1]
 
97
         : t[1] != c[2] ? (int) t[1] - (int) c[2]
 
98
                   : (int) t[0] - (int) c[3];
 
99
}
 
100
 
 
101
#ifdef Q_OS_TEMP
 
102
static int __cdecl cmp_uint32_big(const void* target, const void* candidate)
 
103
#else
 
104
static int cmp_uint32_big(const void* target, const void* candidate)
 
105
#endif
 
106
{
 
107
    const uint* t = (const uint*) target;
 
108
    const uint* c = (const uint*) candidate;
 
109
    return (*t > *c ? 1 : (*t == *c ? 0 : -1));
 
110
}
 
111
 
 
112
#if defined(Q_C_CALLBACKS)
 
113
}
 
114
#endif
 
115
 
 
116
static uint elfHash(const char * name)
 
117
{
 
118
    const uchar *k;
 
119
    uint h = 0;
 
120
    uint g;
 
121
 
 
122
    if (name) {
 
123
        k = (const uchar *) name;
 
124
        while (*k) {
 
125
            h = (h << 4) + *k++;
 
126
            if ((g = (h & 0xf0000000)) != 0)
 
127
                h ^= g >> 24;
 
128
            h &= ~g;
 
129
        }
 
130
    }
 
131
    if (!h)
 
132
        h = 1;
 
133
    return h;
 
134
}
 
135
 
 
136
extern bool qt_detectRTLLanguage();
 
137
 
 
138
class TranslatorPrivate
 
139
{
 
140
public:
 
141
    struct Offset {
 
142
        Offset()
 
143
            : h(0), o(0) { }
 
144
        Offset(const TranslatorMessage& m, int offset)
 
145
            : h(m.hash()), o(offset) { }
 
146
 
 
147
        bool operator<(const Offset &other) const {
 
148
            return (h != other.h) ? h < other.h : o < other.o;
 
149
        }
 
150
        bool operator==(const Offset &other) const {
 
151
            return h == other.h && o == other.o;
 
152
        }
 
153
        uint h;
 
154
        uint o;
 
155
    };
 
156
 
 
157
    enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69 };
 
158
 
 
159
    TranslatorPrivate(Translator *qq) : q(qq), unmapPointer(0), unmapLength(0) {}
 
160
    // Translator must finalize this before deallocating it
 
161
 
 
162
    Translator *q;
 
163
    // for mmap'ed files, this is what needs to be unmapped.
 
164
    char *unmapPointer;
 
165
    unsigned int unmapLength;
 
166
 
 
167
    // for squeezed but non-file data, this is what needs to be deleted
 
168
    QByteArray messageArray;
 
169
    QByteArray offsetArray;
 
170
    QByteArray contextArray;
 
171
 
 
172
#ifndef QT_NO_TRANSLATION_BUILDER
 
173
    QMap<TranslatorMessage, void *> messages;
 
174
#endif
 
175
 
 
176
    bool do_load(const uchar *data, int len);
 
177
 
 
178
};
 
179
 
 
180
 
 
181
/*!
 
182
    \class Translator
 
183
 
 
184
    \brief The Translator class provides internationalization support for text
 
185
    output.
 
186
 
 
187
    \ingroup i18n
 
188
    \ingroup environment
 
189
    \mainclass
 
190
 
 
191
    An object of this class contains a set of TranslatorMessage
 
192
    objects, each of which specifies a translation from a source
 
193
    language to a target language. Translator provides functions to
 
194
    look up translations, add new ones, remove them, load and save
 
195
    them, etc.
 
196
 
 
197
    The most common use of Translator is to: load a translator file
 
198
    created with \l{Qt Linguist Manual}, install it using
 
199
    QApplication::installTranslator(), and use it via QObject::tr().
 
200
    For example:
 
201
 
 
202
    \code
 
203
    int main(int argc, char ** argv)
 
204
    {
 
205
        QApplication app(argc, argv);
 
206
 
 
207
        Translator translator(0);
 
208
        translator.load("french.qm", ".");
 
209
        app.installTranslator(&translator);
 
210
 
 
211
        MyWidget m;
 
212
        app.setMainWidget(&m);
 
213
        m.show();
 
214
 
 
215
        return app.exec();
 
216
    }
 
217
    \endcode
 
218
    Note that the translator must be created \e before the
 
219
    application's main window.
 
220
 
 
221
    Most applications will never need to do anything else with this
 
222
    class. The other functions provided by this class are useful for
 
223
    applications that work on translator files.
 
224
 
 
225
    We call a translation a "messsage". For this reason, translation
 
226
    files are sometimes referred to as "message files".
 
227
 
 
228
    It is possible to lookup a translation using findMessage() (as
 
229
    tr() and QApplication::translate() do) and contains(), to insert a
 
230
    new translation messsage using insert(), and to remove one using
 
231
    remove().
 
232
 
 
233
    Translation tools often need more information than the bare source
 
234
    text and translation, for example, context information to help
 
235
    the translator. But end-user programs that are using translations
 
236
    usually only need lookup. To cater for these different needs,
 
237
    Translator can use stripped translator files that use the minimum
 
238
    of memory and which support little more functionality than
 
239
    findMessage().
 
240
 
 
241
    Thus, load() may not load enough information to make anything more
 
242
    than findMessage() work. save() has an argument indicating
 
243
    whether to save just this minimum of information or to save
 
244
    everything.
 
245
 
 
246
    "Everything" means that for each translation item the following
 
247
    information is kept:
 
248
 
 
249
    \list
 
250
    \i The \e {translated text} - the return value from tr().
 
251
    \i The input key:
 
252
        \list
 
253
        \i The \e {source text} - usually the argument to tr().
 
254
        \i The \e context - usually the class name for the tr() caller.
 
255
        \i The \e comment - a comment that helps disambiguate different uses
 
256
           of the same text in the same context.
 
257
        \endlist
 
258
    \endlist
 
259
 
 
260
    The minimum for each item is just the information necessary for
 
261
    findMessage() to return the right text. This may include the
 
262
    source, context and comment, but usually it is just a hash value
 
263
    and the translated text.
 
264
 
 
265
    For example, the "Cancel" in a dialog might have "Anuluj" when the
 
266
    program runs in Polish (in this case the source text would be
 
267
    "Cancel"). The context would (normally) be the dialog's class
 
268
    name; there would normally be no comment, and the translated text
 
269
    would be "Anuluj".
 
270
 
 
271
    But it's not always so simple. The Spanish version of a printer
 
272
    dialog with settings for two-sided printing and binding would
 
273
    probably require both "Activado" and "Activada" as translations
 
274
    for "Enabled". In this case the source text would be "Enabled" in
 
275
    both cases, and the context would be the dialog's class name, but
 
276
    the two items would have disambiguating comments such as
 
277
    "two-sided printing" for one and "binding" for the other. The
 
278
    comment enables the translator to choose the appropriate gender
 
279
    for the Spanish version, and enables Qt to distinguish between
 
280
    translations.
 
281
 
 
282
    Note that when Translator loads a stripped file, most functions
 
283
    do not work. The functions that do work with stripped files are
 
284
    explicitly documented as such.
 
285
 
 
286
    \sa TranslatorMessage QApplication::installTranslator()
 
287
    QApplication::removeTranslator() QObject::tr() QApplication::translate()
 
288
*/
 
289
 
 
290
/*!
 
291
    \enum Translator::SaveMode
 
292
 
 
293
    This enum type defines how Translator writes translation
 
294
    files. There are two modes:
 
295
 
 
296
    \value Everything  files are saved with all available information
 
297
    \value Stripped  files are saved with just enough information for
 
298
        end-user applications
 
299
 
 
300
    Note that when Translator loads a stripped file, most functions do
 
301
    not work. The functions that do work with stripped files are
 
302
    explicitly documented as such.
 
303
*/
 
304
 
 
305
/*!
 
306
    Constructs an empty message file object with parent \a parent that
 
307
    is not connected to any file.
 
308
*/
 
309
 
 
310
Translator::Translator(QObject * parent)
 
311
    : QTranslator(parent)
 
312
{
 
313
    d = new TranslatorPrivate(this);
 
314
}
 
315
 
 
316
/*!
 
317
    Destroys the object and frees any allocated resources.
 
318
*/
 
319
 
 
320
Translator::~Translator()
 
321
{
 
322
    if (QCoreApplication::instance())
 
323
        QCoreApplication::instance()->removeTranslator(this);
 
324
    clear();
 
325
    delete d;
 
326
}
 
327
 
 
328
/*!
 
329
    Loads \a filename + \a suffix (".qm" if the \a suffix is
 
330
    not specified), which may be an absolute file name or relative
 
331
    to \a directory. The previous contents of this translator object
 
332
    is discarded.
 
333
 
 
334
    If the file name does not exist, other file names are tried
 
335
    in the following order:
 
336
 
 
337
    \list 1
 
338
    \i File name without \a suffix appended.
 
339
    \i File name with text after a character in \a search_delimiters
 
340
       stripped ("_." is the default for \a search_delimiters if it is
 
341
       an empty string) and \a suffix.
 
342
    \i File name stripped without \a suffix appended.
 
343
    \i File name stripped further, etc.
 
344
    \endlist
 
345
 
 
346
    For example, an application running in the fr_CA locale
 
347
    (French-speaking Canada) might call load("foo.fr_ca",
 
348
    "/opt/foolib"). load() would then try to open the first existing
 
349
    readable file from this list:
 
350
 
 
351
    \list 1
 
352
    \i /opt/foolib/foo.fr_ca.qm
 
353
    \i /opt/foolib/foo.fr_ca
 
354
    \i /opt/foolib/foo.fr.qm
 
355
    \i /opt/foolib/foo.fr
 
356
    \i /opt/foolib/foo.qm
 
357
    \i /opt/foolib/foo
 
358
    \endlist
 
359
 
 
360
    \sa save()
 
361
*/
 
362
 
 
363
bool Translator::load(const QString & filename, const QString & directory,
 
364
                       const QString & search_delimiters,
 
365
                       const QString & suffix)
 
366
{
 
367
    clear();
 
368
 
 
369
    QString prefix;
 
370
 
 
371
    if (filename[0] == QLatin1Char('/')
 
372
#ifdef Q_WS_WIN
 
373
         || (filename[0].isLetter() && filename[1] == QLatin1Char(':')) || filename[0] == QLatin1Char('\\')
 
374
#endif
 
375
        )
 
376
        prefix = QLatin1String("");
 
377
    else
 
378
        prefix = directory;
 
379
 
 
380
    if (prefix.length()) {
 
381
        if (prefix[int(prefix.length()-1)] != QLatin1Char('/'))
 
382
            prefix += QLatin1Char('/');
 
383
    }
 
384
 
 
385
    QString fname = filename;
 
386
    QString realname;
 
387
    QString delims;
 
388
    delims = search_delimiters.isNull() ? QString::fromLatin1("_.") : search_delimiters;
 
389
 
 
390
    for (;;) {
 
391
        QFileInfo fi;
 
392
 
 
393
        realname = prefix + fname + (suffix.isNull() ? QString::fromLatin1(".qm") : suffix);
 
394
        fi.setFile(realname);
 
395
        if (fi.isReadable())
 
396
            break;
 
397
 
 
398
        realname = prefix + fname;
 
399
        fi.setFile(realname);
 
400
        if (fi.isReadable())
 
401
            break;
 
402
 
 
403
        int rightmost = 0;
 
404
        for (int i = 0; i < (int)delims.length(); i++) {
 
405
            int k = fname.lastIndexOf(delims[i]);
 
406
            if (k > rightmost)
 
407
                rightmost = k;
 
408
        }
 
409
 
 
410
        // no truncations? fail
 
411
        if (rightmost == 0)
 
412
            return false;
 
413
 
 
414
        fname.truncate(rightmost);
 
415
    }
 
416
 
 
417
    // realname is now the fully qualified name of a readable file.
 
418
 
 
419
    bool ok = false;
 
420
 
 
421
#ifdef QT_USE_MMAP
 
422
 
 
423
#ifndef MAP_FILE
 
424
#define MAP_FILE 0
 
425
#endif
 
426
#ifndef MAP_FAILED
 
427
#define MAP_FAILED -1
 
428
#endif
 
429
 
 
430
    int fd = -1;
 
431
    if (!realname.startsWith(QLatin1String(":")))
 
432
        fd = QT_OPEN(QFile::encodeName(realname), O_RDONLY,
 
433
#if defined(Q_OS_WIN)
 
434
                 _S_IREAD | _S_IWRITE
 
435
#else
 
436
                 0666
 
437
#endif
 
438
                );
 
439
    if (fd >= 0) {
 
440
        struct stat st;
 
441
        if (!fstat(fd, &st)) {
 
442
            char *ptr;
 
443
            ptr = reinterpret_cast<char *>(
 
444
                        mmap(0, st.st_size,             // any address, whole file
 
445
                             PROT_READ,                 // read-only memory
 
446
                             MAP_FILE | MAP_PRIVATE,    // swap-backed map from file
 
447
                             fd, 0));                   // from offset 0 of fd
 
448
            if (ptr && ptr != reinterpret_cast<char *>(MAP_FAILED)) {
 
449
                d->unmapPointer = ptr;
 
450
                d->unmapLength = st.st_size;
 
451
                ok = true;
 
452
            }
 
453
        }
 
454
        ::close(fd);
 
455
    }
 
456
#endif // QT_USE_MMAP
 
457
 
 
458
    if (!ok) {
 
459
        QFile file(realname);
 
460
        if (!file.exists())
 
461
            return false;
 
462
        d->unmapLength = file.size();
 
463
        d->unmapPointer = new char[d->unmapLength];
 
464
 
 
465
        if (file.open(QIODevice::ReadOnly))
 
466
            ok = (d->unmapLength == (uint)file.read(d->unmapPointer, d->unmapLength));
 
467
 
 
468
        if (!ok) {
 
469
            delete [] d->unmapPointer;
 
470
            d->unmapPointer = 0;
 
471
            d->unmapLength = 0;
 
472
            return false;
 
473
        }
 
474
    }
 
475
 
 
476
    return d->do_load(reinterpret_cast<const uchar *>(d->unmapPointer), d->unmapLength);
 
477
}
 
478
 
 
479
/*!
 
480
  \overload
 
481
  \fn bool Translator::load(const uchar *data, int len)
 
482
 
 
483
  Loads the .qm file data \a data of length \a len into the
 
484
  translator.
 
485
 
 
486
  The data is not copied. The caller must be able to guarantee that \a data
 
487
  will not be deleted or modified.
 
488
*/
 
489
bool Translator::load(const uchar *data, int len)
 
490
{
 
491
    clear();
 
492
    return d->do_load(data, len);
 
493
}
 
494
 
 
495
bool TranslatorPrivate::do_load(const uchar *data, int len)
 
496
{
 
497
    if (len < MagicLength || memcmp(data, magic, MagicLength) != 0) {
 
498
        q->clear();
 
499
        return false;
 
500
    }
 
501
 
 
502
    QByteArray array = QByteArray::fromRawData((const char *) data, len);
 
503
    QDataStream s(&array, QIODevice::ReadOnly);
 
504
    bool ok = true;
 
505
 
 
506
    s.device()->seek(MagicLength);
 
507
 
 
508
    quint8 tag = 0;
 
509
    quint32 blockLen = 0;
 
510
    s >> tag >> blockLen;
 
511
    while (tag && blockLen) {
 
512
        if ((quint32) s.device()->pos() + blockLen > (quint32) len) {
 
513
            ok = false;
 
514
            break;
 
515
        }
 
516
 
 
517
        if (tag == TranslatorPrivate::Contexts) {
 
518
            contextArray = QByteArray(array.constData() + s.device()->pos(), blockLen);
 
519
        } else if (tag == TranslatorPrivate::Hashes) {
 
520
            offsetArray = QByteArray(array.constData() + s.device()->pos(), blockLen);
 
521
        } else if (tag == TranslatorPrivate::Messages) {
 
522
            messageArray = QByteArray(array.constData() + s.device()->pos(), blockLen);
 
523
        }
 
524
 
 
525
        if (!s.device()->seek(s.device()->pos() + blockLen)) {
 
526
            ok = false;
 
527
            break;
 
528
        }
 
529
        tag = 0;
 
530
        blockLen = 0;
 
531
        if (!s.atEnd())
 
532
            s >> tag >> blockLen;
 
533
    }
 
534
 
 
535
    return ok;
 
536
}
 
537
 
 
538
#ifndef QT_NO_TRANSLATION_BUILDER
 
539
 
 
540
/*!
 
541
    Saves this message file to \a filename, overwriting the previous
 
542
    contents of \a filename. If \a mode is \c Everything (the
 
543
    default), all the information is preserved. If \a mode is \c
 
544
    Stripped, any information that is not necessary for findMessage()
 
545
    is stripped away.
 
546
 
 
547
    \sa load()
 
548
*/
 
549
 
 
550
bool Translator::save(const QString & filename, SaveMode mode)
 
551
{
 
552
    QFile file(filename);
 
553
    if (file.open(QIODevice::WriteOnly)) {
 
554
        squeeze(mode);
 
555
 
 
556
        QDataStream s(&file);
 
557
        s.writeRawData((const char *)magic, MagicLength);
 
558
        quint8 tag;
 
559
 
 
560
        if (!d->offsetArray.isEmpty()) {
 
561
            tag = (quint8)TranslatorPrivate::Hashes;
 
562
            quint32 oas = (quint32)d->offsetArray.size();
 
563
            s << tag << oas;
 
564
            s.writeRawData(d->offsetArray, oas);
 
565
        }
 
566
        if (!d->messageArray.isEmpty()) {
 
567
            tag = (quint8)TranslatorPrivate::Messages;
 
568
            quint32 mas = (quint32)d->messageArray.size();
 
569
            s << tag << mas;
 
570
            s.writeRawData(d->messageArray, mas);
 
571
        }
 
572
        if (!d->contextArray.isEmpty()) {
 
573
            tag = (quint8)TranslatorPrivate::Contexts;
 
574
            quint32 cas = (quint32)d->contextArray.size();
 
575
            s << tag << cas;
 
576
            s.writeRawData(d->contextArray, cas);
 
577
        }
 
578
        return true;
 
579
    }
 
580
    return false;
 
581
}
 
582
 
 
583
#endif
 
584
 
 
585
/*!
 
586
    Empties this translator of all contents.
 
587
 
 
588
    This function works with stripped translator files.
 
589
*/
 
590
 
 
591
void Translator::clear()
 
592
{
 
593
    if (d->unmapPointer && d->unmapLength) {
 
594
#if defined(QT_USE_MMAP)
 
595
        munmap(d->unmapPointer, d->unmapLength);
 
596
#else
 
597
        delete [] d->unmapPointer;
 
598
#endif
 
599
        d->unmapPointer = 0;
 
600
        d->unmapLength = 0;
 
601
    }
 
602
 
 
603
    d->messageArray.clear();
 
604
    d->offsetArray.clear();
 
605
    d->contextArray.clear();
 
606
#ifndef QT_NO_TRANSLATION_BUILDER
 
607
    d->messages.clear();
 
608
#endif
 
609
 
 
610
    QEvent ev(QEvent::LanguageChange);
 
611
    QCoreApplication::sendEvent(QCoreApplication::instance(), &ev);
 
612
}
 
613
 
 
614
#ifndef QT_NO_TRANSLATION_BUILDER
 
615
 
 
616
/*!
 
617
    Converts this message file to the compact format used to store
 
618
    message files on disk.
 
619
 
 
620
    You should never need to call this directly; save() and other
 
621
    functions call it as necessary. \a mode is for internal use.
 
622
 
 
623
    \sa save() unsqueeze()
 
624
*/
 
625
 
 
626
void Translator::squeeze(SaveMode mode)
 
627
{
 
628
    if (d->messages.isEmpty()) {
 
629
        if (mode == Stripped)
 
630
            unsqueeze();
 
631
        else
 
632
            return;
 
633
    }
 
634
 
 
635
    QMap<TranslatorMessage, void *> messages = d->messages;
 
636
    clear();
 
637
 
 
638
    QMap<TranslatorPrivate::Offset, void *> offsets;
 
639
 
 
640
    QDataStream ms(&d->messageArray, QIODevice::WriteOnly);
 
641
    QMap<TranslatorMessage, void *>::const_iterator it, next;
 
642
    int cpPrev = 0, cpNext = 0;
 
643
    for (it = messages.constBegin(); it != messages.constEnd(); ++it) {
 
644
        cpPrev = cpNext;
 
645
        next = it;
 
646
        ++next;
 
647
        if (next == messages.constEnd())
 
648
            cpNext = 0;
 
649
        else
 
650
            cpNext = (int) it.key().commonPrefix(next.key());
 
651
        offsets.insert(TranslatorPrivate::Offset(it.key(), ms.device()->pos()), (void *)0);
 
652
        it.key().write(ms, mode == Stripped, (TranslatorMessage::Prefix)qMax(cpPrev, cpNext + 1));
 
653
    }
 
654
 
 
655
    QMap<TranslatorPrivate::Offset, void *>::Iterator offset;
 
656
    offset = offsets.begin();
 
657
    QDataStream ds(&d->offsetArray, QIODevice::WriteOnly);
 
658
    while (offset != offsets.end()) {
 
659
        TranslatorPrivate::Offset k = offset.key();
 
660
        ++offset;
 
661
        ds << (quint32)k.h << (quint32)k.o;
 
662
    }
 
663
 
 
664
    if (mode == Stripped) {
 
665
        QMap<QByteArray, int> contextSet;
 
666
        for (it = messages.constBegin(); it != messages.constEnd(); ++it)
 
667
            ++contextSet[it.key().context()];
 
668
 
 
669
        quint16 hTableSize;
 
670
        if (contextSet.size() < 200)
 
671
            hTableSize = (contextSet.size() < 60) ? 151 : 503;
 
672
        else if (contextSet.size() < 2500)
 
673
            hTableSize = (contextSet.size() < 750) ? 1511 : 5003;
 
674
        else
 
675
            hTableSize = (contextSet.size() < 10000) ? 15013 : 3 * contextSet.size() / 2;
 
676
 
 
677
        QMultiMap<int, const char *> hashMap;
 
678
        QMap<QByteArray, int>::const_iterator c;
 
679
        for (c = contextSet.constBegin(); c != contextSet.constEnd(); ++c)
 
680
            hashMap.insert(elfHash(c.key()) % hTableSize, c.key());
 
681
 
 
682
        /*
 
683
          The contexts found in this translator are stored in a hash
 
684
          table to provide fast lookup. The context array has the
 
685
          following format:
 
686
 
 
687
              quint16 hTableSize;
 
688
              quint16 hTable[hTableSize];
 
689
              quint8  contextPool[...];
 
690
 
 
691
          The context pool stores the contexts as Pascal strings:
 
692
 
 
693
              quint8  len;
 
694
              quint8  data[len];
 
695
 
 
696
          Let's consider the look-up of context "FunnyDialog".  A
 
697
          hash value between 0 and hTableSize - 1 is computed, say h.
 
698
          If hTable[h] is 0, "FunnyDialog" is not covered by this
 
699
          translator. Else, we check in the contextPool at offset
 
700
          2 * hTable[h] to see if "FunnyDialog" is one of the
 
701
          contexts stored there, until we find it or we meet the
 
702
          empty string.
 
703
        */
 
704
        d->contextArray.resize(2 + (hTableSize << 1));
 
705
        QDataStream t(&d->contextArray, QIODevice::WriteOnly);
 
706
 
 
707
        quint16 *hTable = new quint16[hTableSize];
 
708
        memset(hTable, 0, hTableSize * sizeof(quint16));
 
709
 
 
710
        t << hTableSize;
 
711
        t.device()->seek(2 + (hTableSize << 1));
 
712
        t << (quint16)0; // the entry at offset 0 cannot be used
 
713
        uint upto = 2;
 
714
 
 
715
        QMap<int, const char *>::const_iterator entry = hashMap.constBegin();
 
716
        while (entry != hashMap.constEnd()) {
 
717
            int i = entry.key();
 
718
            const char *con = entry.value();
 
719
            hTable[i] = (quint16)(upto >> 1);
 
720
 
 
721
            do {
 
722
                uint len = (uint)qstrlen(con);
 
723
                len = qMin(len, 255u);
 
724
                t << (quint8)len;
 
725
                t.writeRawData(con, len);
 
726
                upto += 1 + len;
 
727
                ++entry;
 
728
            } while (entry != hashMap.constEnd() && entry.key() == i);
 
729
            do {
 
730
                t << (quint8) 0; // empty string
 
731
                ++upto;
 
732
            } while ((upto & 0x1) != 0); // offsets have to be even
 
733
        }
 
734
        t.device()->seek(2);
 
735
        for (int j = 0; j < hTableSize; j++)
 
736
            t << hTable[j];
 
737
        delete [] hTable;
 
738
 
 
739
        if (upto > 131072) {
 
740
            qWarning("Translator::squeeze: Too many contexts");
 
741
            d->contextArray.clear();
 
742
        }
 
743
    }
 
744
}
 
745
 
 
746
 
 
747
/*!
 
748
    Converts this message file into an easily modifiable data
 
749
    structure, less compact than the format used in the files.
 
750
 
 
751
    You should never need to call this function; it is called by
 
752
    insert() and friends as necessary.
 
753
 
 
754
    \sa squeeze()
 
755
*/
 
756
 
 
757
void Translator::unsqueeze()
 
758
{
 
759
    if (!d->messages.isEmpty() || d->messageArray.isEmpty())
 
760
        return;
 
761
 
 
762
    QDataStream s(&d->messageArray, QIODevice::ReadOnly);
 
763
    for (;;) {
 
764
        TranslatorMessage m(s);
 
765
        if (m.hash() == 0)
 
766
            break;
 
767
        d->messages.insert(m, (void *)0);
 
768
    }
 
769
}
 
770
 
 
771
 
 
772
/*!
 
773
    Returns true if this message file contains a message with the key
 
774
    (\a context, \a sourceText, \a comment); otherwise returns false.
 
775
 
 
776
    This function works with stripped translator files.
 
777
 
 
778
    (This is is a one-liner that calls findMessage().)
 
779
*/
 
780
 
 
781
bool Translator::contains(const char* context, const char* sourceText,
 
782
                            const char* comment) const
 
783
{
 
784
    return !findMessage(context, sourceText, comment).translation().isNull();
 
785
}
 
786
 
 
787
 
 
788
/*!
 
789
    Inserts \a message into this message file.
 
790
 
 
791
    This function does \e not work with stripped translator files. It
 
792
    may appear to, but that is not dependable.
 
793
 
 
794
    \sa remove()
 
795
*/
 
796
 
 
797
void Translator::insert(const TranslatorMessage& message)
 
798
{
 
799
    unsqueeze();
 
800
    d->messages.remove(message); // safer
 
801
    d->messages.insert(message, (void *) 0);
 
802
}
 
803
 
 
804
/*!
 
805
  \fn void Translator::insert(const char *context, const char
 
806
 *sourceText, const QString &translation)
 
807
  \overload
 
808
  \obsolete
 
809
 
 
810
  Inserts the \a sourceText and \a translation into the translator
 
811
  with the given \a context.
 
812
*/
 
813
 
 
814
/*!
 
815
    Removes \a message from this translator.
 
816
 
 
817
    This function works with stripped translator files.
 
818
 
 
819
    \sa insert()
 
820
*/
 
821
 
 
822
void Translator::remove(const TranslatorMessage& message)
 
823
{
 
824
    unsqueeze();
 
825
    d->messages.remove(message);
 
826
}
 
827
 
 
828
 
 
829
/*!
 
830
  \fn void Translator::remove(const char *, const char *)
 
831
  \overload
 
832
  \obsolete
 
833
 
 
834
  Removes the translation associated to the key (\a context, \a sourceText,
 
835
  "") from this translator.
 
836
*/
 
837
#endif
 
838
 
 
839
/*!  Returns the TranslatorMessage for the key
 
840
     (\a context, \a sourceText, \a comment). If none is found,
 
841
     also tries (\a context, \a sourceText, "").
 
842
*/
 
843
 
 
844
TranslatorMessage Translator::findMessage(const char *context, const char *sourceText,
 
845
                                          const char *comment) const
 
846
{
 
847
    if (context == 0)
 
848
        context = "";
 
849
    if (sourceText == 0)
 
850
        sourceText = "";
 
851
    if (comment == 0)
 
852
        comment = "";
 
853
 
 
854
#ifndef QT_NO_TRANSLATION_BUILDER
 
855
    if (!d->messages.isEmpty()) {
 
856
        QMap<TranslatorMessage, void *>::const_iterator it;
 
857
 
 
858
        it = d->messages.find(TranslatorMessage(context, sourceText, comment));
 
859
        if (it != d->messages.constEnd())
 
860
            return it.key();
 
861
 
 
862
        if (comment[0]) {
 
863
            it = d->messages.find(TranslatorMessage(context, sourceText, ""));
 
864
            if (it != d->messages.constEnd())
 
865
                return it.key();
 
866
        }
 
867
        return TranslatorMessage();
 
868
    }
 
869
#endif
 
870
 
 
871
    if (d->offsetArray.isEmpty())
 
872
        return TranslatorMessage();
 
873
 
 
874
    /*
 
875
        Check if the context belongs to this Translator. If many
 
876
        translators are installed, this step is necessary.
 
877
    */
 
878
    if (!d->contextArray.isEmpty()) {
 
879
        quint16 hTableSize = 0;
 
880
        QDataStream t(d->contextArray);
 
881
        t >> hTableSize;
 
882
        uint g = elfHash(context) % hTableSize;
 
883
        t.device()->seek(2 + (g << 1));
 
884
        quint16 off;
 
885
        t >> off;
 
886
        if (off == 0)
 
887
            return TranslatorMessage();
 
888
        t.device()->seek(2 + (hTableSize << 1) + (off << 1));
 
889
 
 
890
        quint8 len;
 
891
        char con[256];
 
892
        for (;;) {
 
893
            t >> len;
 
894
            if (len == 0)
 
895
                return TranslatorMessage();
 
896
            t.readRawData(con, len);
 
897
            con[len] = '\0';
 
898
            if (qstrcmp(con, context) == 0)
 
899
                break;
 
900
        }
 
901
    }
 
902
 
 
903
    size_t numItems = d->offsetArray.size() / (2 * sizeof(quint32));
 
904
    if (!numItems)
 
905
        return TranslatorMessage();
 
906
 
 
907
    for (;;) {
 
908
        quint32 h = elfHash(QByteArray(sourceText) + comment);
 
909
 
 
910
        char *r = (char *) bsearch(&h, d->offsetArray, numItems,
 
911
                                   2 * sizeof(quint32),
 
912
                                   (QSysInfo::ByteOrder == QSysInfo::BigEndian) ? cmp_uint32_big
 
913
                                   : cmp_uint32_little);
 
914
        if (r != 0) {
 
915
            // go back on equal key
 
916
            while (r != d->offsetArray.constData() && cmp_uint32_big(r - 8, r) == 0)
 
917
                r -= 8;
 
918
 
 
919
            QDataStream s(d->offsetArray);
 
920
            s.device()->seek(r - d->offsetArray.constData());
 
921
 
 
922
            quint32 rh, ro;
 
923
            s >> rh >> ro;
 
924
 
 
925
            QDataStream ms(d->messageArray);
 
926
            while (rh == h) {
 
927
                ms.device()->seek(ro);
 
928
                TranslatorMessage m(ms);
 
929
                if (match(m.context(), context)
 
930
                        && match(m.sourceText(), sourceText)
 
931
                        && match(m.comment(), comment))
 
932
                    return m;
 
933
                if (s.atEnd())
 
934
                    break;
 
935
                s >> rh >> ro;
 
936
            }
 
937
        }
 
938
        if (!comment[0])
 
939
            break;
 
940
        comment = "";
 
941
    }
 
942
    return TranslatorMessage();
 
943
}
 
944
 
 
945
/*!
 
946
    Returns true if this translator is empty, otherwise returns false.
 
947
    This function works with stripped and unstripped translation files.
 
948
*/
 
949
bool Translator::isEmpty() const
 
950
{
 
951
    return !d->unmapPointer && !d->unmapLength && d->messageArray.isEmpty() &&
 
952
           d->offsetArray.isEmpty() && d->contextArray.isEmpty() && d->messages.isEmpty();
 
953
}
 
954
 
 
955
 
 
956
#ifndef QT_NO_TRANSLATION_BUILDER
 
957
 
 
958
/*!
 
959
    Returns a list of the messages in the translator. This function is
 
960
    rather slow. Because it is seldom called, it's optimized for
 
961
    simplicity and small size, rather than speed.
 
962
 
 
963
    If you want to iterate over the list, you should iterate over a
 
964
    copy, e.g.
 
965
    \code
 
966
    QList<TranslatorMessage> list = myTranslator.messages();
 
967
    QList<TranslatorMessage>::Iterator it = list.begin();
 
968
    while (it != list.end()) {
 
969
        process_message(*it);
 
970
        ++it;
 
971
    }
 
972
  \endcode
 
973
*/
 
974
 
 
975
QList<TranslatorMessage> Translator::messages() const
 
976
{
 
977
    ((Translator *) this)->unsqueeze();
 
978
    return d->messages.keys();
 
979
}
 
980
 
 
981
#endif
 
982
 
 
983
/*!
 
984
    \class TranslatorMessage
 
985
 
 
986
    \brief The TranslatorMessage class contains a translator message and its
 
987
    properties.
 
988
 
 
989
    \ingroup i18n
 
990
    \ingroup environment
 
991
 
 
992
    This class is of no interest to most applications. It is useful
 
993
    for translation tools such as \l{Qt Linguist Manual}{Qt Linguist}.
 
994
    It is provided simply to make the API complete and regular.
 
995
 
 
996
    For a Translator object, a lookup key is a triple (\e context, \e
 
997
    {source text}, \e comment) that uniquely identifies a message. An
 
998
    extended key is a quadruple (\e hash, \e context, \e {source
 
999
    text}, \e comment), where \e hash is computed from the source text
 
1000
    and the comment. Unless you plan to read and write messages
 
1001
    yourself, you need not worry about the hash value.
 
1002
 
 
1003
    TranslatorMessage stores this triple or quadruple and the relevant
 
1004
    translation if there is any.
 
1005
 
 
1006
    \sa Translator
 
1007
*/
 
1008
 
 
1009
/*!
 
1010
    Constructs a translator message with the extended key (0, 0, 0, 0)
 
1011
    and an empty string as translation.
 
1012
*/
 
1013
 
 
1014
TranslatorMessage::TranslatorMessage()
 
1015
    : h(0)
 
1016
{
 
1017
}
 
1018
 
 
1019
 
 
1020
/*!
 
1021
    Constructs an translator message with the extended key (\e h, \a
 
1022
    context, \a sourceText, \a comment), where \e h is computed from
 
1023
    \a sourceText and \a comment, and possibly with a \a translation.
 
1024
*/
 
1025
 
 
1026
TranslatorMessage::TranslatorMessage(const char * context,
 
1027
                                        const char * sourceText,
 
1028
                                        const char * comment,
 
1029
                                        const QString& translation)
 
1030
    : cx(context), st(sourceText), cm(comment), tn(translation)
 
1031
{
 
1032
    // 0 means we don't know, "" means empty
 
1033
    if (cx == (const char*)0)
 
1034
        cx = "";
 
1035
    if (st == (const char*)0)
 
1036
        st = "";
 
1037
    if (cm == (const char*)0)
 
1038
        cm = "";
 
1039
    h = elfHash(st + cm);
 
1040
}
 
1041
 
 
1042
 
 
1043
/*!
 
1044
    Constructs a translator message read from the \a stream. The
 
1045
    resulting message may have any combination of content.
 
1046
 
 
1047
    \sa Translator::save()
 
1048
*/
 
1049
 
 
1050
TranslatorMessage::TranslatorMessage(QDataStream & stream)
 
1051
    : h(0)
 
1052
{
 
1053
    QString str16;
 
1054
    char tag;
 
1055
    quint8 obs1;
 
1056
 
 
1057
    for (;;) {
 
1058
        tag = 0;
 
1059
        if (!stream.atEnd())
 
1060
            stream.readRawData(&tag, 1);
 
1061
        switch((Tag)tag) {
 
1062
        case Tag_End:
 
1063
            if (h == 0)
 
1064
                h = elfHash(st + cm);
 
1065
            return;
 
1066
        case Tag_SourceText16: // obsolete
 
1067
            stream >> str16;
 
1068
            st = str16.toLatin1();
 
1069
            break;
 
1070
        case Tag_Translation:
 
1071
            stream >> tn;
 
1072
            break;
 
1073
        case Tag_Context16: // obsolete
 
1074
            stream >> str16;
 
1075
            cx = str16.toLatin1();
 
1076
            break;
 
1077
        case Tag_Hash:
 
1078
            stream >> h;
 
1079
            break;
 
1080
        case Tag_SourceText:
 
1081
            stream >> st;
 
1082
            break;
 
1083
        case Tag_Context:
 
1084
            stream >> cx;
 
1085
            if (cx.isEmpty()) // for compatibility
 
1086
                cx = 0;
 
1087
            break;
 
1088
        case Tag_Comment:
 
1089
            stream >> cm;
 
1090
            break;
 
1091
        case Tag_Obsolete1: // obsolete
 
1092
            stream >> obs1;
 
1093
            break;
 
1094
        default:
 
1095
            h = 0;
 
1096
            st = 0;
 
1097
            cx = 0;
 
1098
            cm = 0;
 
1099
            tn.clear();
 
1100
            return;
 
1101
        }
 
1102
    }
 
1103
}
 
1104
 
 
1105
 
 
1106
/*!
 
1107
    Constructs a copy of translator message \a m.
 
1108
*/
 
1109
 
 
1110
TranslatorMessage::TranslatorMessage(const TranslatorMessage & m)
 
1111
    : cx(m.cx), st(m.st), cm(m.cm), tn(m.tn)
 
1112
{
 
1113
    h = m.h;
 
1114
}
 
1115
 
 
1116
 
 
1117
/*!
 
1118
    Assigns message \a m to this translator message and returns a
 
1119
    reference to this translator message.
 
1120
*/
 
1121
 
 
1122
TranslatorMessage & TranslatorMessage::operator=(
 
1123
        const TranslatorMessage & m)
 
1124
{
 
1125
    h = m.h;
 
1126
    cx = m.cx;
 
1127
    st = m.st;
 
1128
    cm = m.cm;
 
1129
    tn = m.tn;
 
1130
    return *this;
 
1131
}
 
1132
 
 
1133
 
 
1134
/*!
 
1135
    \fn uint TranslatorMessage::hash() const
 
1136
 
 
1137
    Returns the hash value used internally to represent the lookup
 
1138
    key. This value is zero only if this translator message was
 
1139
    constructed from a stream containing invalid data.
 
1140
 
 
1141
    The hashing function is unspecified, but it will remain unchanged
 
1142
    in future versions of Qt.
 
1143
*/
 
1144
 
 
1145
/*!
 
1146
    \fn const char *TranslatorMessage::context() const
 
1147
 
 
1148
    Returns the context for this message (e.g. "MyDialog").
 
1149
*/
 
1150
 
 
1151
/*!
 
1152
    \fn const char *TranslatorMessage::sourceText() const
 
1153
 
 
1154
    Returns the source text of this message (e.g. "&Save").
 
1155
*/
 
1156
 
 
1157
/*!
 
1158
    \fn const char *TranslatorMessage::comment() const
 
1159
 
 
1160
    Returns the comment for this message (e.g. "File|Save").
 
1161
*/
 
1162
 
 
1163
/*!
 
1164
    \fn void TranslatorMessage::setTranslation(const QString & translation)
 
1165
 
 
1166
    Sets the translation of the source text to \a translation.
 
1167
 
 
1168
    \sa translation()
 
1169
*/
 
1170
 
 
1171
/*!
 
1172
    \fn QString TranslatorMessage::translation() const
 
1173
 
 
1174
    Returns the translation of the source text (e.g., "&Sauvegarder").
 
1175
 
 
1176
    \sa setTranslation()
 
1177
*/
 
1178
 
 
1179
/*!
 
1180
    \enum TranslatorMessage::Prefix
 
1181
 
 
1182
    Let (\e h, \e c, \e s, \e m) be the extended key. The possible
 
1183
    prefixes are
 
1184
 
 
1185
    \value NoPrefix  no prefix
 
1186
    \value Hash  only (\e h)
 
1187
    \value HashContext  only (\e h, \e c)
 
1188
    \value HashContextSourceText  only (\e h, \e c, \e s)
 
1189
    \value HashContextSourceTextComment  the whole extended key, (\e
 
1190
        h, \e c, \e s, \e m)
 
1191
 
 
1192
    \sa write() commonPrefix()
 
1193
*/
 
1194
 
 
1195
/*!
 
1196
    Writes this translator message to the \a stream. If \a strip is
 
1197
    false (the default), all the information in the message is
 
1198
    written. If \a strip is true, only the part of the extended key
 
1199
    specified by \a prefix is written with the translation (\c
 
1200
    HashContextSourceTextComment by default).
 
1201
 
 
1202
    \sa commonPrefix()
 
1203
*/
 
1204
 
 
1205
void TranslatorMessage::write(QDataStream & stream, bool strip,
 
1206
                                Prefix prefix) const
 
1207
{
 
1208
    char tag;
 
1209
 
 
1210
    tag = (char)Tag_Translation;
 
1211
    stream.writeRawData(&tag, 1);
 
1212
    stream << tn;
 
1213
 
 
1214
    if (!strip)
 
1215
        prefix = HashContextSourceTextComment;
 
1216
 
 
1217
    switch (prefix) {
 
1218
    case HashContextSourceTextComment:
 
1219
        tag = (char)Tag_Comment;
 
1220
        stream.writeRawData(&tag, 1);
 
1221
        stream << cm;
 
1222
        // fall through
 
1223
    case HashContextSourceText:
 
1224
        tag = (char)Tag_SourceText;
 
1225
        stream.writeRawData(&tag, 1);
 
1226
        stream << st;
 
1227
        // fall through
 
1228
    case HashContext:
 
1229
        tag = (char)Tag_Context;
 
1230
        stream.writeRawData(&tag, 1);
 
1231
        stream << cx;
 
1232
        // fall through
 
1233
    default:
 
1234
        tag = (char)Tag_Hash;
 
1235
        stream.writeRawData(&tag, 1);
 
1236
        stream << h;
 
1237
    }
 
1238
 
 
1239
    tag = (char)Tag_End;
 
1240
    stream.writeRawData(&tag, 1);
 
1241
}
 
1242
 
 
1243
 
 
1244
/*!
 
1245
    Returns the widest lookup prefix that is common to this translator
 
1246
    message and to message \a m.
 
1247
 
 
1248
    For example, if the extended key is for this message is (71,
 
1249
    "PrintDialog", "Yes", "Print?") and that for \a m is (71,
 
1250
    "PrintDialog", "No", "Print?"), this function returns \c
 
1251
    HashContext.
 
1252
 
 
1253
    \sa write()
 
1254
*/
 
1255
 
 
1256
TranslatorMessage::Prefix TranslatorMessage::commonPrefix(
 
1257
        const TranslatorMessage& m) const
 
1258
{
 
1259
    if (h != m.h)
 
1260
        return NoPrefix;
 
1261
    if (cx != m.cx)
 
1262
        return Hash;
 
1263
    if (st != m.st)
 
1264
        return HashContext;
 
1265
    if (cm != m.cm)
 
1266
        return HashContextSourceText;
 
1267
    return HashContextSourceTextComment;
 
1268
}
 
1269
 
 
1270
 
 
1271
/*!
 
1272
 Returns true if the extended key of this object is equal to that of
 
1273
 \a m; otherwise returns false.
 
1274
*/
 
1275
 
 
1276
bool TranslatorMessage::operator==(const TranslatorMessage& m) const
 
1277
{
 
1278
    return h == m.h && cx == m.cx && st == m.st && cm == m.cm;
 
1279
}
 
1280
 
 
1281
 
 
1282
/*!
 
1283
    \fn bool TranslatorMessage::operator!=(const TranslatorMessage& m) const
 
1284
 
 
1285
    Returns true if the extended key of this object is different from
 
1286
    that of \a m; otherwise returns false.
 
1287
*/
 
1288
 
 
1289
 
 
1290
/*!
 
1291
    Returns true if the extended key of this object is
 
1292
    lexicographically before than that of \a m; otherwise returns
 
1293
    false.
 
1294
*/
 
1295
 
 
1296
bool TranslatorMessage::operator<(const TranslatorMessage& m) const
 
1297
{
 
1298
    return h != m.h ? h < m.h
 
1299
           : (cx != m.cx ? cx < m.cx
 
1300
             : (st != m.st ? st < m.st : cm < m.cm));
 
1301
}
 
1302
 
 
1303
 
 
1304
/*!
 
1305
    \fn bool TranslatorMessage::operator<=(const TranslatorMessage& m) const
 
1306
 
 
1307
    Returns true if the extended key of this object is
 
1308
    lexicographically before that of \a m or if they are equal;
 
1309
    otherwise returns false.
 
1310
*/
 
1311
 
 
1312
/*!
 
1313
    \fn bool TranslatorMessage::operator>(const TranslatorMessage& m) const
 
1314
 
 
1315
    Returns true if the extended key of this object is
 
1316
    lexicographically after that of \a m; otherwise returns false.
 
1317
*/
 
1318
 
 
1319
/*!
 
1320
    \fn bool TranslatorMessage::operator>=(const TranslatorMessage& m) const
 
1321
 
 
1322
    Returns true if the extended key of this object is
 
1323
    lexicographically after that of \a m or if they are equal;
 
1324
    otherwise returns false.
 
1325
*/
 
1326
 
 
1327
/*!
 
1328
    \fn QString Translator::find(const char *context, const char *sourceText, const char * comment) const
 
1329
 
 
1330
    Use findMessage() instead.
 
1331
*/
 
1332
 
 
1333
#endif // QT_NO_TRANSLATION