c6c88ea8aa1f50ba4620d726e2943c2ec9854aba
[profile/ivi/qtbase.git] / src / corelib / tools / qtextboundaryfinder.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include <QtCore/qtextboundaryfinder.h>
42 #include <QtCore/qvarlengtharray.h>
43
44 #include <private/qunicodetables_p.h>
45 #include <private/qunicodetools_p.h>
46
47 QT_BEGIN_NAMESPACE
48
49 class QTextBoundaryFinderPrivate
50 {
51 public:
52     HB_CharAttributes attributes[1];
53 };
54
55 static void init(QTextBoundaryFinder::BoundaryType type, const QChar *chars, int length, HB_CharAttributes *attributes)
56 {
57     QVarLengthArray<HB_ScriptItem> scriptItems;
58
59     const ushort *string = reinterpret_cast<const ushort *>(chars);
60     const ushort *unicode = string;
61     // correctly assign script, isTab and isObject to the script analysis
62     const ushort *uc = unicode;
63     const ushort *e = uc + length;
64     int script = QUnicodeTables::Common;
65     int lastScript = QUnicodeTables::Common;
66     const ushort *start = uc;
67     while (uc < e) {
68         int s = QUnicodeTables::script(*uc);
69         if (s != QUnicodeTables::Inherited)
70             script = s;
71         if (*uc == QChar::ObjectReplacementCharacter || *uc == QChar::LineSeparator || *uc == 9) 
72             script = QUnicodeTables::Common;
73         if (script != lastScript) {
74             if (uc != start) {
75                 HB_ScriptItem item;
76                 item.pos = start - string;
77                 item.length = uc - start;
78                 item.script = (HB_Script)lastScript;
79                 item.bidiLevel = 0; // ### what's the proper value?
80                 scriptItems.append(item);
81                 start = uc;
82             }
83             lastScript = script;
84         }
85         ++uc;
86     }
87     if (uc != start) {
88         HB_ScriptItem item;
89         item.pos = start - string;
90         item.length = uc - start;
91         item.script = (HB_Script)lastScript;
92         item.bidiLevel = 0; // ### what's the proper value?
93         scriptItems.append(item);
94     }
95
96     QCharAttributeOptions options = 0;
97     if (type == QTextBoundaryFinder::Word)
98         options |= GetWordBreaks;
99     else if (type == QTextBoundaryFinder::Sentence)
100         options |= GetSentenceBreaks;
101     qGetCharAttributes(string, length, scriptItems.data(), scriptItems.count(), attributes, options);
102 }
103
104 /*! 
105     \class QTextBoundaryFinder
106
107     \brief The QTextBoundaryFinder class provides a way of finding Unicode text boundaries in a string.
108
109     \since 4.4
110     \ingroup tools
111     \ingroup shared
112     \ingroup string-processing
113     \reentrant
114
115     QTextBoundaryFinder allows to find Unicode text boundaries in a
116     string, similar to the Unicode text boundary specification (see
117     http://www.unicode.org/reports/tr29/tr29-11.html).
118
119     QTextBoundaryFinder can operate on a QString in four possible
120     modes depending on the value of \a BoundaryType.
121
122     Units of Unicode characters that make up what the user thinks of
123     as a character or basic unit of the language are here called
124     Grapheme clusters. The two unicode characters 'A' + diaeresis do
125     for example form one grapheme cluster as the user thinks of them
126     as one character, yet it is in this case represented by two
127     unicode code points.
128
129     Word boundaries are there to locate the start and end of what a
130     language considers to be a word.
131
132     Line break boundaries give possible places where a line break
133     might happen and sentence boundaries will show the beginning and
134     end of whole sentences.
135
136     The first position in a string is always a valid boundary and
137     refers to the position before the first character. The last
138     position at the length of the string is also valid and refers
139     to the position after the last character.
140 */
141
142 /*!
143     \enum QTextBoundaryFinder::BoundaryType
144
145     \value Grapheme Finds a grapheme which is the smallest boundary. It
146     including letters, punctation marks, numerals and more.
147     \value Word Finds a word.
148     \value Line Finds possible positions for breaking the text into multiple
149     lines.
150     \value Sentence Finds sentence boundaries. These include periods, question
151     marks etc.
152 */
153
154 /*!
155   \enum QTextBoundaryFinder::BoundaryReason
156
157   \value NotAtBoundary  The boundary finder is not at a boundary position.
158   \value StartWord  The boundary finder is at the start of a word.
159   \value EndWord  The boundary finder is at the end of a word.
160 */
161
162 /*!
163   Constructs an invalid QTextBoundaryFinder object.
164 */
165 QTextBoundaryFinder::QTextBoundaryFinder()
166     : t(Grapheme)
167     , chars(0)
168     , length(0)
169     , freePrivate(true)
170     , d(0)
171 {
172 }
173
174 /*!
175   Copies the QTextBoundaryFinder object, \a other.
176 */
177 QTextBoundaryFinder::QTextBoundaryFinder(const QTextBoundaryFinder &other)
178     : t(other.t)
179     , s(other.s)
180     , chars(other.chars)
181     , length(other.length)
182     , pos(other.pos)
183     , freePrivate(true)
184 {
185     d = (QTextBoundaryFinderPrivate *) malloc(length*sizeof(HB_CharAttributes));
186     Q_CHECK_PTR(d);
187     memcpy(d, other.d, length*sizeof(HB_CharAttributes));
188 }
189
190 /*!
191   Assigns the object, \a other, to another QTextBoundaryFinder object.
192 */
193 QTextBoundaryFinder &QTextBoundaryFinder::operator=(const QTextBoundaryFinder &other)
194 {
195     if (&other == this)
196         return *this;
197
198     t = other.t;
199     s = other.s;
200     chars = other.chars;
201     length = other.length;
202     pos = other.pos;
203
204     QTextBoundaryFinderPrivate *newD = (QTextBoundaryFinderPrivate *)
205         realloc(freePrivate ? d : 0, length*sizeof(HB_CharAttributes));
206     Q_CHECK_PTR(newD);
207     freePrivate = true;
208     d = newD;
209     memcpy(d, other.d, length*sizeof(HB_CharAttributes));
210
211     return *this;
212 }
213
214 /*!
215   Destructs the QTextBoundaryFinder object.
216 */
217 QTextBoundaryFinder::~QTextBoundaryFinder()
218 {
219     if (freePrivate)
220         free(d);
221 }
222
223 /*!
224   Creates a QTextBoundaryFinder object of \a type operating on \a string.
225 */
226 QTextBoundaryFinder::QTextBoundaryFinder(BoundaryType type, const QString &string)
227     : t(type)
228     , s(string)
229     , chars(string.unicode())
230     , length(string.length())
231     , pos(0)
232     , freePrivate(true)
233 {
234     d = (QTextBoundaryFinderPrivate *) malloc(length*sizeof(HB_CharAttributes));
235     Q_CHECK_PTR(d);
236     init(t, chars, length, d->attributes);
237 }
238
239 /*!
240   Creates a QTextBoundaryFinder object of \a type operating on \a chars
241   with \a length.
242
243   \a buffer is an optional working buffer of size \a bufferSize you can pass to
244   the QTextBoundaryFinder. If the buffer is large enough to hold the working
245   data required, it will use this instead of allocating its own buffer.
246
247   \warning QTextBoundaryFinder does not create a copy of \a chars. It is the
248   application programmer's responsibility to ensure the array is allocated for
249   as long as the QTextBoundaryFinder object stays alive. The same applies to
250   \a buffer.
251 */
252 QTextBoundaryFinder::QTextBoundaryFinder(BoundaryType type, const QChar *chars, int length, unsigned char *buffer, int bufferSize)
253     : t(type)
254     , chars(chars)
255     , length(length)
256     , pos(0)
257 {
258     if (buffer && (uint)bufferSize >= length*sizeof(HB_CharAttributes)) {
259         d = (QTextBoundaryFinderPrivate *)buffer;
260         freePrivate = false;
261     } else {
262         d = (QTextBoundaryFinderPrivate *) malloc(length*sizeof(HB_CharAttributes));
263         Q_CHECK_PTR(d);
264         freePrivate = true;
265     }
266     init(t, chars, length, d->attributes);
267 }
268
269 /*!
270   Moves the finder to the start of the string. This is equivalent to setPosition(0).
271
272   \sa setPosition(), position()
273 */
274 void QTextBoundaryFinder::toStart()
275 {
276     pos = 0;
277 }
278
279 /*!
280   Moves the finder to the end of the string. This is equivalent to setPosition(string.length()).
281
282   \sa setPosition(), position()
283 */
284 void QTextBoundaryFinder::toEnd()
285 {
286     pos = length;
287 }
288
289 /*!
290   Returns the current position of the QTextBoundaryFinder.
291
292   The range is from 0 (the beginning of the string) to the length of
293   the string inclusive.
294
295   \sa setPosition()
296 */
297 int QTextBoundaryFinder::position() const
298 {
299     return pos;
300 }
301
302 /*!
303   Sets the current position of the QTextBoundaryFinder to \a position.
304
305   If \a position is out of bounds, it will be bound to only valid
306   positions. In this case, valid positions are from 0 to the length of
307   the string inclusive.
308
309   \sa position()
310 */
311 void QTextBoundaryFinder::setPosition(int position)
312 {
313     pos = qBound(0, position, length);
314 }
315
316 /*! \fn QTextBoundaryFinder::BoundaryType QTextBoundaryFinder::type() const
317
318   Returns the type of the QTextBoundaryFinder.
319 */
320
321 /*! \fn bool QTextBoundaryFinder::isValid() const
322
323    Returns true if the text boundary finder is valid; otherwise returns false.
324    A default QTextBoundaryFinder is invalid.
325 */
326
327 /*!
328   Returns the string  the QTextBoundaryFinder object operates on.
329 */
330 QString QTextBoundaryFinder::string() const
331 {
332     if (chars == s.unicode() && length == s.length())
333         return s;
334     return QString(chars, length);
335 }
336
337
338 /*!
339   Moves the QTextBoundaryFinder to the next boundary position and returns that position.
340
341   Returns -1 if there is no next boundary.
342 */
343 int QTextBoundaryFinder::toNextBoundary()
344 {
345     if (!d) {
346         pos = -1;
347         return pos;
348     }
349
350     if (pos < 0 || pos >= length) {
351         pos = -1;
352         return pos;
353     }
354     ++pos;
355     if (pos == length)
356         return pos;
357     
358     switch(t) {
359     case Grapheme:
360         while (pos < length && !d->attributes[pos].charStop)
361             ++pos;
362         break;
363     case Word:
364         while (pos < length && !d->attributes[pos].wordBoundary)
365             ++pos;
366         break;
367     case Sentence:
368         while (pos < length && !d->attributes[pos].sentenceBoundary)
369             ++pos;
370         break;
371     case Line:
372         Q_ASSERT(pos);
373         while (pos < length && d->attributes[pos-1].lineBreakType < HB_Break)
374             ++pos;
375         break;
376     }
377
378     return pos;
379 }
380
381 /*!
382   Moves the QTextBoundaryFinder to the previous boundary position and returns that position.
383
384   Returns -1 if there is no previous boundary.
385 */
386 int QTextBoundaryFinder::toPreviousBoundary()
387 {
388     if (!d) {
389         pos = -1;
390         return pos;
391     }
392
393     if (pos <= 0 || pos > length) {
394         pos = -1;
395         return pos;
396     }
397     --pos;
398     if (pos == 0)
399         return pos;
400
401     switch(t) {
402     case Grapheme:
403         while (pos > 0 && !d->attributes[pos].charStop)
404             --pos;
405         break;
406     case Word:
407         while (pos > 0 && !d->attributes[pos].wordBoundary)
408             --pos;
409         break;
410     case Sentence:
411         while (pos > 0 && !d->attributes[pos].sentenceBoundary)
412             --pos;
413         break;
414     case Line:
415         while (pos > 0 && d->attributes[pos-1].lineBreakType < HB_Break)
416             --pos;
417         break;
418     }
419
420     return pos;
421 }
422
423 /*!
424   Returns true if the object's position() is currently at a valid text boundary.
425 */
426 bool QTextBoundaryFinder::isAtBoundary() const
427 {
428     if (!d || pos < 0)
429         return false;
430
431     if (pos == length)
432         return true;
433
434     switch(t) {
435     case Grapheme:
436         return d->attributes[pos].charStop;
437     case Word:
438         return d->attributes[pos].wordBoundary;
439     case Line:
440         return (pos > 0) ? d->attributes[pos-1].lineBreakType >= HB_Break : true;
441     case Sentence:
442         return d->attributes[pos].sentenceBoundary;
443     }
444     return false;
445 }
446
447 /*!
448   Returns the reasons for the boundary finder to have chosen the current position as a boundary.
449 */
450 QTextBoundaryFinder::BoundaryReasons QTextBoundaryFinder::boundaryReasons() const
451 {
452     if (!d)
453         return NotAtBoundary;
454     if (! isAtBoundary())
455         return NotAtBoundary;
456     if (pos == 0) {
457         if (d->attributes[pos].whiteSpace)
458             return NotAtBoundary;
459         return StartWord;
460     }
461     if (pos == length) {
462         if (d->attributes[length-1].whiteSpace)
463             return NotAtBoundary;
464         return EndWord;
465     }
466
467     const bool nextIsSpace = d->attributes[pos].whiteSpace;
468     const bool prevIsSpace = d->attributes[pos - 1].whiteSpace;
469
470     if (prevIsSpace && !nextIsSpace)
471         return StartWord;
472     else if (!prevIsSpace && nextIsSpace)
473         return EndWord;
474     else if (!prevIsSpace && !nextIsSpace)
475         return BoundaryReasons(StartWord | EndWord);
476     else
477         return NotAtBoundary;
478 }
479
480 QT_END_NAMESPACE