summaryrefslogtreecommitdiff
path: root/inputmethods
Side-by-side diff
Diffstat (limited to 'inputmethods') (more/less context) (ignore whitespace changes)
-rw-r--r--inputmethods/handwriting/qimpenchar.cpp9
-rw-r--r--inputmethods/handwriting/qimpeninput.cpp24
-rw-r--r--inputmethods/handwriting/qimpenmatch.cpp35
-rw-r--r--inputmethods/handwriting/qimpenstroke.cpp15
-rw-r--r--inputmethods/handwriting/qimpenwordpick.cpp2
5 files changed, 45 insertions, 40 deletions
diff --git a/inputmethods/handwriting/qimpenchar.cpp b/inputmethods/handwriting/qimpenchar.cpp
index 929f370..db5d135 100644
--- a/inputmethods/handwriting/qimpenchar.cpp
+++ b/inputmethods/handwriting/qimpenchar.cpp
@@ -1,92 +1,93 @@
/**********************************************************************
** 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"
+#include "opie2/odebug.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;
@@ -106,144 +107,144 @@ QString QIMPenChar::name() const
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 );
+ odebug << "char " << pen->ch <<", stroke starting pt diff excessive" << oendl;
#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() );
+ odebug << "char: " << pen->ch << ", maxErr " << maxErr << ", diff " << diff << ", " << strokes.count() << oendl;
#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 )
@@ -397,115 +398,115 @@ bool QIMPenCharSet::save( Domain d )
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() );
+
+ odebug << "Match: \'" << (*it).penChar->character() "\', error " << (*it).error ", strokes " <<(*it).penChar->penStrokes().count() << oendl;
}
*/
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/qimpeninput.cpp b/inputmethods/handwriting/qimpeninput.cpp
index d073cdf..6ea1bb4 100644
--- a/inputmethods/handwriting/qimpeninput.cpp
+++ b/inputmethods/handwriting/qimpeninput.cpp
@@ -1,102 +1,103 @@
/**********************************************************************
** Copyright (C) 2000-2002 Trolltech AS. All rights reserved.
**
** This file is part of the 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 "qimpenwidget.h"
#include "qimpensetup.h"
#include "qimpeninput.h"
#include "qimpencombining.h"
#include "qimpenwordpick.h"
#include "qimpenmatch.h"
#include "qimpenhelp.h"
#include <qpe/qpeapplication.h>
#include <qpe/qdawg.h>
#include <qpe/config.h>
#include <qpe/global.h>
#include <qlayout.h>
#include <qpushbutton.h>
#include <qlabel.h>
#include <qtimer.h>
#include <qdir.h>
+#include <opie2/odebug.h>
#include <limits.h>
// We'll use little pixmaps for the buttons to save screen space.
/* XPM */
static const char * const pen_xpm[] = {
"12 12 4 1",
" c None",
". c #000000",
"+ c #FFFFFF",
"@ c #808080",
" . ",
" .+. ",
" ..@@.",
" .+@.. ",
" .+@@. ",
" .+@@. ",
" .+@@. ",
" .@.@. ",
" .@@. ",
" .... ",
" .. ",
" "};
/* XPM */
static char * bs_xpm[] = {
"12 12 5 1",
" c None",
". c #333333",
"+ c #000000",
"@ c #FFFFFF",
"# c #666666",
" ",
" ",
" ",
" . ",
" ++ ",
" +@#+++++. ",
" +@@@@@@@@+ ",
" +@#+++++. ",
" ++ ",
" . ",
" ",
" "};
/* XPM */
static char * enter_xpm[] = {
"12 12 5 1",
" c None",
". c #333333",
"+ c #000000",
"@ c #FFFFFF",
"# c #666666",
" ",
" .+. ",
" +@+ ",
" . +@+ ",
" ++ +@+ ",
" +@#++++@+ ",
" +@@@@@@@@+ ",
" +@#+++++. ",
@@ -258,258 +259,259 @@ void QIMPenInput::loadProfiles()
config.setGroup( "Settings" );
QString prof = config.readEntry( "Profile", "Default" );
selectProfile( prof );
}
void QIMPenInput::selectProfile( const QString &name )
{
QListIterator<QIMPenProfile> it( profileList );
for ( ; it.current(); ++it ) {
if ( it.current()->name() == name ) {
profile = it.current();
break;
}
}
if ( !it.current() )
return;
pw->clearCharSets();
baseSets.clear();
matcher->setMultiStrokeTimeout( profile->multiStrokeTimeout() );
matcher->setWordMatchingEnabled( profile->matchWords() );
if ( !Global::fixedDawg().root() || !matcher->isWordMatchingEnabled() )
wordPicker->hide();
else
wordPicker->show();
if ( profile->uppercase() && profile->style() == QIMPenProfile::BothCases ) {
baseSets.append( profile->uppercase() );
pw->insertCharSet( profile->uppercase() );
}
if ( profile->lowercase() ) {
baseSets.append( profile->lowercase() );
pw->insertCharSet( profile->lowercase(), profile->style() == QIMPenProfile::BothCases ? 1 : 2 );
}
if ( profile->numeric() ) {
baseSets.append( profile->numeric() );
pw->insertCharSet( profile->numeric() );
}
if ( helpDlg )
delete (HandwritingHelp*) helpDlg;
}
void QIMPenInput::wordPicked( const QString &w )
{
int bs = matcher->word().length();
for ( int i = 0; i < bs; i++ )
keypress( Qt::Key_Backspace << 16 );
for ( unsigned int i = 0; i < w.length(); i++ )
keypress( w[i].unicode() );
matcher->resetState();
wordPicker->clear();
}
void QIMPenInput::selectCharSet( int idx )
{
if ( mode == Switch ) {
- //qDebug( "Switch back to normal" );
+ //odebug << "Switch back to normal" << oendl;
pw->changeCharSet( baseSets.at(currCharSet), currCharSet );
mode = Normal;
}
currCharSet = idx;
}
void QIMPenInput::beginStroke()
{
}
void QIMPenInput::strokeEntered( QIMPenStroke * )
{
pw->greyStroke();
}
void QIMPenInput::erase()
{
keypress( Qt::Key_Backspace << 16 );
}
void QIMPenInput::matchedCharacters( const QIMPenCharMatchList &cl )
{
const QIMPenChar *ch = cl.first().penChar;
int scan = ch->character() >> 16;
if ( scan < QIMPenChar::ModeBase )
return;
// We matched a special character...
switch ( scan ) {
case QIMPenChar::Caps:
if ( profile->style() == QIMPenProfile::ToggleCases ) {
-// qDebug( "Caps" );
+// odebug << "Caps" << oendl;
+//
if ( mode == SwitchLock ) {
-// qDebug( "Switch to normal" );
+// odebug << "Switch to normal" << oendl;
pw->changeCharSet( profile->lowercase(), currCharSet );
mode = Switch;
} else {
-// qDebug( "Switch to upper" );
+// odebug << "Switch to upper" << oendl;
pw->changeCharSet( profile->uppercase(), currCharSet );
mode = Switch;
}
}
break;
case QIMPenChar::CapsLock:
if ( profile->style() == QIMPenProfile::ToggleCases ) {
-// qDebug( "CapsLock" );
+// odebug << "CapsLock" << oendl;
if ( mode == Switch &&
baseSets.at(currCharSet) == profile->uppercase() ) {
-// qDebug( "Switch to normal" );
+// odebug << "Switch to normal" << oendl;
pw->changeCharSet( profile->lowercase(), currCharSet );
// change our base set back to lower.
baseSets.remove( currCharSet );
baseSets.insert( currCharSet, profile->lowercase() );
mode = Normal;
} else {
-// qDebug( "Switch to caps lock" );
+// odebug << "Switch to caps lock" << oendl;
pw->changeCharSet( profile->uppercase(), currCharSet );
// change our base set to upper.
baseSets.remove( currCharSet );
baseSets.insert( currCharSet, profile->uppercase() );
mode = SwitchLock;
}
}
break;
case QIMPenChar::Punctuation:
if ( profile->punctuation() ) {
- //qDebug( "Switch to punctuation" );
+ //odebug << "Switch to punctuation" << oendl;
pw->changeCharSet( profile->punctuation(), currCharSet );
mode = Switch;
}
break;
case QIMPenChar::Symbol:
if ( profile->symbol() ) {
- //qDebug( "Switch to symbol" );
+ //odebug << "Switch to symbol" << oendl ;
pw->changeCharSet( profile->symbol(), currCharSet );
mode = Switch;
}
break;
case QIMPenChar::Shortcut:
if ( shortcutCharSet ) {
pw->changeCharSet( shortcutCharSet, currCharSet );
mode = Switch;
}
break;
case QIMPenChar::Extended:
handleExtended( ch->data() );
break;
}
}
void QIMPenInput::keypress( uint scan_uni )
{
int scan = scan_uni >> 16;
if ( !scan ) {
if ( scan_uni >= 'a' && scan_uni <= 'z' ) {
scan = Qt::Key_A + scan_uni - 'a';
} else if ( scan_uni >= 'A' && scan_uni <= 'Z' ) {
scan = Qt::Key_A + scan_uni - 'A';
} else if ( scan_uni == ' ' ) {
scan = Qt::Key_Space;
}
}
switch ( scan ) {
case Key_Tab:
scan_uni = 9;
break;
case Key_Return:
scan_uni = 13;
break;
case Key_Backspace:
scan_uni = 8;
break;
case Key_Escape:
scan_uni = 27;
break;
default:
break;
}
if ( mode == Switch ) {
-// qDebug( "Switch back to normal" );
+// odebug << "Switch back to normal" << oendl ;
pw->changeCharSet( baseSets.at(currCharSet), currCharSet );
if ( baseSets.at(currCharSet) == profile->uppercase() )
mode = SwitchLock;
else
mode = Normal;
}
emit key( scan_uni&0xffff, scan, 0, true, false );
emit key( scan_uni&0xffff, scan, 0, false, false );
}
void QIMPenInput::handleExtended( const QString &ex )
{
if ( ex.find( "Select" ) == 0 ) {
QString set = ex.mid( 7 );
- qDebug( "Select new profile: %s", set.latin1() );
+ odebug << "Select new profile: " << set.latin1() << oendl;
selectProfile( set );
}
}
void QIMPenInput::help()
{
if ( helpDlg )
delete (HandwritingHelp*) helpDlg;
helpDlg = new HandwritingHelp( profile, 0, 0, WDestructiveClose );
helpDlg->showMaximized();
helpDlg->show();
helpDlg->raise();
}
/*!
Open the setup dialog
*/
void QIMPenInput::setup()
{
if ( !setupDlg ) {
// We are working with our copy of the char sets here.
setupDlg = new QIMPenSetup( profile, 0, 0, TRUE );
setupDlg->editor()->selectCharSet( profile->charSets().at(1) ); // lower case? This is crap.
if ( qApp->desktop()->width() < 640 )
setupDlg->showMaximized();
Global::hideInputMethod();
setupDlg->exec();
loadProfiles();
delete setupDlg;
setupDlg = 0;
Global::showInputMethod();
} else {
setupDlg->raise();
}
}
void QIMPenInput::backspace()
{
keypress( Qt::Key_Backspace << 16 );
matcher->backspace();
}
void QIMPenInput::enter()
{
keypress( Qt::Key_Return << 16 );
matcher->resetState();
}
void QIMPenInput::resetState()
{
matcher->resetState();
}
diff --git a/inputmethods/handwriting/qimpenmatch.cpp b/inputmethods/handwriting/qimpenmatch.cpp
index 0d3e25a..a0448b6 100644
--- a/inputmethods/handwriting/qimpenmatch.cpp
+++ b/inputmethods/handwriting/qimpenmatch.cpp
@@ -1,352 +1,353 @@
/**********************************************************************
** 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 "qimpenmatch.h"
#include <qpe/qdawg.h>
#include <qpe/global.h>
#include <qapplication.h>
#include <qtimer.h>
+#include <opie2/odebug.h>
#include <limits.h>
#define ERROR_THRESHOLD 200000
#define LOOKAHEAD_ERROR 2500
//#define DEBUG_QIMPEN
QIMPenMatch::QIMPenMatch( QObject *parent, const char *name )
: QObject( parent, name )
{
strokes.setAutoDelete( TRUE );
wordChars.setAutoDelete( TRUE );
wordMatches.setAutoDelete( TRUE );
multiTimer = new QTimer( this );
connect( multiTimer, SIGNAL(timeout()), this, SLOT(endMulti()) );
prevMatchChar = 0;
prevMatchError = INT_MAX;
charSet = 0;
multiCharSet = 0;
multiTimeout = 500;
canErase = FALSE;
doWordMatching = true;
}
QIMPenMatch::~QIMPenMatch()
{
}
void QIMPenMatch::setCharSet( QIMPenCharSet *cs )
{
charSet = cs;
}
void QIMPenMatch::beginStroke()
{
multiTimer->stop();
}
void QIMPenMatch::strokeEntered( QIMPenStroke *st )
{
#ifdef DEBUG_QIMPEN
- qDebug( "---------- new stroke -------------" );
+ odebug << "---------- new stroke -------------" << oendl;
#endif
strokes.append( new QIMPenStroke( *st ) );
QIMPenChar testChar;
QIMPenStrokeIterator it(strokes);
for ( ; it.current(); ++it ) {
testChar.addStroke( it.current() );
}
QIMPenCharMatchList ml;
if ( strokes.count() > 1 && multiCharSet ) {
#ifdef DEBUG_QIMPEN
- qDebug( "Matching against multi set" );
+ odebug << "Matching against multi set" << oendl;
#endif
ml = multiCharSet->match( &testChar );
} else {
#ifdef DEBUG_QIMPEN
- qDebug( "Matching against single set" );
+ odebug << "Matching against single set" << oendl;
#endif
ml = charSet->match( &testChar );
}
processMatches( ml );
}
void QIMPenMatch::processMatches( QIMPenCharMatchList &ml )
{
#ifdef DEBUG_QIMPEN
- qDebug( "Entering strokes.count() = %d", strokes.count() );
+ odebug << "Entering strokes.count() = " << strokes.count() << oendl;
#endif
QIMPenCharMatch candidate1 = { INT_MAX, 0 };
QIMPenCharMatch candidate2 = { INT_MAX, 0 };
QIMPenCharMatchList ml2;
if ( ml.count() ) {//&&
// ml.first().penChar->penStrokes().count() == strokes.count() ) {
candidate1 = ml.first();
#ifdef DEBUG_QIMPEN
- qDebug( QString("Candidate1 = %1").arg(QChar(candidate1.penChar->character())) );
+ odebug << "Candidate1 = " << candidate1.penChar->character() << oendl;
#endif
}
if ( strokes.count() > 1 ) {
// See if the last stroke can match a new character
QIMPenChar testChar;
QIMPenStroke *st = strokes.at(strokes.count()-1);
testChar.addStroke( st );
ml2 = charSet->match( &testChar );
if ( ml2.count() ) {
candidate2 = ml2.first();
#ifdef DEBUG_QIMPEN
- qDebug( QString("Candidate2 = %1").arg(QChar(candidate2.penChar->character())) );
+ odebug << "Candidate2 = " << candidate2.penChar->character() << oendl;
#endif
}
}
bool eraseLast = FALSE;
bool output = TRUE;
if ( candidate1.penChar && candidate2.penChar ) {
// Hmmm, a multi-stroke or a new character are both possible.
// Bias the multi-stroke case.
if ( QMAX(candidate2.error, prevMatchError)*3 < candidate1.error ) {
int i = strokes.count()-1;
while ( i-- ) {
strokes.removeFirst();
emit removeStroke();
}
prevMatchChar = candidate2.penChar;
prevMatchError = candidate2.error;
multiCharSet = charSet;
ml = ml2;
#ifdef DEBUG_QIMPEN
- qDebug( "** Using Candidate2" );
+ odebug << "** Using Candidate2" << oendl;
#endif
} else {
if ( (prevMatchChar->character() >> 16) != Qt::Key_Backspace &&
(prevMatchChar->character() >> 16) < QIMPenChar::ModeBase )
eraseLast = TRUE;
prevMatchChar = candidate1.penChar;
prevMatchError = candidate1.error;
#ifdef DEBUG_QIMPEN
- qDebug( "** Using Candidate1, with erase" );
+ odebug << "** Using Candidate1, with erase" << oendl;
#endif
}
} else if ( candidate1.penChar ) {
if ( strokes.count() != 1 )
eraseLast = TRUE;
else
multiCharSet = charSet;
prevMatchChar = candidate1.penChar;
prevMatchError = candidate1.error;
#ifdef DEBUG_QIMPEN
- qDebug( "** Using Candidate1" );
+ odebug << "** Using Candidate1" << oendl;
#endif
} else if ( candidate2.penChar ) {
int i = strokes.count()-1;
while ( i-- ) {
strokes.removeFirst();
emit removeStroke();
}
prevMatchChar = candidate2.penChar;
prevMatchError = candidate2.error;
multiCharSet = charSet;
ml = ml2;
#ifdef DEBUG_QIMPEN
- qDebug( "** Using Candidate2" );
+ odebug << "** Using Candidate2" << oendl;
#endif
} else {
if ( !ml.count() ) {
#ifdef DEBUG_QIMPEN
- qDebug( "** Failed" );
+ odebug << "** Failed" << oendl;
#endif
canErase = FALSE;
} else {
#ifdef DEBUG_QIMPEN
- qDebug( "Need more strokes" );
+ odebug << "Need more strokes" << oendl;
#endif
if ( strokes.count() == 1 )
canErase = FALSE;
multiCharSet = charSet;
}
output = FALSE;
emit noMatch();
}
if ( eraseLast && canErase ) {
#ifdef DEBUG_QIMPEN
- qDebug( "deleting last" );
+ odebug << "deleting last" << oendl;
#endif
emit erase();
wordChars.removeLast();
wordEntered.truncate( wordEntered.length() - 1 );
}
if ( output ) {
emit matchedCharacters( ml );
uint code = prevMatchChar->character() >> 16;
if ( code < QIMPenChar::ModeBase ) {
updateWordMatch( ml );
emit keypress( prevMatchChar->character() );
}
canErase = TRUE;
}
if ( strokes.count() )
multiTimer->start( multiTimeout, TRUE );
}
void QIMPenMatch::updateWordMatch( QIMPenCharMatchList &ml )
{
if ( !ml.count() || !doWordMatching )
return;
int ch = ml.first().penChar->character();
QChar qch( ch );
int code = ch >> 16;
if ( qch.isPunct() || qch.isSpace() ||
code == Qt::Key_Enter || code == Qt::Key_Return ||
code == Qt::Key_Tab || code == Qt::Key_Escape ) {
-// qDebug( "Word Matching: Clearing word" );
+// odebug << "Word Matching: Clearing word" << oendl;
wordChars.clear();
wordMatches.clear();
wordEntered = QString();
} else if ( code == Qt::Key_Backspace ) {
- //qDebug( "Word Matching: Handle backspace" );
+ //odebug << "Word Matching: Handle backspace" << oendl;
wordChars.removeLast();
wordEntered.truncate( wordEntered.length() - 1 );
matchWords();
} else {
QIMPenChar *matchCh;
wordChars.append( new QIMPenCharMatchList() );
wordEntered += ml.first().penChar->character();
QIMPenCharMatchList::Iterator it;
for ( it = ml.begin(); it != ml.end(); ++it ) {
matchCh = (*it).penChar;
if ( matchCh->penStrokes().count() == strokes.count() ) {
QChar ch(matchCh->character());
if ( !ch.isPunct() && !ch.isSpace() ) {
wordChars.last()->append( QIMPenCharMatch( (*it) ) );
}
}
}
matchWords();
}
if ( !wordMatches.count() || wordMatches.getFirst()->word != wordEntered )
wordMatches.prepend( new MatchWord( wordEntered, 0 ) );
emit matchedWords( wordMatches );
}
void QIMPenMatch::matchWords()
{
if ( wordEntered.length() > 0 ) {
// more leaniency if we don't have many matches
if ( badMatches < 200 )
errorThreshold += (200 - badMatches) * 100;
} else
errorThreshold = ERROR_THRESHOLD;
wordMatches.clear();
goodMatches = 0;
badMatches = 0;
if ( wordChars.count() > 0 ) {
maxGuess = (int)wordChars.count() * 2;
if ( maxGuess < 3 )
maxGuess = 3;
QString str;
scanDict( Global::fixedDawg().root(), 0, str, 0 );
/*
QListIterator<MatchWord> it( wordMatches);
for ( ; it.current(); ++it ) {
- qDebug( QString("Match word: %1").arg(it.current()->word) );
+ odebug << "Match word: " << it.current()->word << oendl;
}
*/
}
- //qDebug( "Possibles: Good %d, total %d", goodMatches, wordMatches.count() );
+ //odebug << "Possibles: Good " << goodMatches << ", total " << wordMatches.count() << oendl;
wordMatches.sort();
}
void QIMPenMatch::scanDict( const QDawg::Node* n, int ipos, const QString& str, int error )
{
if ( !n )
return;
if ( error / (ipos+1) > errorThreshold )
return;
while (n) {
if ( goodMatches > 20 )
break;
if ( ipos < (int)wordChars.count() ) {
int i;
QChar testCh = QChar(n->letter());
QIMPenCharMatchList::Iterator it;
for ( i = 0, it = wordChars.at(ipos)->begin();
it != wordChars.at(ipos)->end() && i < 8; ++it, i++ ) {
QChar ch( (*it).penChar->character() );
if ( ch == testCh || ( !ipos && ch.lower() == testCh.lower() ) ) {
int newerr = error + (*it).error;
if ( testCh.category() == QChar::Letter_Uppercase )
ch = testCh;
QString newstr( str + ch );
if ( n->isWord() && ipos == (int)wordChars.count() - 1 ) {
wordMatches.append( new MatchWord( newstr, newerr ) );
goodMatches++;
}
scanDict( n->jump(), ipos+1, newstr, newerr );
}
}
} else if ( badMatches < 200 && ipos < maxGuess ) {
int d = ipos - wordChars.count();
int newerr = error + ERROR_THRESHOLD + LOOKAHEAD_ERROR*d;
QString newstr( str + n->letter() );
if ( n->isWord() ) {
wordMatches.append( new MatchWord( newstr, newerr ) );
badMatches++;
}
scanDict( n->jump(), ipos+1, newstr, newerr );
}
n = n->next();
}
}
void QIMPenMatch::backspace()
{
wordChars.removeLast();
wordEntered.truncate( wordEntered.length() - 1 );
matchWords();
if ( !wordMatches.count() || wordMatches.getFirst()->word != wordEntered )
wordMatches.prepend( new MatchWord( wordEntered, 0 ) );
emit matchedWords( wordMatches );
if ( wordEntered.length() )
canErase = TRUE;
}
void QIMPenMatch::endMulti()
{
int i = strokes.count();
while ( i-- )
emit removeStroke();
strokes.clear();
diff --git a/inputmethods/handwriting/qimpenstroke.cpp b/inputmethods/handwriting/qimpenstroke.cpp
index 3567d6d..14e435a 100644
--- a/inputmethods/handwriting/qimpenstroke.cpp
+++ b/inputmethods/handwriting/qimpenstroke.cpp
@@ -1,314 +1,315 @@
/**********************************************************************
** 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 "qimpenstroke.h"
+#include "opie2/odebug.h"
#define QIMPEN_CORRELATION_POINTS 25
//#define DEBUG_QIMPEN
/*!
\class QIMPenStroke qimpenstroke.h
Handles a single stroke. Can calculate closeness of match to
another stroke.
*/
QIMPenStroke::QIMPenStroke()
{
}
QIMPenStroke::QIMPenStroke( const QIMPenStroke &st )
{
startPoint = st.startPoint;
lastPoint = st.lastPoint;
links = st.links.copy();
}
QIMPenStroke &QIMPenStroke::operator=( const QIMPenStroke &s )
{
clear();
- //qDebug( "copy strokes %d", s.links.count() );
+ //odebug << "copy strokes " << s.links.count() << oendl;
startPoint = s.startPoint;
lastPoint = s.lastPoint;
links = s.links.copy();
return *this;
}
void QIMPenStroke::clear()
{
startPoint = QPoint(0,0);
lastPoint = QPoint( 0, 0 );
links.resize( 0 );
tsig.resize( 0 );
dsig.resize( 0 );
asig.resize( 0 );
}
/*!
Begin inputting a new stroke.
*/
void QIMPenStroke::beginInput( QPoint p )
{
clear();
startPoint = p;
bounding = QRect();
internalAddPoint( p );
}
/*!
Add a point to the stroke's shape.
Returns TRUE if the point was successfully added.
*/
bool QIMPenStroke::addPoint( QPoint p )
{
if ( links.count() > 500 ) // sanity check (that the user is sane).
return FALSE;
int dx = p.x() - lastPoint.x();
int dy = p.y() - lastPoint.y();
if ( QABS( dx ) > 1 || QABS( dy ) > 1 ) {
// The point is not adjacent to the previous point, so we fill
// in with a straight line. Some kind of non-linear
// interpolation might be better.
int x = lastPoint.x();
int y = lastPoint.y();
int ix = 1;
int iy = 1;
if ( dx < 0 ) {
ix = -1;
dx = -dx;
}
if ( dy < 0 ) {
iy = -1;
dy = -dy;
}
int d = 0;
if ( dx < dy ) {
d = dx;
do {
y += iy;
d += dx;
if ( d > dy ) {
x += ix;
d -= dy;
}
internalAddPoint( QPoint( x, y ) );
} while ( y != p.y() );
} else {
d = dy;
do {
x += ix;
d += dy;
if ( d > dx ) {
y += iy;
d -= dx;
}
internalAddPoint( QPoint( x, y ) );
} while ( x != p.x() );
}
} else {
internalAddPoint( p );
}
return TRUE;
}
/*!
Finish inputting a stroke.
*/
void QIMPenStroke::endInput()
{
if ( links.count() < 3 ) {
QIMPenGlyphLink gl;
links.resize(1);
gl.dx = 1;
gl.dy = 0;
links[0] = gl;
}
- //qDebug("Points: %d", links.count() );
+ //odebug << "Points: " << links.count() << oendl;
}
/*!
Return an indicator of the closeness of this stroke to \a pen.
Lower value is better.
*/
unsigned int QIMPenStroke::match( QIMPenStroke *pen )
{
double lratio;
if ( links.count() > pen->links.count() )
lratio = (links.count()+2) / (pen->links.count()+2);
else
lratio = (pen->links.count()+2) / (links.count()+2);
lratio -= 1.0;
if ( lratio > 2.0 ) {
#ifdef DEBUG_QIMPEN
- qDebug( "stroke length too different" );
+ odebug << "stroke length too different" << oendl;
#endif
return 400000;
}
createSignatures();
pen->createSignatures();
// Starting point offset
int vdiff = QABS(startPoint.y() - pen->startPoint.y());
// Insanely offset?
if ( vdiff > 18 ) {
return 400000;
}
vdiff -= 4;
if ( vdiff < 0 )
vdiff = 0;
// Ending point offset
int evdiff = QABS(lastPoint.y() - pen->lastPoint.y());
// Insanely offset?
if ( evdiff > 20 ) {
return 400000;
}
evdiff -= 5;
if ( evdiff < 0 )
evdiff = 0;
// do a correlation with the three available signatures.
int err1 = INT_MAX;
int err2 = INT_MAX;
int err3 = INT_MAX;
// base has extra points at the start and end to enable
// correlation of a sliding window with the pen supplied.
QArray<int> base = createBase( tsig, 2 );
for ( int i = 0; i < 4; i++ ) {
int e = calcError( base, pen->tsig, i, TRUE );
if ( e < err1 )
err1 = e;
}
if ( err1 > 40 ) { // no need for more matching
#ifdef DEBUG_QIMPEN
- qDebug( "tsig too great: %d", err1 );
+ odebug << "tsig too great: " << err1 << oendl;
#endif
return 400000;
}
// maybe a sliding window is worthwhile for these too.
err2 = calcError( dsig, pen->dsig, 0, FALSE );
if ( err2 > 100 ) {
#ifdef DEBUG_QIMPEN
- qDebug( "dsig too great: %d", err2 );
+ odebug << "dsig too great: " << err2 << oendl;
#endif
return 400000;
}
err3 = calcError( asig, pen->asig, 0, TRUE );
if ( err3 > 60 ) {
#ifdef DEBUG_QIMPEN
- qDebug( "asig too great: %d", err3 );
+ odebug << "asig too great: " << err3 << oendl;
#endif
return 400000;
}
// Some magic numbers here - the addition reduces the weighting of
// the error and compensates for the different error scales. I
// consider the tangent signature to be the best indicator, so it
// has the most weight. This ain't rocket science.
// Basically, these numbers are the tuning factors.
unsigned int err = (err1+1) * ( err2 + 60 ) * ( err3 + 20 ) +
vdiff * 1000 + evdiff * 500 +
(unsigned int)(lratio * 5000.0);
#ifdef DEBUG_QIMPEN
- qDebug( "err %d ( %d, %d, %d, %d)", err, err1, err2, err3, vdiff );
+ odebug << "err " << err << "( " << err1 << ", " << err2 << ", " << err3 << ", " << vdiff << oendl;
#endif
return err;
}
/*!
Return the bounding rect of this stroke.
*/
QRect QIMPenStroke::boundingRect()
{
if ( !bounding.isValid() ) {
int x = startPoint.x();
int y = startPoint.y();
bounding = QRect( x, y, 1, 1 );
for ( unsigned i = 0; i < links.count(); i++ ) {
x += links[i].dx;
y += links[i].dy;
if ( x < bounding.left() )
bounding.setLeft( x );
if ( x > bounding.right() )
bounding.setRight( x );
if ( y < bounding.top() )
bounding.setTop( y );
if ( y > bounding.bottom() )
bounding.setBottom( y );
}
}
return bounding;
}
/*!
Perform a correlation of the supplied arrays. \a base should have
win.count() + 2 * off points to enable sliding \a win over the
\a base data. If \a t is TRUE, the comparison takes into account
the circular nature of the angular data.
Returns the best (lowest error) match.
*/
int QIMPenStroke::calcError( const QArray<int> &base,
const QArray<int> &win, int off, bool t )
{
int err = 0;
for ( unsigned i = 0; i < win.count(); i++ ) {
int d = QABS( base[i+off] - win[i] );
if ( t && d > 128 )
d -= 256;
err += QABS( d );
}
err /= win.count();
return err;
}
/*!
Creates signatures used in matching if not already created.
*/
void QIMPenStroke::createSignatures()
{
if ( tsig.isEmpty() )
diff --git a/inputmethods/handwriting/qimpenwordpick.cpp b/inputmethods/handwriting/qimpenwordpick.cpp
index 8ee103d..39745c6 100644
--- a/inputmethods/handwriting/qimpenwordpick.cpp
+++ b/inputmethods/handwriting/qimpenwordpick.cpp
@@ -43,71 +43,71 @@ void QIMPenWordPick::setWords( const QIMPenMatch::MatchWordList &w )
{
words.clear();
QListIterator<QIMPenMatch::MatchWord> it( w );
for ( ; it.current(); ++it ) {
words.append( it.current()->word );
}
repaint();
}
int QIMPenWordPick::onWord( QPoint p )
{
int x = 2;
int idx = 0;
for ( QStringList::Iterator it = words.begin(); it != words.end(); ++it ) {
QString word = *it;
int w = fontMetrics().width( word );
if ( x + w > width() )
break;
if ( p.x() > x-2 && p.x() < x + w + 2 )
return idx;
x += w + 5;
if ( !idx )
x += 3;
idx++;
}
return -1;
}
void QIMPenWordPick::paintEvent( QPaintEvent * )
{
QPainter p(this);
int x = 2;
int h = p.fontMetrics().ascent() + 1;
int idx = 0;
for ( QStringList::Iterator it = words.begin(); it != words.end(); ++it ) {
QString word = *it;
int w = p.fontMetrics().width( word );
if ( x + w > width() )
break;
if ( idx == clickWord ) {
p.fillRect( x, 0, w, height(), black );
p.setPen( white );
} else {
p.setPen( colorGroup().text() );
}
p.drawText( x, h, word );
x += w + 5;
if ( !idx )
x += 3;
idx++;
}
}
void QIMPenWordPick::mousePressEvent( QMouseEvent *e )
{
clickWord = onWord( e->pos() );
repaint();
}
void QIMPenWordPick::mouseReleaseEvent( QMouseEvent *e )
{
int wordIdx = onWord( e->pos() );
if ( wordIdx >= 0 && wordIdx == clickWord ) {
- //qDebug( "Clicked %s", words[wordIdx].latin1() );
+ //odebug << "Clicked " << words[wordIdx].latin1() << oendl;
emit wordClicked( words[wordIdx] );
}
clickWord = -1;
repaint();
}