Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

qimpenchar.cpp

Go to the documentation of this file.
00001 /**********************************************************************
00002  ** Copyright (C) 2000 Trolltech AS.  All rights reserved.
00003  **
00004  ** This file is part of Qtopia Environment.
00005  **
00006  ** This file may be distributed and/or modified under the terms of the
00007  ** GNU General Public License version 2 as published by the Free Software
00008  ** Foundation and appearing in the file LICENSE.GPL included in the
00009  ** packaging of this file.
00010  **
00011  ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00012  ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00013  **
00014  ** See http://www.trolltech.com/gpl/ for GPL licensing information.
00015  **
00016  ** Contact info@trolltech.com if any conditions of this licensing are
00017  ** not clear to you.
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            if ( strokes.count() > pen->strokes.count() )
00146            return INT_MAX;
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 ) { // not a chance
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; // magic weighting :)
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() ); // override system
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  * If the Domain is System and the Char is marked System - OR
00383  * the domain is User, the Char is User and it's not deleted - OR
00384  * the domain is User, the Char is System and it is deleted - AND
00385  * the character is not an automated Combined Character
00386  * 
00387  * This is required to ensure that we don't save user defined chars that have been deleted, but
00388  * we *DO* save System chars that have been deleted. There is still the issue of deleted combined
00389  * chars but I'm not sure how to tackle that yet
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                         // remove the tmp file, otherwise, it will just lay around...
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            QIMPenCharMatchList::Iterator it;
00475            for ( it = matches.begin(); it != matches.end(); ++it ) {
00476         
00477            odebug << "Match: \'" << (*it).penChar->character() "\', error " << (*it).error ", strokes " <<(*it).penChar->penStrokes().count() << oendl;
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 

Generated on Sat Nov 5 16:16:01 2005 for OPIE by  doxygen 1.4.2