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