00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <qfile.h>
00022 #include <qtl.h>
00023 #include <math.h>
00024 #include <limits.h>
00025 #include <errno.h>
00026 #include <qdatastream.h>
00027 #include "qimpencombining.h"
00028 #include "qimpenchar.h"
00029 #include "opie2/odebug.h"
00030
00031 #define QIMPEN_MATCH_THRESHOLD 200000
00032
00033 const QIMPenSpecialKeys qimpen_specialKeys[] = {
00034 { Qt::Key_Escape, "[Esc]" },
00035 { Qt::Key_Tab, "[Tab]" },
00036 { Qt::Key_Backspace, "[BackSpace]" },
00037 { Qt::Key_Return, "[Return]" },
00038 { QIMPenChar::Caps, "[Uppercase]" },
00039 { QIMPenChar::CapsLock, "[Caps Lock]" },
00040 { QIMPenChar::Shortcut, "[Shortcut]" },
00041 { QIMPenChar::Punctuation, "[Punctuation]" },
00042 { QIMPenChar::Symbol, "[Symbol]" },
00043 { QIMPenChar::Extended, "[Extended]" },
00044 { Qt::Key_unknown, 0 } };
00045
00046
00054 QIMPenChar::QIMPenChar()
00055 {
00056 flags = 0;
00057 strokes.setAutoDelete( TRUE );
00058 }
00059
00060 QIMPenChar::QIMPenChar( const QIMPenChar &chr )
00061 {
00062 strokes.setAutoDelete( TRUE );
00063 ch = chr.ch;
00064 flags = chr.flags;
00065 d = chr.d;
00066 QIMPenStrokeIterator it( chr.strokes );
00067 while ( it.current() ) {
00068 strokes.append( new QIMPenStroke( *it.current() ) );
00069 ++it;
00070 }
00071 }
00072
00073 QIMPenChar &QIMPenChar::operator=( const QIMPenChar &chr )
00074 {
00075 strokes.clear();
00076 ch = chr.ch;
00077 flags = chr.flags;
00078 d = chr.d;
00079 QIMPenStrokeIterator it( chr.strokes );
00080 while ( it.current() ) {
00081 strokes.append( new QIMPenStroke( *it.current() ) );
00082 ++it;
00083 }
00084
00085 return *this;
00086 }
00087
00088 QString QIMPenChar::name() const
00089 {
00090 QString n;
00091
00092 if ( (ch & 0x0000FFFF) == 0 ) {
00093 int code = ch >> 16;
00094 for ( int i = 0; qimpen_specialKeys[i].code != Qt::Key_unknown; i++ ) {
00095 if ( qimpen_specialKeys[i].code == code ) {
00096 n = qimpen_specialKeys[i].name;
00097 break;
00098 }
00099 }
00100 } else {
00101 n = QChar( ch & 0x0000FFFF );
00102 }
00103
00104 return n;
00105 }
00106
00107 void QIMPenChar::clear()
00108 {
00109 ch = 0;
00110 flags = 0;
00111 d = QString::null;
00112 strokes.clear();
00113 }
00114
00115 unsigned int QIMPenChar::strokeLength( int s ) const
00116 {
00117 QIMPenStrokeIterator it( strokes );
00118 while ( it.current() && s ) {
00119 ++it;
00120 --s;
00121 }
00122
00123 if ( it.current() )
00124 return it.current()->length();
00125
00126 return 0;
00127 }
00128
00132 void QIMPenChar::addStroke( QIMPenStroke *st )
00133 {
00134 QIMPenStroke *stroke = new QIMPenStroke( *st );
00135 strokes.append( stroke );
00136 }
00137
00142 int QIMPenChar::match( QIMPenChar *pen )
00143 {
00144
00145
00146
00147
00148 int err = 0;
00149 int maxErr = 0;
00150 int diff = 0;
00151 QIMPenStrokeIterator it1( strokes );
00152 QIMPenStrokeIterator it2( pen->strokes );
00153 err = it1.current()->match( it2.current() );
00154 if ( err > maxErr )
00155 maxErr = err;
00156 ++it1;
00157 ++it2;
00158 while ( err < 400000 && it1.current() && it2.current() ) {
00159 QPoint p1 = it1.current()->boundingRect().center() -
00160 strokes.getFirst()->boundingRect().center();
00161 QPoint p2 = it2.current()->boundingRect().center() -
00162 pen->strokes.getFirst()->boundingRect().center();
00163 int xdiff = QABS( p1.x() - p2.x() ) - 6;
00164 int ydiff = QABS( p1.y() - p2.y() ) - 5;
00165 if ( xdiff < 0 )
00166 xdiff = 0;
00167 if ( ydiff < 0 )
00168 ydiff = 0;
00169 if ( xdiff > 10 || ydiff > 10 ) {
00170 #ifdef DEBUG_QIMPEN
00171 odebug << "char " << pen->ch <<", stroke starting pt diff excessive" << oendl;
00172 #endif
00173 return INT_MAX;
00174 }
00175 diff += xdiff*xdiff + ydiff*ydiff;
00176 err = it1.current()->match( it2.current() );
00177 if ( err > maxErr )
00178 maxErr = err;
00179 ++it1;
00180 ++it2;
00181 }
00182
00183 maxErr += diff * diff * 6;
00184
00185 #ifdef DEBUG_QIMPEN
00186 odebug << "char: " << pen->ch << ", maxErr " << maxErr << ", diff " << diff << ", " << strokes.count() << oendl;
00187 #endif
00188 return maxErr;
00189 }
00190
00196 QRect QIMPenChar::boundingRect()
00197 {
00198 QRect br;
00199 QIMPenStroke *st = strokes.first();
00200 while ( st ) {
00201 br |= st->boundingRect();
00202 st = strokes.next();
00203 }
00204
00205 return br;
00206 }
00207
00208
00212 QDataStream &operator<< (QDataStream &s, const QIMPenChar &ws)
00213 {
00214 s << ws.ch;
00215 s << ws.flags;
00216 if ( ws.flags & QIMPenChar::Data )
00217 s << ws.d;
00218 s << ws.strokes.count();
00219 QIMPenStrokeIterator it( ws.strokes );
00220 while ( it.current() ) {
00221 s << *it.current();
00222 ++it;
00223 }
00224
00225 return s;
00226 }
00227
00231 QDataStream &operator>> (QDataStream &s, QIMPenChar &ws)
00232 {
00233 s >> ws.ch;
00234 s >> ws.flags;
00235 if ( ws.flags & QIMPenChar::Data )
00236 s >> ws.d;
00237 unsigned size;
00238 s >> size;
00239 for ( unsigned i = 0; i < size; i++ ) {
00240 QIMPenStroke *st = new QIMPenStroke();
00241 s >> *st;
00242 ws.strokes.append( st );
00243 }
00244
00245 return s;
00246 }
00247
00248
00249
00250 bool QIMPenCharMatch::operator>( const QIMPenCharMatch &m )
00251 {
00252 return error > m.error;
00253 }
00254
00255 bool QIMPenCharMatch::operator<( const QIMPenCharMatch &m )
00256 {
00257 return error < m.error;
00258 }
00259
00260 bool QIMPenCharMatch::operator<=( const QIMPenCharMatch &m )
00261 {
00262 return error <= m.error;
00263 }
00264
00265
00266
00273 QIMPenCharSet::QIMPenCharSet()
00274 {
00275 chars.setAutoDelete( TRUE );
00276 desc = "Unnamed";
00277 csTitle = "abc";
00278 csType = Unknown;
00279 maxStrokes = 0;
00280 }
00281
00285 QIMPenCharSet::QIMPenCharSet( const QString &fn )
00286 {
00287 chars.setAutoDelete( TRUE );
00288 desc = "Unnamed";
00289 csTitle = "abc";
00290 csType = Unknown;
00291 maxStrokes = 0;
00292 load( fn, System );
00293 }
00294
00295 const QString &QIMPenCharSet::filename( Domain d ) const
00296 {
00297 if ( d == System )
00298 return sysFilename;
00299 else
00300 return userFilename;
00301 }
00302
00303 void QIMPenCharSet::setFilename( const QString &fn, Domain d )
00304 {
00305 if ( d == System )
00306 sysFilename = fn;
00307 else if ( d == User )
00308 userFilename = fn;
00309 }
00310
00314 bool QIMPenCharSet::load( const QString &fn, Domain d )
00315 {
00316 setFilename( fn, d );
00317
00318 bool ok = FALSE;
00319 QFile file( fn );
00320 if ( file.open( IO_ReadOnly ) ) {
00321 QDataStream ds( &file );
00322 QString version;
00323 ds >> version;
00324 ds >> csTitle;
00325 ds >> desc;
00326 int major = version.mid( 4, 1 ).toInt();
00327 int minor = version.mid( 6 ).toInt();
00328 if ( major >= 1 && minor > 0 ) {
00329 ds >> (Q_INT8 &)csType;
00330 } else {
00331 if ( csTitle == "abc" )
00332 csType = Lower;
00333 else if ( csTitle == "ABC" )
00334 csType = Upper;
00335 else if ( csTitle == "123" )
00336 csType = Numeric;
00337 else if ( fn == "Combining" )
00338 csType = Combining;
00339 }
00340 while ( !ds.atEnd() ) {
00341 QIMPenChar *pc = new QIMPenChar;
00342 ds >> *pc;
00343 if ( d == User )
00344 markDeleted( pc->character() );
00345 if ( !pc->testFlag( QIMPenChar::Deleted ) )
00346 addChar( pc );
00347 }
00348 if ( file.status() == IO_Ok )
00349 ok = TRUE;
00350 }
00351 setHidden ( false );
00352 return ok;
00353 }
00354
00358 bool QIMPenCharSet::save( Domain d )
00359 {
00360 if ( filename( d ).isEmpty() )
00361 return FALSE;
00362
00363 if ( hidden() )
00364 return TRUE;
00365
00366 bool ok = FALSE;
00367
00368 QString fn = filename( d );
00369 QString tmpFn = fn + ".new";
00370 QFile file( tmpFn );
00371 if ( file.open( IO_WriteOnly|IO_Raw ) ) {
00372 QByteArray buf;
00373 QDataStream ds( buf, IO_WriteOnly );
00374 ds << QString( "QPT 1.1" );
00375 ds << csTitle;
00376 ds << desc;
00377 ds << (Q_INT8)csType;
00378 QIMPenCharIterator ci( chars );
00379 for ( ; ci.current(); ++ci ) {
00380 QIMPenChar *pc = ci.current();
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393 if ( ( ( (d == System) && pc->testFlag( QIMPenChar::System ) ) ||
00394 ( (d == User) && !pc->testFlag( QIMPenChar::System ) && !pc->testFlag( QIMPenChar::Deleted ) ) ||
00395 ( (d == User) && pc->testFlag( QIMPenChar::System ) && pc->testFlag( QIMPenChar::Deleted ) ) ) &&
00396 ( !pc->testFlag (QIMPenChar::Combined ) ) ) {
00397 ds << *pc;
00398 }
00399 }
00400
00401 file.writeBlock( buf );
00402 file.close();
00403 if ( file.status() == IO_Ok )
00404 ok = TRUE;
00405 }
00406
00407 if ( ok ) {
00408 if ( ::rename( tmpFn.latin1(), fn.latin1() ) < 0 ) {
00409 owarn << "problem renaming file " <<tmpFn.latin1() << " to "<< fn.latin1()
00410 << ", errno: " << errno << oendl;
00411
00412 QFile::remove( tmpFn.latin1() );
00413 ok = FALSE;
00414 }
00415 }
00416
00417 return ok;
00418 }
00419
00420 QIMPenChar *QIMPenCharSet::at( int i )
00421 {
00422 return chars.at(i);
00423 }
00424
00425 void QIMPenCharSet::markDeleted( uint ch )
00426 {
00427 QIMPenCharIterator ci( chars );
00428 for ( ; ci.current(); ++ci ) {
00429 QIMPenChar *pc = ci.current();
00430 if ( pc->character() == ch && pc->testFlag( QIMPenChar::System ) )
00431 pc->setFlag( QIMPenChar::Deleted );
00432 }
00433 }
00434
00438 QIMPenCharMatchList QIMPenCharSet::match( QIMPenChar *ch )
00439 {
00440 QIMPenCharMatchList matches;
00441
00442 QIMPenCharIterator ci( chars );
00443 for ( ; ci.current(); ++ci ) {
00444 QIMPenChar *tmplChar = ci.current();
00445 if ( tmplChar->testFlag( QIMPenChar::Deleted ) ) {
00446 continue;
00447 }
00448 int err;
00449 if ( ch->penStrokes().count() <= tmplChar->penStrokes().count() ) {
00450 err = ch->match( tmplChar );
00451 if ( err <= QIMPEN_MATCH_THRESHOLD ) {
00452 if (tmplChar->penStrokes().count() != ch->penStrokes().count())
00453 err = QMIN(err*3, QIMPEN_MATCH_THRESHOLD);
00454 QIMPenCharMatchList::Iterator it;
00455 for ( it = matches.begin(); it != matches.end(); ++it ) {
00456 if ( (*it).penChar->character() == tmplChar->character() &&
00457 (*it).penChar->penStrokes().count() == tmplChar->penStrokes().count() ) {
00458 if ( (*it).error > err )
00459 (*it).error = err;
00460 break;
00461 }
00462 }
00463 if ( it == matches.end() ) {
00464 QIMPenCharMatch m;
00465 m.error = err;
00466 m.penChar = tmplChar;
00467 matches.append( m );
00468 }
00469 }
00470 }
00471 }
00472 qHeapSort( matches );
00473
00474
00475
00476
00477
00478
00479
00480 return matches;
00481 }
00482
00487 void QIMPenCharSet::addChar( QIMPenChar *ch )
00488 {
00489 if ( ch->penStrokes().count() > maxStrokes )
00490 maxStrokes = ch->penStrokes().count();
00491 chars.append( ch );
00492 }
00493
00498 void QIMPenCharSet::removeChar( QIMPenChar *ch )
00499 {
00500 chars.remove( ch );
00501 }
00502
00506 void QIMPenCharSet::up( QIMPenChar *ch )
00507 {
00508 int idx = chars.findRef( ch );
00509 if ( idx > 0 ) {
00510 chars.take();
00511 chars.insert( idx - 1, ch );
00512 }
00513 }
00514
00518 void QIMPenCharSet::down( QIMPenChar *ch )
00519 {
00520 int idx = chars.findRef( ch );
00521 if ( idx >= 0 && idx < (int)chars.count() - 1 ) {
00522 chars.take();
00523 chars.insert( idx + 1, ch );
00524 }
00525 }
00526