-rw-r--r-- | inputmethods/handwriting/qimpenchar.cpp | 13 | ||||
-rw-r--r-- | inputmethods/handwriting/qimpenchar.h | 2 | ||||
-rw-r--r-- | inputmethods/handwriting/qimpencombining.cpp | 1 |
3 files changed, 10 insertions, 6 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 @@ /********************************************************************** ** Copyright (C) 2000 Trolltech AS. All rights reserved. ** ** This file is part of Qtopia Environment. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #include <qfile.h> #include <qtl.h> #include <math.h> #include <limits.h> #include <errno.h> #include <qdatastream.h> #include "qimpencombining.h" #include "qimpenchar.h" #define QIMPEN_MATCH_THRESHOLD 200000 const QIMPenSpecialKeys qimpen_specialKeys[] = { { Qt::Key_Escape, "[Esc]" }, { Qt::Key_Tab, "[Tab]" }, { Qt::Key_Backspace, "[BackSpace]" }, { Qt::Key_Return, "[Return]" }, { QIMPenChar::Caps, "[Uppercase]" }, { QIMPenChar::CapsLock, "[Caps Lock]" }, { QIMPenChar::Shortcut, "[Shortcut]" }, { QIMPenChar::Punctuation, "[Punctuation]" }, { QIMPenChar::Symbol, "[Symbol]" }, { QIMPenChar::Extended, "[Extended]" }, { Qt::Key_unknown, 0 } }; /*! \class QIMPenChar qimpenchar.h Handles a single character. Can calculate closeness of match to another character. */ QIMPenChar::QIMPenChar() { flags = 0; strokes.setAutoDelete( TRUE ); } QIMPenChar::QIMPenChar( const QIMPenChar &chr ) { strokes.setAutoDelete( TRUE ); ch = chr.ch; flags = chr.flags; d = chr.d; QIMPenStrokeIterator it( chr.strokes ); while ( it.current() ) { strokes.append( new QIMPenStroke( *it.current() ) ); ++it; } } QIMPenChar &QIMPenChar::operator=( const QIMPenChar &chr ) { strokes.clear(); ch = chr.ch; flags = chr.flags; d = chr.d; QIMPenStrokeIterator it( chr.strokes ); while ( it.current() ) { strokes.append( new QIMPenStroke( *it.current() ) ); ++it; } return *this; } QString QIMPenChar::name() const { QString n; if ( (ch & 0x0000FFFF) == 0 ) { int code = ch >> 16; for ( int i = 0; qimpen_specialKeys[i].code != Qt::Key_unknown; i++ ) { if ( qimpen_specialKeys[i].code == code ) { n = qimpen_specialKeys[i].name; break; } } } else { n = QChar( ch & 0x0000FFFF ); } return n; } void QIMPenChar::clear() { ch = 0; flags = 0; d = QString::null; strokes.clear(); } unsigned int QIMPenChar::strokeLength( int s ) const { QIMPenStrokeIterator it( strokes ); while ( it.current() && s ) { ++it; --s; } if ( it.current() ) return it.current()->length(); return 0; } /*! Add a stroke to the character */ void QIMPenChar::addStroke( QIMPenStroke *st ) { QIMPenStroke *stroke = new QIMPenStroke( *st ); strokes.append( stroke ); } /*! Return an indicator of the closeness of this character to \a pen. Lower value is better. */ int QIMPenChar::match( QIMPenChar *pen ) { /* if ( strokes.count() > pen->strokes.count() ) return INT_MAX; */ int err = 0; int maxErr = 0; int diff = 0; QIMPenStrokeIterator it1( strokes ); QIMPenStrokeIterator it2( pen->strokes ); err = it1.current()->match( it2.current() ); if ( err > maxErr ) maxErr = err; ++it1; ++it2; while ( err < 400000 && it1.current() && it2.current() ) { QPoint p1 = it1.current()->boundingRect().center() - strokes.getFirst()->boundingRect().center(); QPoint p2 = it2.current()->boundingRect().center() - pen->strokes.getFirst()->boundingRect().center(); int xdiff = QABS( p1.x() - p2.x() ) - 6; int ydiff = QABS( p1.y() - p2.y() ) - 5; if ( xdiff < 0 ) xdiff = 0; if ( ydiff < 0 ) ydiff = 0; if ( xdiff > 10 || ydiff > 10 ) { // not a chance #ifdef DEBUG_QIMPEN qDebug( "char %c, stroke starting pt diff excessive", pen->ch ); #endif return INT_MAX; } diff += xdiff*xdiff + ydiff*ydiff; err = it1.current()->match( it2.current() ); if ( err > maxErr ) maxErr = err; ++it1; ++it2; } maxErr += diff * diff * 6; // magic weighting :) #ifdef DEBUG_QIMPEN qDebug( "char: %c, maxErr %d, diff %d, (%d)", pen->ch, maxErr, diff, strokes.count() ); #endif return maxErr; } /*! Return the bounding rect of this character. It may have sides with negative coords since its origin is where the user started drawing the character. */ QRect QIMPenChar::boundingRect() { QRect br; QIMPenStroke *st = strokes.first(); while ( st ) { br |= st->boundingRect(); st = strokes.next(); } return br; } /*! Write the character's data to the stream. */ QDataStream &operator<< (QDataStream &s, const QIMPenChar &ws) { s << ws.ch; s << ws.flags; if ( ws.flags & QIMPenChar::Data ) s << ws.d; s << ws.strokes.count(); QIMPenStrokeIterator it( ws.strokes ); while ( it.current() ) { s << *it.current(); ++it; } return s; } /*! Read the character's data from the stream. */ QDataStream &operator>> (QDataStream &s, QIMPenChar &ws) { s >> ws.ch; s >> ws.flags; if ( ws.flags & QIMPenChar::Data ) s >> ws.d; unsigned size; s >> size; for ( unsigned i = 0; i < size; i++ ) { QIMPenStroke *st = new QIMPenStroke(); s >> *st; ws.strokes.append( st ); } return s; } //=========================================================================== bool QIMPenCharMatch::operator>( const QIMPenCharMatch &m ) { return error > m.error; } bool QIMPenCharMatch::operator<( const QIMPenCharMatch &m ) { return error < m.error; } bool QIMPenCharMatch::operator<=( const QIMPenCharMatch &m ) { return error <= m.error; } //=========================================================================== /*! \class QIMPenCharSet qimpenchar.h Maintains a set of related characters. */ QIMPenCharSet::QIMPenCharSet() { chars.setAutoDelete( TRUE ); desc = "Unnamed"; csTitle = "abc"; csType = Unknown; maxStrokes = 0; } /*! Construct and load a characters set from file \a fn. */ QIMPenCharSet::QIMPenCharSet( const QString &fn ) { chars.setAutoDelete( TRUE ); desc = "Unnamed"; csTitle = "abc"; csType = Unknown; maxStrokes = 0; load( fn, System ); } const QString &QIMPenCharSet::filename( Domain d ) const { if ( d == System ) return sysFilename; else return userFilename; } void QIMPenCharSet::setFilename( const QString &fn, Domain d ) { if ( d == System ) sysFilename = fn; else if ( d == User ) userFilename = fn; } /*! Load a character set from file \a fn. */ bool QIMPenCharSet::load( const QString &fn, Domain d ) { setFilename( fn, d ); bool ok = FALSE; QFile file( fn ); if ( file.open( IO_ReadOnly ) ) { QDataStream ds( &file ); QString version; ds >> version; ds >> csTitle; ds >> desc; int major = version.mid( 4, 1 ).toInt(); int minor = version.mid( 6 ).toInt(); if ( major >= 1 && minor > 0 ) { ds >> (Q_INT8 &)csType; } else { if ( csTitle == "abc" ) csType = Lower; else if ( csTitle == "ABC" ) csType = Upper; else if ( csTitle == "123" ) csType = Numeric; else if ( fn == "Combining" ) csType = Combining; } while ( !ds.atEnd() ) { QIMPenChar *pc = new QIMPenChar; ds >> *pc; if ( d == User ) markDeleted( pc->character() ); // override system addChar( pc ); } if ( file.status() == IO_Ok ) ok = TRUE; } return ok; } /*! Save this character set. */ bool QIMPenCharSet::save( Domain d ) { if ( filename( d ).isEmpty() ) return FALSE; bool ok = FALSE; QString fn = filename( d ); QString tmpFn = fn + ".new"; QFile file( tmpFn ); if ( file.open( IO_WriteOnly|IO_Raw ) ) { - QDataStream ds( &file ); + QByteArray buf; + QDataStream ds( buf, IO_WriteOnly ); ds << QString( "QPT 1.1" ); ds << csTitle; ds << desc; ds << (Q_INT8)csType; QIMPenCharIterator ci( chars ); for ( ; ci.current(); ++ci ) { QIMPenChar *pc = ci.current(); - if ( ( (d == System) && pc->testFlag( QIMPenChar::System ) ) || - ( (d == User) && !pc->testFlag( QIMPenChar::System ) ) ) { + if ( ( ( (d == System) && pc->testFlag( QIMPenChar::System ) ) || + ( (d == User) && !pc->testFlag( QIMPenChar::System ) ) ) && + ( !pc->testFlag (QIMPenChar::Combined ) ) ) { ds << *pc; } - if ( file.status() != IO_Ok ) - break; } + + file.writeBlock( buf ); + file.close(); if ( file.status() == IO_Ok ) ok = TRUE; } if ( ok ) { if ( ::rename( tmpFn.latin1(), fn.latin1() ) < 0 ) { qWarning( "problem renaming file %s to %s, errno: %d", tmpFn.latin1(), fn.latin1(), errno ); // remove the tmp file, otherwise, it will just lay around... QFile::remove( tmpFn.latin1() ); ok = FALSE; } } return ok; } QIMPenChar *QIMPenCharSet::at( int i ) { return chars.at(i); } void QIMPenCharSet::markDeleted( uint ch ) { QIMPenCharIterator ci( chars ); for ( ; ci.current(); ++ci ) { QIMPenChar *pc = ci.current(); if ( pc->character() == ch && pc->testFlag( QIMPenChar::System ) ) pc->setFlag( QIMPenChar::Deleted ); } } /*! Find the best matches for \a ch in this character set. */ QIMPenCharMatchList QIMPenCharSet::match( QIMPenChar *ch ) { QIMPenCharMatchList matches; QIMPenCharIterator ci( chars ); for ( ; ci.current(); ++ci ) { QIMPenChar *tmplChar = ci.current(); if ( tmplChar->testFlag( QIMPenChar::Deleted ) ) { continue; } int err; if ( ch->penStrokes().count() <= tmplChar->penStrokes().count() ) { err = ch->match( tmplChar ); if ( err <= QIMPEN_MATCH_THRESHOLD ) { if (tmplChar->penStrokes().count() != ch->penStrokes().count()) err = QMIN(err*3, QIMPEN_MATCH_THRESHOLD); QIMPenCharMatchList::Iterator it; for ( it = matches.begin(); it != matches.end(); ++it ) { if ( (*it).penChar->character() == tmplChar->character() && (*it).penChar->penStrokes().count() == tmplChar->penStrokes().count() ) { if ( (*it).error > err ) (*it).error = err; break; } } if ( it == matches.end() ) { QIMPenCharMatch m; m.error = err; m.penChar = tmplChar; matches.append( m ); } } } } qHeapSort( matches ); /* QIMPenCharMatchList::Iterator it; for ( it = matches.begin(); it != matches.end(); ++it ) { qDebug( "Match: \'%c\', error %d, strokes %d", (*it).penChar->character(), (*it).error, (*it).penChar->penStrokes().count() ); } */ return matches; } /*! Add a character \a ch to this set. QIMPenCharSet will delete this character when it is no longer needed. */ void QIMPenCharSet::addChar( QIMPenChar *ch ) { if ( ch->penStrokes().count() > maxStrokes ) maxStrokes = ch->penStrokes().count(); chars.append( ch ); } /*! Remove a character by reference \a ch from this set. QIMPenCharSet will delete this character. */ void QIMPenCharSet::removeChar( QIMPenChar *ch ) { chars.remove( ch ); } /*! Move the character up the list of characters. */ void QIMPenCharSet::up( QIMPenChar *ch ) { int idx = chars.findRef( ch ); if ( idx > 0 ) { chars.take(); chars.insert( idx - 1, ch ); } } /*! Move the character down the list of characters. */ void QIMPenCharSet::down( QIMPenChar *ch ) { int idx = chars.findRef( ch ); if ( idx >= 0 && idx < (int)chars.count() - 1 ) { chars.take(); chars.insert( idx + 1, ch ); } } 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 @@ -1,157 +1,157 @@ /********************************************************************** ** Copyright (C) 2000 Trolltech AS. All rights reserved. ** ** This file is part of Qtopia Environment. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #ifndef QIMPENCHAR_H_ #define QIMPENCHAR_H_ #include <qlist.h> #include <qvaluelist.h> #include <qcstring.h> #include "qimpenstroke.h" struct QIMPenSpecialKeys { int code; char *name; }; extern const QIMPenSpecialKeys qimpen_specialKeys[]; class QIMPenChar { public: QIMPenChar(); QIMPenChar( const QIMPenChar & ); unsigned int character() const { return ch; } void setCharacter( unsigned int c ) { ch = c; } const QString &data() const { return d; } void setData( const QString &ba ) { d = ba; } QString name() const; bool isEmpty() const { return strokes.isEmpty(); } unsigned int strokeCount() const { return strokes.count(); } unsigned int strokeLength( int s ) const; void clear(); int match( QIMPenChar *ch ); const QIMPenStrokeList &penStrokes() { return strokes; } QPoint startingPoint() const { return strokes.getFirst()->startingPoint(); } QRect boundingRect(); void setFlag( int f ) { flags |= f; } void clearFlag( int f ) { flags &= ~f; } bool testFlag( int f ) { return flags & f; } - enum Flags { System=0x01, Deleted=0x02, CombineRight=0x04, Data=0x08 }; + enum Flags { System=0x01, Deleted=0x02, CombineRight=0x04, Data=0x08, Combined=0x10 }; // Correspond to codes in template files. Do not change values. enum Mode { ModeBase=0x4000, Caps=0x4001, Shortcut=0x4002, CapsLock=0x4003, Punctuation=0x4004, Symbol=0x4005, Extended=0x4006 }; QIMPenChar &operator=( const QIMPenChar &s ); void addStroke( QIMPenStroke * ); protected: unsigned int ch; QString d; Q_UINT8 flags; QIMPenStrokeList strokes; friend QDataStream &operator<< (QDataStream &, const QIMPenChar &); friend QDataStream &operator>> (QDataStream &, QIMPenChar &); }; typedef QList<QIMPenChar> QIMPenCharList; typedef QListIterator<QIMPenChar> QIMPenCharIterator; QDataStream & operator<< (QDataStream & s, const QIMPenChar &ws); QDataStream & operator>> (QDataStream & s, QIMPenChar &ws); struct QIMPenCharMatch { int error; QIMPenChar *penChar; bool operator>( const QIMPenCharMatch &m ); bool operator<( const QIMPenCharMatch &m ); bool operator<=( const QIMPenCharMatch &m ); }; typedef QValueList<QIMPenCharMatch> QIMPenCharMatchList; class QIMPenCharSet { public: QIMPenCharSet(); QIMPenCharSet( const QString &fn ); bool isEmpty() const { return chars.isEmpty(); } unsigned int count() const { return chars.count(); } void clear() { chars.clear(); } void setDescription( const QString &d ) { desc = d; } QString description() const { return desc; } void setTitle( const QString &t ) { csTitle = t; } QString title() const { return csTitle; } QIMPenCharMatchList match( QIMPenChar *ch ); void addChar( QIMPenChar *ch ); void removeChar( QIMPenChar *ch ); QIMPenChar *at( int i ); unsigned maximumStrokes() const { return maxStrokes; } void up( QIMPenChar *ch ); void down( QIMPenChar *ch ); enum Domain { System, User }; enum Type { Unknown=0x00, Lower=0x01, Upper=0x02, Combining=0x04, Numeric=0x08, Punctuation=0x10, Symbol=0x20, Shortcut=0x40 }; const QIMPenCharList &characters() const { return chars; } void setType( Type t ) { csType = t; } Type type() const { return csType; } const QString &filename( Domain d ) const; void setFilename( const QString &fn, Domain d=System ); bool load( const QString &fn, Domain d=System ); bool save( Domain d=System ); protected: void markDeleted( uint ch ); protected: QString csTitle; QString desc; QString sysFilename; QString userFilename; Type csType; unsigned maxStrokes; QIMPenCharList chars; QIMPenCharMatchList matches; }; typedef QList<QIMPenCharSet> QIMPenCharSetList; typedef QListIterator<QIMPenCharSet> QIMPenCharSetIterator; #endif 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 @@ -1,141 +1,142 @@ /********************************************************************** ** Copyright (C) 2000 Trolltech AS. All rights reserved. ** ** This file is part of Qtopia Environment. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #include <qfile.h> #include <qtl.h> #include <math.h> #include <limits.h> #include <qdatastream.h> #include "qimpencombining.h" static unsigned int combiningSymbols[] = { '\\', '/', '^', '~', '\"', 'o' }; static unsigned int combiningChars[][7] = { // \ / ^ ~ " { 'A', 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5 }, { 'O', 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0000 }, { 'U', 0x00D9, 0x00DA, 0x00DB, 0x0000, 0x00DC, 0x0000 }, { 'E', 0x00C8, 0x00C9, 0x00CA, 0x0000, 0x00CB, 0x0000 }, { 'I', 0x00CC, 0x00CD, 0x00CE, 0x0000, 0x00CF, 0x0000 }, { 'a', 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5 }, { 'e', 0x00E8, 0x00E9, 0x00EA, 0x0000, 0x00EB, 0x0000 }, { 'i', 0x00EC, 0x00ED, 0x00EE, 0x0000, 0x00EF, 0x0000 }, { 'n', 0x0000, 0x0000, 0x0000, 0x00F1, 0x0000, 0x0000 }, { 'o', 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0000 }, { 'u', 0x00F9, 0x00FA, 0x00FB, 0x0000, 0x00FC, 0x0000 }, { 'y', 0x0000, 0x00FD, 0x0000, 0x0000, 0x00FF, 0x0000 }, { 0, 0, 0, 0, 0, 0, 0 } }; QIMPenCombining::QIMPenCombining() { } QIMPenCombining::QIMPenCombining( const QString &fn ) : QIMPenCharSet( fn ) { } void QIMPenCombining::addCombined( QIMPenCharSet *cs ) { unsigned int count = cs->count(); QIMPenCharIterator it( cs->characters() ); for ( ; it.current() && count; ++it, --count ) { QIMPenChar *pc = it.current(); if ( pc->testFlag( QIMPenChar::Deleted ) ) continue; int charIdx = findCombining( pc->character() ); if ( charIdx < 0 ) continue; for ( int i = 0; i < 6; i++ ) { if ( combiningChars[charIdx][i+1] ) { QIMPenCharIterator cit( chars ); for ( ; cit.current(); ++cit ) { QIMPenChar *accentPc = cit.current(); if ( accentPc->character() == combiningSymbols[i] ) { QIMPenChar *combined = combine( pc, accentPc ); combined->setCharacter( combiningChars[charIdx][i+1] ); + combined->setFlag( QIMPenChar::Combined ); cs->addChar( combined ); } } } } } } int QIMPenCombining::findCombining( unsigned int ch ) const { int i = 0; while ( combiningChars[i][0] ) { if ( combiningChars[i][0] == ch ) return i; i++; } return -1; } QIMPenChar *QIMPenCombining::combine( QIMPenChar *base, QIMPenChar *accent ) { QRect brect = base->boundingRect(); QRect arect = accent->boundingRect(); int offset; if ( accent->testFlag( QIMPenChar::CombineRight ) ) offset = brect.left() - arect.left() + brect.width() + 2; else offset = brect.left() - arect.left() + (brect.width() - arect.width())/2; QIMPenChar *combined = 0; if ( base->character() == 'i' ) { // Hack to remove the dot from i's when combining. if ( base->penStrokes().count() > 1 ) { combined = new QIMPenChar; QIMPenStrokeIterator it( base->penStrokes() ); for ( unsigned int i = 0; i < base->penStrokes().count()-1; ++it, i++ ) { QIMPenStroke *st = new QIMPenStroke( *(it.current()) ); combined->addStroke( st ); } combined->setFlag( QIMPenChar::System ); } } if ( !combined ) combined = new QIMPenChar( *base ); QIMPenStrokeIterator it( accent->penStrokes() ); for ( ; it.current(); ++it ) { QIMPenStroke *st = new QIMPenStroke( *(it.current()) ); st->setStartingPoint( st->startingPoint() + QPoint(offset, 0 )); combined->addStroke( st ); delete st; } return combined; } QIMPenChar *QIMPenCombining::penChar( int type ) { QIMPenCharIterator it( chars ); for ( ; it.current(); ++it ) { QIMPenChar *pc = it.current(); if ( pc->character() == combiningSymbols[type] ) return pc; } return 0; } |