summaryrefslogtreecommitdiff
path: root/inputmethods/handwriting
Unidiff
Diffstat (limited to 'inputmethods/handwriting') (more/less context) (ignore whitespace changes)
-rw-r--r--inputmethods/handwriting/qimpenchar.cpp637
-rw-r--r--inputmethods/handwriting/qimpenchar.h2
-rw-r--r--inputmethods/handwriting/qimpencombining.cpp1
3 files changed, 322 insertions, 318 deletions
diff --git a/inputmethods/handwriting/qimpenchar.cpp b/inputmethods/handwriting/qimpenchar.cpp
index 152bfec..0c37e5c 100644
--- a/inputmethods/handwriting/qimpenchar.cpp
+++ b/inputmethods/handwriting/qimpenchar.cpp
@@ -1,505 +1,508 @@
1/********************************************************************** 1/**********************************************************************
2** Copyright (C) 2000 Trolltech AS. All rights reserved. 2 ** Copyright (C) 2000 Trolltech AS. All rights reserved.
3** 3 **
4** This file is part of Qtopia Environment. 4 ** This file is part of Qtopia Environment.
5** 5 **
6** This file may be distributed and/or modified under the terms of the 6 ** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software 7 ** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the 8 ** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file. 9 ** packaging of this file.
10** 10 **
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 11 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 12 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13** 13 **
14** See http://www.trolltech.com/gpl/ for GPL licensing information. 14 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
15** 15 **
16** Contact info@trolltech.com if any conditions of this licensing are 16 ** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you. 17 ** not clear to you.
18** 18 **
19**********************************************************************/ 19 **********************************************************************/
20 20
21#include <qfile.h> 21#include <qfile.h>
22#include <qtl.h> 22#include <qtl.h>
23#include <math.h> 23#include <math.h>
24#include <limits.h> 24#include <limits.h>
25#include <errno.h> 25#include <errno.h>
26#include <qdatastream.h> 26#include <qdatastream.h>
27#include "qimpencombining.h" 27#include "qimpencombining.h"
28#include "qimpenchar.h" 28#include "qimpenchar.h"
29 29
30 #define QIMPEN_MATCH_THRESHOLD 200000 30 #define QIMPEN_MATCH_THRESHOLD 200000
31 31
32const QIMPenSpecialKeys qimpen_specialKeys[] = { 32const QIMPenSpecialKeys qimpen_specialKeys[] = {
33 { Qt::Key_Escape, "[Esc]" }, 33 { Qt::Key_Escape, "[Esc]" },
34 { Qt::Key_Tab, "[Tab]" }, 34 { Qt::Key_Tab, "[Tab]" },
35 { Qt::Key_Backspace,"[BackSpace]" }, 35 { Qt::Key_Backspace,"[BackSpace]" },
36 { Qt::Key_Return, "[Return]" }, 36 { Qt::Key_Return, "[Return]" },
37 { QIMPenChar::Caps, "[Uppercase]" }, 37 { QIMPenChar::Caps, "[Uppercase]" },
38 { QIMPenChar::CapsLock,"[Caps Lock]" }, 38 { QIMPenChar::CapsLock,"[Caps Lock]" },
39 { QIMPenChar::Shortcut,"[Shortcut]" }, 39 { QIMPenChar::Shortcut,"[Shortcut]" },
40 { QIMPenChar::Punctuation, "[Punctuation]" }, 40 { QIMPenChar::Punctuation, "[Punctuation]" },
41 { QIMPenChar::Symbol,"[Symbol]" }, 41 { QIMPenChar::Symbol,"[Symbol]" },
42 { QIMPenChar::Extended,"[Extended]" }, 42 { QIMPenChar::Extended,"[Extended]" },
43 { Qt::Key_unknown, 0 } }; 43 { Qt::Key_unknown, 0 } };
44 44
45 45
46/*! 46/*!
47 \class QIMPenChar qimpenchar.h 47 \class QIMPenChar qimpenchar.h
48 48
49 Handles a single character. Can calculate closeness of match to 49 Handles a single character. Can calculate closeness of match to
50 another character. 50 another character.
51*/ 51 */
52 52
53QIMPenChar::QIMPenChar() 53QIMPenChar::QIMPenChar()
54{ 54{
55 flags = 0; 55 flags = 0;
56 strokes.setAutoDelete( TRUE ); 56 strokes.setAutoDelete( TRUE );
57} 57}
58 58
59QIMPenChar::QIMPenChar( const QIMPenChar &chr ) 59QIMPenChar::QIMPenChar( const QIMPenChar &chr )
60{ 60{
61 strokes.setAutoDelete( TRUE ); 61 strokes.setAutoDelete( TRUE );
62 ch = chr.ch; 62 ch = chr.ch;
63 flags = chr.flags; 63 flags = chr.flags;
64 d = chr.d; 64 d = chr.d;
65 QIMPenStrokeIterator it( chr.strokes ); 65 QIMPenStrokeIterator it( chr.strokes );
66 while ( it.current() ) { 66 while ( it.current() ) {
67 strokes.append( new QIMPenStroke( *it.current() ) ); 67 strokes.append( new QIMPenStroke( *it.current() ) );
68 ++it; 68 ++it;
69 } 69 }
70} 70}
71 71
72QIMPenChar &QIMPenChar::operator=( const QIMPenChar &chr ) 72QIMPenChar &QIMPenChar::operator=( const QIMPenChar &chr )
73{ 73{
74 strokes.clear(); 74 strokes.clear();
75 ch = chr.ch; 75 ch = chr.ch;
76 flags = chr.flags; 76 flags = chr.flags;
77 d = chr.d; 77 d = chr.d;
78 QIMPenStrokeIterator it( chr.strokes ); 78 QIMPenStrokeIterator it( chr.strokes );
79 while ( it.current() ) { 79 while ( it.current() ) {
80 strokes.append( new QIMPenStroke( *it.current() ) ); 80 strokes.append( new QIMPenStroke( *it.current() ) );
81 ++it; 81 ++it;
82 } 82 }
83 83
84 return *this; 84 return *this;
85} 85}
86 86
87QString QIMPenChar::name() const 87QString QIMPenChar::name() const
88{ 88{
89 QString n; 89 QString n;
90 90
91 if ( (ch & 0x0000FFFF) == 0 ) { 91 if ( (ch & 0x0000FFFF) == 0 ) {
92 int code = ch >> 16; 92 int code = ch >> 16;
93 for ( int i = 0; qimpen_specialKeys[i].code != Qt::Key_unknown; i++ ) { 93 for ( int i = 0; qimpen_specialKeys[i].code != Qt::Key_unknown; i++ ) {
94 if ( qimpen_specialKeys[i].code == code ) { 94 if ( qimpen_specialKeys[i].code == code ) {
95 n = qimpen_specialKeys[i].name; 95 n = qimpen_specialKeys[i].name;
96 break; 96 break;
97 } 97 }
98 }
99 } else {
100 n = QChar( ch & 0x0000FFFF );
98 } 101 }
99 } else {
100 n = QChar( ch & 0x0000FFFF );
101 }
102 102
103 return n; 103 return n;
104} 104}
105 105
106void QIMPenChar::clear() 106void QIMPenChar::clear()
107{ 107{
108 ch = 0; 108 ch = 0;
109 flags = 0; 109 flags = 0;
110 d = QString::null; 110 d = QString::null;
111 strokes.clear(); 111 strokes.clear();
112} 112}
113 113
114unsigned int QIMPenChar::strokeLength( int s ) const 114unsigned int QIMPenChar::strokeLength( int s ) const
115{ 115{
116 QIMPenStrokeIterator it( strokes ); 116 QIMPenStrokeIterator it( strokes );
117 while ( it.current() && s ) { 117 while ( it.current() && s ) {
118 ++it; 118 ++it;
119 --s; 119 --s;
120 } 120 }
121 121
122 if ( it.current() ) 122 if ( it.current() )
123 return it.current()->length(); 123 return it.current()->length();
124 124
125 return 0; 125 return 0;
126} 126}
127 127
128/*! 128/*!
129 Add a stroke to the character 129 Add a stroke to the character
130*/ 130 */
131void QIMPenChar::addStroke( QIMPenStroke *st ) 131void QIMPenChar::addStroke( QIMPenStroke *st )
132{ 132{
133 QIMPenStroke *stroke = new QIMPenStroke( *st ); 133 QIMPenStroke *stroke = new QIMPenStroke( *st );
134 strokes.append( stroke ); 134 strokes.append( stroke );
135} 135}
136 136
137/*! 137/*!
138 Return an indicator of the closeness of this character to \a pen. 138 Return an indicator of the closeness of this character to \a pen.
139 Lower value is better. 139 Lower value is better.
140*/ 140 */
141int QIMPenChar::match( QIMPenChar *pen ) 141int QIMPenChar::match( QIMPenChar *pen )
142{ 142{
143/* 143 /*
144 if ( strokes.count() > pen->strokes.count() ) 144 if ( strokes.count() > pen->strokes.count() )
145 return INT_MAX; 145 return INT_MAX;
146*/ 146 */
147 int err = 0; 147 int err = 0;
148 int maxErr = 0; 148 int maxErr = 0;
149 int diff = 0; 149 int diff = 0;
150 QIMPenStrokeIterator it1( strokes ); 150 QIMPenStrokeIterator it1( strokes );
151 QIMPenStrokeIterator it2( pen->strokes ); 151 QIMPenStrokeIterator it2( pen->strokes );
152 err = it1.current()->match( it2.current() ); 152 err = it1.current()->match( it2.current() );
153 if ( err > maxErr ) 153 if ( err > maxErr )
154 maxErr = err; 154 maxErr = err;
155 ++it1; 155 ++it1;
156 ++it2; 156 ++it2;
157 while ( err < 400000 && it1.current() && it2.current() ) { 157 while ( err < 400000 && it1.current() && it2.current() ) {
158 QPoint p1 = it1.current()->boundingRect().center() - 158 QPoint p1 = it1.current()->boundingRect().center() -
159 strokes.getFirst()->boundingRect().center(); 159 strokes.getFirst()->boundingRect().center();
160 QPoint p2 = it2.current()->boundingRect().center() - 160 QPoint p2 = it2.current()->boundingRect().center() -
161 pen->strokes.getFirst()->boundingRect().center(); 161 pen->strokes.getFirst()->boundingRect().center();
162 int xdiff = QABS( p1.x() - p2.x() ) - 6; 162 int xdiff = QABS( p1.x() - p2.x() ) - 6;
163 int ydiff = QABS( p1.y() - p2.y() ) - 5; 163 int ydiff = QABS( p1.y() - p2.y() ) - 5;
164 if ( xdiff < 0 ) 164 if ( xdiff < 0 )
165 xdiff = 0; 165 xdiff = 0;
166 if ( ydiff < 0 ) 166 if ( ydiff < 0 )
167 ydiff = 0; 167 ydiff = 0;
168 if ( xdiff > 10 || ydiff > 10 ) { // not a chance 168 if ( xdiff > 10 || ydiff > 10 ) { // not a chance
169#ifdef DEBUG_QIMPEN 169#ifdef DEBUG_QIMPEN
170 qDebug( "char %c, stroke starting pt diff excessive", pen->ch ); 170 qDebug( "char %c, stroke starting pt diff excessive", pen->ch );
171#endif 171#endif
172 return INT_MAX; 172 return INT_MAX;
173 }
174 diff += xdiff*xdiff + ydiff*ydiff;
175 err = it1.current()->match( it2.current() );
176 if ( err > maxErr )
177 maxErr = err;
178 ++it1;
179 ++it2;
173 } 180 }
174 diff += xdiff*xdiff + ydiff*ydiff;
175 err = it1.current()->match( it2.current() );
176 if ( err > maxErr )
177 maxErr = err;
178 ++it1;
179 ++it2;
180 }
181 181
182 maxErr += diff * diff * 6; // magic weighting :) 182 maxErr += diff * diff * 6; // magic weighting :)
183 183
184#ifdef DEBUG_QIMPEN 184#ifdef DEBUG_QIMPEN
185 qDebug( "char: %c, maxErr %d, diff %d, (%d)", pen->ch, maxErr, diff, strokes.count() ); 185 qDebug( "char: %c, maxErr %d, diff %d, (%d)", pen->ch, maxErr, diff, strokes.count() );
186#endif 186#endif
187 return maxErr; 187 return maxErr;
188} 188}
189 189
190/*! 190/*!
191 Return the bounding rect of this character. It may have sides with 191 Return the bounding rect of this character. It may have sides with
192 negative coords since its origin is where the user started drawing 192 negative coords since its origin is where the user started drawing
193 the character. 193 the character.
194*/ 194 */
195QRect QIMPenChar::boundingRect() 195QRect QIMPenChar::boundingRect()
196{ 196{
197 QRect br; 197 QRect br;
198 QIMPenStroke *st = strokes.first(); 198 QIMPenStroke *st = strokes.first();
199 while ( st ) { 199 while ( st ) {
200 br |= st->boundingRect(); 200 br |= st->boundingRect();
201 st = strokes.next(); 201 st = strokes.next();
202 } 202 }
203 203
204 return br; 204 return br;
205} 205}
206 206
207 207
208/*! 208/*!
209 Write the character's data to the stream. 209 Write the character's data to the stream.
210*/ 210 */
211QDataStream &operator<< (QDataStream &s, const QIMPenChar &ws) 211QDataStream &operator<< (QDataStream &s, const QIMPenChar &ws)
212{ 212{
213 s << ws.ch; 213 s << ws.ch;
214 s << ws.flags; 214 s << ws.flags;
215 if ( ws.flags & QIMPenChar::Data ) 215 if ( ws.flags & QIMPenChar::Data )
216 s << ws.d; 216 s << ws.d;
217 s << ws.strokes.count(); 217 s << ws.strokes.count();
218 QIMPenStrokeIterator it( ws.strokes ); 218 QIMPenStrokeIterator it( ws.strokes );
219 while ( it.current() ) { 219 while ( it.current() ) {
220 s << *it.current(); 220 s << *it.current();
221 ++it; 221 ++it;
222 } 222 }
223 223
224 return s; 224 return s;
225} 225}
226 226
227/*! 227/*!
228 Read the character's data from the stream. 228 Read the character's data from the stream.
229*/ 229 */
230QDataStream &operator>> (QDataStream &s, QIMPenChar &ws) 230QDataStream &operator>> (QDataStream &s, QIMPenChar &ws)
231{ 231{
232 s >> ws.ch; 232 s >> ws.ch;
233 s >> ws.flags; 233 s >> ws.flags;
234 if ( ws.flags & QIMPenChar::Data ) 234 if ( ws.flags & QIMPenChar::Data )
235 s >> ws.d; 235 s >> ws.d;
236 unsigned size; 236 unsigned size;
237 s >> size; 237 s >> size;
238 for ( unsigned i = 0; i < size; i++ ) { 238 for ( unsigned i = 0; i < size; i++ ) {
239 QIMPenStroke *st = new QIMPenStroke(); 239 QIMPenStroke *st = new QIMPenStroke();
240 s >> *st; 240 s >> *st;
241 ws.strokes.append( st ); 241 ws.strokes.append( st );
242 } 242 }
243 243
244 return s; 244 return s;
245} 245}
246 246
247//=========================================================================== 247//===========================================================================
248 248
249bool QIMPenCharMatch::operator>( const QIMPenCharMatch &m ) 249bool QIMPenCharMatch::operator>( const QIMPenCharMatch &m )
250{ 250{
251 return error > m.error; 251 return error > m.error;
252} 252}
253 253
254bool QIMPenCharMatch::operator<( const QIMPenCharMatch &m ) 254bool QIMPenCharMatch::operator<( const QIMPenCharMatch &m )
255{ 255{
256 return error < m.error; 256 return error < m.error;
257} 257}
258 258
259bool QIMPenCharMatch::operator<=( const QIMPenCharMatch &m ) 259bool QIMPenCharMatch::operator<=( const QIMPenCharMatch &m )
260{ 260{
261 return error <= m.error; 261 return error <= m.error;
262} 262}
263 263
264//=========================================================================== 264//===========================================================================
265 265
266/*! 266/*!
267 \class QIMPenCharSet qimpenchar.h 267 \class QIMPenCharSet qimpenchar.h
268 268
269 Maintains a set of related characters. 269 Maintains a set of related characters.
270*/ 270 */
271 271
272QIMPenCharSet::QIMPenCharSet() 272QIMPenCharSet::QIMPenCharSet()
273{ 273{
274 chars.setAutoDelete( TRUE ); 274 chars.setAutoDelete( TRUE );
275 desc = "Unnamed"; 275 desc = "Unnamed";
276 csTitle = "abc"; 276 csTitle = "abc";
277 csType = Unknown; 277 csType = Unknown;
278 maxStrokes = 0; 278 maxStrokes = 0;
279} 279}
280 280
281/*! 281/*!
282 Construct and load a characters set from file \a fn. 282 Construct and load a characters set from file \a fn.
283*/ 283 */
284QIMPenCharSet::QIMPenCharSet( const QString &fn ) 284QIMPenCharSet::QIMPenCharSet( const QString &fn )
285{ 285{
286 chars.setAutoDelete( TRUE ); 286 chars.setAutoDelete( TRUE );
287 desc = "Unnamed"; 287 desc = "Unnamed";
288 csTitle = "abc"; 288 csTitle = "abc";
289 csType = Unknown; 289 csType = Unknown;
290 maxStrokes = 0; 290 maxStrokes = 0;
291 load( fn, System ); 291 load( fn, System );
292} 292}
293 293
294const QString &QIMPenCharSet::filename( Domain d ) const 294const QString &QIMPenCharSet::filename( Domain d ) const
295{ 295{
296 if ( d == System ) 296 if ( d == System )
297 return sysFilename; 297 return sysFilename;
298 else 298 else
299 return userFilename; 299 return userFilename;
300} 300}
301 301
302void QIMPenCharSet::setFilename( const QString &fn, Domain d ) 302void QIMPenCharSet::setFilename( const QString &fn, Domain d )
303{ 303{
304 if ( d == System ) 304 if ( d == System )
305 sysFilename = fn; 305 sysFilename = fn;
306 else if ( d == User ) 306 else if ( d == User )
307 userFilename = fn; 307 userFilename = fn;
308} 308}
309 309
310/*! 310/*!
311 Load a character set from file \a fn. 311 Load a character set from file \a fn.
312*/ 312 */
313bool QIMPenCharSet::load( const QString &fn, Domain d ) 313bool QIMPenCharSet::load( const QString &fn, Domain d )
314{ 314{
315 setFilename( fn, d ); 315 setFilename( fn, d );
316 316
317 bool ok = FALSE; 317 bool ok = FALSE;
318 QFile file( fn ); 318 QFile file( fn );
319 if ( file.open( IO_ReadOnly ) ) { 319 if ( file.open( IO_ReadOnly ) ) {
320 QDataStream ds( &file ); 320 QDataStream ds( &file );
321 QString version; 321 QString version;
322 ds >> version; 322 ds >> version;
323 ds >> csTitle; 323 ds >> csTitle;
324 ds >> desc; 324 ds >> desc;
325 int major = version.mid( 4, 1 ).toInt(); 325 int major = version.mid( 4, 1 ).toInt();
326 int minor = version.mid( 6 ).toInt(); 326 int minor = version.mid( 6 ).toInt();
327 if ( major >= 1 && minor > 0 ) { 327 if ( major >= 1 && minor > 0 ) {
328 ds >> (Q_INT8 &)csType; 328 ds >> (Q_INT8 &)csType;
329 } else { 329 } else {
330 if ( csTitle == "abc" ) 330 if ( csTitle == "abc" )
331 csType = Lower; 331 csType = Lower;
332 else if ( csTitle == "ABC" ) 332 else if ( csTitle == "ABC" )
333 csType = Upper; 333 csType = Upper;
334 else if ( csTitle == "123" ) 334 else if ( csTitle == "123" )
335 csType = Numeric; 335 csType = Numeric;
336 else if ( fn == "Combining" ) 336 else if ( fn == "Combining" )
337 csType = Combining; 337 csType = Combining;
338 }
339 while ( !ds.atEnd() ) {
340 QIMPenChar *pc = new QIMPenChar;
341 ds >> *pc;
342 if ( d == User )
343 markDeleted( pc->character() ); // override system
344 addChar( pc );
345 }
346 if ( file.status() == IO_Ok )
347 ok = TRUE;
338 } 348 }
339 while ( !ds.atEnd() ) { 349
340 QIMPenChar *pc = new QIMPenChar; 350 return ok;
341 ds >> *pc;
342 if ( d == User )
343 markDeleted( pc->character() ); // override system
344 addChar( pc );
345 }
346 if ( file.status() == IO_Ok )
347 ok = TRUE;
348 }
349
350 return ok;
351} 351}
352 352
353/*! 353/*!
354 Save this character set. 354 Save this character set.
355*/ 355 */
356bool QIMPenCharSet::save( Domain d ) 356bool QIMPenCharSet::save( Domain d )
357{ 357{
358 if ( filename( d ).isEmpty() ) 358 if ( filename( d ).isEmpty() )
359 return FALSE; 359 return FALSE;
360 360
361 bool ok = FALSE; 361 bool ok = FALSE;
362 362
363 QString fn = filename( d ); 363 QString fn = filename( d );
364 QString tmpFn = fn + ".new"; 364 QString tmpFn = fn + ".new";
365 QFile file( tmpFn ); 365 QFile file( tmpFn );
366 if ( file.open( IO_WriteOnly|IO_Raw ) ) { 366 if ( file.open( IO_WriteOnly|IO_Raw ) ) {
367 QDataStream ds( &file ); 367 QByteArray buf;
368 ds << QString( "QPT 1.1" ); 368 QDataStream ds( buf, IO_WriteOnly );
369 ds << csTitle; 369 ds << QString( "QPT 1.1" );
370 ds << desc; 370 ds << csTitle;
371 ds << (Q_INT8)csType; 371 ds << desc;
372 QIMPenCharIterator ci( chars ); 372 ds << (Q_INT8)csType;
373 for ( ; ci.current(); ++ci ) { 373 QIMPenCharIterator ci( chars );
374 QIMPenChar *pc = ci.current(); 374 for ( ; ci.current(); ++ci ) {
375 if ( ( (d == System) && pc->testFlag( QIMPenChar::System ) ) || 375 QIMPenChar *pc = ci.current();
376 ( (d == User) && !pc->testFlag( QIMPenChar::System ) ) ) { 376 if ( ( ( (d == System) && pc->testFlag( QIMPenChar::System ) ) ||
377 ds << *pc; 377 ( (d == User) && !pc->testFlag( QIMPenChar::System ) ) ) &&
378 } 378 ( !pc->testFlag (QIMPenChar::Combined ) ) ) {
379 if ( file.status() != IO_Ok ) 379 ds << *pc;
380 break; 380 }
381 } 381 }
382 if ( file.status() == IO_Ok ) 382
383 ok = TRUE; 383 file.writeBlock( buf );
384 } 384 file.close();
385 385 if ( file.status() == IO_Ok )
386 if ( ok ) { 386 ok = TRUE;
387 if ( ::rename( tmpFn.latin1(), fn.latin1() ) < 0 ) {
388 qWarning( "problem renaming file %s to %s, errno: %d",
389 tmpFn.latin1(), fn.latin1(), errno );
390 // remove the tmp file, otherwise, it will just lay around...
391 QFile::remove( tmpFn.latin1() );
392 ok = FALSE;
393 } 387 }
394 }
395 388
396 return ok; 389 if ( ok ) {
390 if ( ::rename( tmpFn.latin1(), fn.latin1() ) < 0 ) {
391 qWarning( "problem renaming file %s to %s, errno: %d",
392 tmpFn.latin1(), fn.latin1(), errno );
393 // remove the tmp file, otherwise, it will just lay around...
394 QFile::remove( tmpFn.latin1() );
395 ok = FALSE;
396 }
397 }
398
399 return ok;
397} 400}
398 401
399QIMPenChar *QIMPenCharSet::at( int i ) 402QIMPenChar *QIMPenCharSet::at( int i )
400{ 403{
401 return chars.at(i); 404 return chars.at(i);
402} 405}
403 406
404void QIMPenCharSet::markDeleted( uint ch ) 407void QIMPenCharSet::markDeleted( uint ch )
405{ 408{
406 QIMPenCharIterator ci( chars ); 409 QIMPenCharIterator ci( chars );
407 for ( ; ci.current(); ++ci ) { 410 for ( ; ci.current(); ++ci ) {
408 QIMPenChar *pc = ci.current(); 411 QIMPenChar *pc = ci.current();
409 if ( pc->character() == ch && pc->testFlag( QIMPenChar::System ) ) 412 if ( pc->character() == ch && pc->testFlag( QIMPenChar::System ) )
410 pc->setFlag( QIMPenChar::Deleted ); 413 pc->setFlag( QIMPenChar::Deleted );
411 } 414 }
412} 415}
413 416
414/*! 417/*!
415 Find the best matches for \a ch in this character set. 418 Find the best matches for \a ch in this character set.
416*/ 419 */
417QIMPenCharMatchList QIMPenCharSet::match( QIMPenChar *ch ) 420QIMPenCharMatchList QIMPenCharSet::match( QIMPenChar *ch )
418{ 421{
419 QIMPenCharMatchList matches; 422 QIMPenCharMatchList matches;
420 423
421 QIMPenCharIterator ci( chars ); 424 QIMPenCharIterator ci( chars );
422 for ( ; ci.current(); ++ci ) { 425 for ( ; ci.current(); ++ci ) {
423 QIMPenChar *tmplChar = ci.current(); 426 QIMPenChar *tmplChar = ci.current();
424 if ( tmplChar->testFlag( QIMPenChar::Deleted ) ) { 427 if ( tmplChar->testFlag( QIMPenChar::Deleted ) ) {
425 continue; 428 continue;
426 }
427 int err;
428 if ( ch->penStrokes().count() <= tmplChar->penStrokes().count() ) {
429 err = ch->match( tmplChar );
430 if ( err <= QIMPEN_MATCH_THRESHOLD ) {
431 if (tmplChar->penStrokes().count() != ch->penStrokes().count())
432 err = QMIN(err*3, QIMPEN_MATCH_THRESHOLD);
433 QIMPenCharMatchList::Iterator it;
434 for ( it = matches.begin(); it != matches.end(); ++it ) {
435 if ( (*it).penChar->character() == tmplChar->character() &&
436 (*it).penChar->penStrokes().count() == tmplChar->penStrokes().count() ) {
437 if ( (*it).error > err )
438 (*it).error = err;
439 break;
440 }
441 } 429 }
442 if ( it == matches.end() ) { 430 int err;
443 QIMPenCharMatch m; 431 if ( ch->penStrokes().count() <= tmplChar->penStrokes().count() ) {
444 m.error = err; 432 err = ch->match( tmplChar );
445 m.penChar = tmplChar; 433 if ( err <= QIMPEN_MATCH_THRESHOLD ) {
446 matches.append( m ); 434 if (tmplChar->penStrokes().count() != ch->penStrokes().count())
435 err = QMIN(err*3, QIMPEN_MATCH_THRESHOLD);
436 QIMPenCharMatchList::Iterator it;
437 for ( it = matches.begin(); it != matches.end(); ++it ) {
438 if ( (*it).penChar->character() == tmplChar->character() &&
439 (*it).penChar->penStrokes().count() == tmplChar->penStrokes().count() ) {
440 if ( (*it).error > err )
441 (*it).error = err;
442 break;
443 }
444 }
445 if ( it == matches.end() ) {
446 QIMPenCharMatch m;
447 m.error = err;
448 m.penChar = tmplChar;
449 matches.append( m );
450 }
451 }
447 } 452 }
448 }
449 } 453 }
450 } 454 qHeapSort( matches );
451 qHeapSort( matches ); 455 /*
452/* 456 QIMPenCharMatchList::Iterator it;
453 QIMPenCharMatchList::Iterator it; 457 for ( it = matches.begin(); it != matches.end(); ++it ) {
454 for ( it = matches.begin(); it != matches.end(); ++it ) { 458 qDebug( "Match: \'%c\', error %d, strokes %d", (*it).penChar->character(),
455 qDebug( "Match: \'%c\', error %d, strokes %d", (*it).penChar->character(), 459 (*it).error, (*it).penChar->penStrokes().count() );
456 (*it).error, (*it).penChar->penStrokes().count() ); 460 }
457 } 461 */
458*/ 462 return matches;
459 return matches;
460} 463}
461 464
462/*! 465/*!
463 Add a character \a ch to this set. 466 Add a character \a ch to this set.
464 QIMPenCharSet will delete this character when it is no longer needed. 467 QIMPenCharSet will delete this character when it is no longer needed.
465*/ 468 */
466void QIMPenCharSet::addChar( QIMPenChar *ch ) 469void QIMPenCharSet::addChar( QIMPenChar *ch )
467{ 470{
468 if ( ch->penStrokes().count() > maxStrokes ) 471 if ( ch->penStrokes().count() > maxStrokes )
469 maxStrokes = ch->penStrokes().count(); 472 maxStrokes = ch->penStrokes().count();
470 chars.append( ch ); 473 chars.append( ch );
471} 474}
472 475
473/*! 476/*!
474 Remove a character by reference \a ch from this set. 477 Remove a character by reference \a ch from this set.
475 QIMPenCharSet will delete this character. 478 QIMPenCharSet will delete this character.
476*/ 479 */
477void QIMPenCharSet::removeChar( QIMPenChar *ch ) 480void QIMPenCharSet::removeChar( QIMPenChar *ch )
478{ 481{
479 chars.remove( ch ); 482 chars.remove( ch );
480} 483}
481 484
482/*! 485/*!
483 Move the character up the list of characters. 486 Move the character up the list of characters.
484*/ 487 */
485void QIMPenCharSet::up( QIMPenChar *ch ) 488void QIMPenCharSet::up( QIMPenChar *ch )
486{ 489{
487 int idx = chars.findRef( ch ); 490 int idx = chars.findRef( ch );
488 if ( idx > 0 ) { 491 if ( idx > 0 ) {
489 chars.take(); 492 chars.take();
490 chars.insert( idx - 1, ch ); 493 chars.insert( idx - 1, ch );
491 } 494 }
492} 495}
493 496
494/*! 497/*!
495 Move the character down the list of characters. 498 Move the character down the list of characters.
496*/ 499 */
497void QIMPenCharSet::down( QIMPenChar *ch ) 500void QIMPenCharSet::down( QIMPenChar *ch )
498{ 501{
499 int idx = chars.findRef( ch ); 502 int idx = chars.findRef( ch );
500 if ( idx >= 0 && idx < (int)chars.count() - 1 ) { 503 if ( idx >= 0 && idx < (int)chars.count() - 1 ) {
501 chars.take(); 504 chars.take();
502 chars.insert( idx + 1, ch ); 505 chars.insert( idx + 1, ch );
503 } 506 }
504} 507}
505 508
diff --git a/inputmethods/handwriting/qimpenchar.h b/inputmethods/handwriting/qimpenchar.h
index 9a5f687..efd6f16 100644
--- a/inputmethods/handwriting/qimpenchar.h
+++ b/inputmethods/handwriting/qimpenchar.h
@@ -47,33 +47,33 @@ public:
47 void setData( const QString &ba ) { d = ba; } 47 void setData( const QString &ba ) { d = ba; }
48 48
49 QString name() const; 49 QString name() const;
50 bool isEmpty() const { return strokes.isEmpty(); } 50 bool isEmpty() const { return strokes.isEmpty(); }
51 unsigned int strokeCount() const { return strokes.count(); } 51 unsigned int strokeCount() const { return strokes.count(); }
52 unsigned int strokeLength( int s ) const; 52 unsigned int strokeLength( int s ) const;
53 void clear(); 53 void clear();
54 int match( QIMPenChar *ch ); 54 int match( QIMPenChar *ch );
55 const QIMPenStrokeList &penStrokes() { return strokes; } 55 const QIMPenStrokeList &penStrokes() { return strokes; }
56 QPoint startingPoint() const { return strokes.getFirst()->startingPoint(); } 56 QPoint startingPoint() const { return strokes.getFirst()->startingPoint(); }
57 QRect boundingRect(); 57 QRect boundingRect();
58 58
59 void setFlag( int f ) { flags |= f; } 59 void setFlag( int f ) { flags |= f; }
60 void clearFlag( int f ) { flags &= ~f; } 60 void clearFlag( int f ) { flags &= ~f; }
61 bool testFlag( int f ) { return flags & f; } 61 bool testFlag( int f ) { return flags & f; }
62 62
63 enum Flags { System=0x01, Deleted=0x02, CombineRight=0x04, Data=0x08 }; 63 enum Flags { System=0x01, Deleted=0x02, CombineRight=0x04, Data=0x08, Combined=0x10 };
64 // Correspond to codes in template files. Do not change values. 64 // Correspond to codes in template files. Do not change values.
65 enum Mode { ModeBase=0x4000, Caps=0x4001, Shortcut=0x4002, CapsLock=0x4003, 65 enum Mode { ModeBase=0x4000, Caps=0x4001, Shortcut=0x4002, CapsLock=0x4003,
66 Punctuation=0x4004, Symbol=0x4005, Extended=0x4006 }; 66 Punctuation=0x4004, Symbol=0x4005, Extended=0x4006 };
67 67
68 QIMPenChar &operator=( const QIMPenChar &s ); 68 QIMPenChar &operator=( const QIMPenChar &s );
69 69
70 void addStroke( QIMPenStroke * ); 70 void addStroke( QIMPenStroke * );
71 71
72protected: 72protected:
73 unsigned int ch; 73 unsigned int ch;
74 QString d; 74 QString d;
75 Q_UINT8 flags; 75 Q_UINT8 flags;
76 QIMPenStrokeList strokes; 76 QIMPenStrokeList strokes;
77 77
78 friend QDataStream &operator<< (QDataStream &, const QIMPenChar &); 78 friend QDataStream &operator<< (QDataStream &, const QIMPenChar &);
79 friend QDataStream &operator>> (QDataStream &, QIMPenChar &); 79 friend QDataStream &operator>> (QDataStream &, QIMPenChar &);
diff --git a/inputmethods/handwriting/qimpencombining.cpp b/inputmethods/handwriting/qimpencombining.cpp
index 30459e7..2e01ac2 100644
--- a/inputmethods/handwriting/qimpencombining.cpp
+++ b/inputmethods/handwriting/qimpencombining.cpp
@@ -59,32 +59,33 @@ void QIMPenCombining::addCombined( QIMPenCharSet *cs )
59 QIMPenCharIterator it( cs->characters() ); 59 QIMPenCharIterator it( cs->characters() );
60 for ( ; it.current() && count; ++it, --count ) { 60 for ( ; it.current() && count; ++it, --count ) {
61 QIMPenChar *pc = it.current(); 61 QIMPenChar *pc = it.current();
62 if ( pc->testFlag( QIMPenChar::Deleted ) ) 62 if ( pc->testFlag( QIMPenChar::Deleted ) )
63 continue; 63 continue;
64 int charIdx = findCombining( pc->character() ); 64 int charIdx = findCombining( pc->character() );
65 if ( charIdx < 0 ) 65 if ( charIdx < 0 )
66 continue; 66 continue;
67 for ( int i = 0; i < 6; i++ ) { 67 for ( int i = 0; i < 6; i++ ) {
68 if ( combiningChars[charIdx][i+1] ) { 68 if ( combiningChars[charIdx][i+1] ) {
69 QIMPenCharIterator cit( chars ); 69 QIMPenCharIterator cit( chars );
70 for ( ; cit.current(); ++cit ) { 70 for ( ; cit.current(); ++cit ) {
71 QIMPenChar *accentPc = cit.current(); 71 QIMPenChar *accentPc = cit.current();
72 if ( accentPc->character() == combiningSymbols[i] ) { 72 if ( accentPc->character() == combiningSymbols[i] ) {
73 QIMPenChar *combined = combine( pc, accentPc ); 73 QIMPenChar *combined = combine( pc, accentPc );
74 combined->setCharacter( combiningChars[charIdx][i+1] ); 74 combined->setCharacter( combiningChars[charIdx][i+1] );
75 combined->setFlag( QIMPenChar::Combined );
75 cs->addChar( combined ); 76 cs->addChar( combined );
76 } 77 }
77 } 78 }
78 } 79 }
79 } 80 }
80 } 81 }
81} 82}
82 83
83int QIMPenCombining::findCombining( unsigned int ch ) const 84int QIMPenCombining::findCombining( unsigned int ch ) const
84{ 85{
85 int i = 0; 86 int i = 0;
86 while ( combiningChars[i][0] ) { 87 while ( combiningChars[i][0] ) {
87 if ( combiningChars[i][0] == ch ) 88 if ( combiningChars[i][0] == ch )
88 return i; 89 return i;
89 i++; 90 i++;
90 } 91 }