00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "qimpenwidget.h"
00022 #include "qimpencombining.h"
00023 #include "qimpenmatch.h"
00024 #include "qimpenhelp.h"
00025
00026 #include <qpe/qpeapplication.h>
00027 #include <qpe/global.h>
00028 #include <qpe/config.h>
00029 #include <qpe/stringutil.h>
00030
00031 #include <qtextview.h>
00032 #include <qlabel.h>
00033 #include <qlistbox.h>
00034 #include <qcombobox.h>
00035 #include <qpushbutton.h>
00036 #include <qlayout.h>
00037 #include <qtimer.h>
00038 #include <qtextstream.h>
00039
00040
00041 static const char * const left_xpm[] = {
00042 "16 16 2 1",
00043 " c None",
00044 ". c #000000",
00045 " ",
00046 " ",
00047 " ",
00048 " . ",
00049 " .. ",
00050 " ... ",
00051 " .... ",
00052 " ..... ",
00053 " ...... ",
00054 " ..... ",
00055 " .... ",
00056 " ... ",
00057 " .. ",
00058 " . ",
00059 " ",
00060 " "};
00061
00062
00063
00064 static const char * const right_xpm[] = {
00065 "16 16 2 1",
00066 " c None",
00067 ". c #000000",
00068 " ",
00069 " ",
00070 " ",
00071 " . ",
00072 " .. ",
00073 " ... ",
00074 " .... ",
00075 " ..... ",
00076 " ...... ",
00077 " ..... ",
00078 " .... ",
00079 " ... ",
00080 " .. ",
00081 " . ",
00082 " ",
00083 " "};
00084
00085 class CharListItem : public QListBoxText
00086 {
00087 public:
00088 CharListItem( const QString &text, uint c )
00089 : QListBoxText( text )
00090 {
00091 _code = c;
00092 }
00093
00094 uint code() const { return _code; }
00095
00096 protected:
00097 uint _code;
00098 };
00099
00100 HandwritingHelp::HandwritingHelp( QIMPenProfile *p, QWidget *parent, const char *name, WFlags f )
00101 : QTabWidget( parent, name, f )
00102 {
00103 setCaption( tr("Handwriting Help") );
00104 QTextView *help = new QTextView( this );
00105 help->setFrameStyle( QFrame::NoFrame );
00106 help->setText(
00107 tr( "<ul><li>When you start to use the handwriting recogniser "
00108 "write slowly, accurately and firmly."
00109 "<li>Use the guide lines when drawing your characters."
00110 "<li>When drawing a character with multiple strokes, each "
00111 "successive stroke must be drawn before the grayed strokes are erased."
00112 "<li>Practice your handwriting using the handwriting trainer."
00113 "<li>When adding your own character templates make sure they "
00114 "are sufficiently different from other characters' templates."
00115 "</ul>") );
00116
00117 addTab( help, tr("Tips") );
00118
00119 HandwritingTrainer *trainer = new HandwritingTrainer( p, this );
00120 addTab( trainer, tr("Trainer") );
00121 }
00122
00123 void HandwritingHelp::showEvent( QShowEvent * )
00124 {
00125 Global::hideInputMethod();
00126 }
00127
00128 void HandwritingHelp::hideEvent( QHideEvent * )
00129 {
00130 Global::showInputMethod();
00131 }
00132
00133
00134
00135 HandwritingTrainer::HandwritingTrainer( QIMPenProfile *p, QWidget *parent, const char *name )
00136 : QWidget( parent, name ), profile(p)
00137 {
00138 QGridLayout *gl = new QGridLayout( this, 4, 2, 0, 4 );
00139 gl->setColStretch( 1, 1 );
00140 gl->setRowStretch(3, 1);
00141
00142 charSetCombo = new QComboBox( this );
00143 gl->addMultiCellWidget( charSetCombo, 0, 0, 0, 1 );
00144 connect( charSetCombo, SIGNAL(activated(int)), SLOT(selectCharSet(int)));
00145 QIMPenCharSetIterator it( profile->charSets() );
00146 for ( ; it.current(); ++it ) {
00147 if ( ! it.current()->hidden() )
00148 charSetCombo->insertItem( it.current()->description() );
00149 }
00150
00151 charList = new QListBox( this );
00152 charList->setHScrollBarMode( QListBox::AlwaysOff );
00153 charList->setFixedWidth(80);
00154 connect( charList, SIGNAL(highlighted(int)), this, SLOT(selectChar(int)) );
00155 gl->addWidget(charList, 1, 0);
00156
00157 result = new QLabel( this );
00158 result->setAlignment(AlignLeft | AlignVCenter | WordBreak);
00159 result->setText(
00160 tr( "Select a reference character from the list. Practice writing in "
00161 "the area on the right."));
00162 gl->addMultiCellWidget(result, 1, 2, 1, 1);
00163
00164 matcher = new QIMPenMatch( this );
00165 matcher->setCharSet( currentSet );
00166 connect( matcher, SIGNAL(noMatch()), this, SLOT(noMatch()) );
00167 connect( matcher, SIGNAL(matchedCharacters(const QIMPenCharMatchList&)),
00168 this, SLOT(matched(const QIMPenCharMatchList&)) );
00169
00170 QHBoxLayout *hb = new QHBoxLayout();
00171 gl->addLayout( hb, 2, 0 );
00172 prevBtn = new QPushButton( this );
00173 prevBtn->setPixmap( QPixmap( (const char **)left_xpm ) );
00174 connect( prevBtn, SIGNAL(clicked()), SLOT(prevChar()));
00175 hb->addWidget( prevBtn );
00176
00177 nextBtn = new QPushButton( this );
00178 nextBtn->setPixmap( QPixmap( (const char **)right_xpm ) );
00179 connect( nextBtn, SIGNAL(clicked()), SLOT(nextChar()));
00180 hb->addWidget( nextBtn );
00181
00182 refPw = new QIMPenWidget( this );
00183 refPw->setReadOnly( TRUE );
00184 gl->addWidget( refPw, 3, 0 );
00185
00186 pracPw = new QIMPenWidget( this );
00187 connect( matcher, SIGNAL(removeStroke()), pracPw, SLOT(removeStroke()) );
00188 connect( pracPw, SIGNAL(beginStroke()),
00189 this, SLOT(beginStroke()) );
00190 connect( pracPw, SIGNAL(stroke(QIMPenStroke*)),
00191 this, SLOT(strokeEntered(QIMPenStroke*)) );
00192 connect( pracPw, SIGNAL(beginStroke()),
00193 matcher, SLOT(beginStroke()) );
00194 connect( pracPw, SIGNAL(stroke(QIMPenStroke*)),
00195 matcher, SLOT(strokeEntered(QIMPenStroke*)) );
00196 gl->addWidget( pracPw, 3, 1 );
00197
00198 redrawTimer = new QTimer( this );
00199 connect( redrawTimer, SIGNAL(timeout()), this, SLOT(redrawChar()) );
00200 redrawTimer->start( 5000 );
00201
00202 currentSet = 0;
00203 charSetCombo->setCurrentItem( 1 );
00204 selectCharSet( 1 );
00205 }
00206
00207 HandwritingTrainer::~HandwritingTrainer()
00208 {
00209 }
00210
00211 void HandwritingTrainer::showEvent( QShowEvent * )
00212 {
00213 redrawChar();
00214 redrawTimer->start( 5000 );
00215 }
00216
00217 void HandwritingTrainer::setCurrentChar( QIMPenChar *c )
00218 {
00219 currentChar = c;
00220 refPw->showCharacter( currentChar );
00221 pracPw->clear();
00222 if ( currentChar ) {
00223 prevBtn->setEnabled( findPrev() != 0 );
00224 nextBtn->setEnabled( findNext() != 0 );
00225 }
00226 redrawTimer->start( 5000 );
00227 }
00228
00229 void HandwritingTrainer::selectChar( int i )
00230 {
00231 static int last_char = 0;
00232
00233 if (last_char != i) {
00234 result->setText("");
00235 }
00236
00237 currentChar = 0;
00238 currentCode = ((CharListItem *)charList->item(i))->code();
00239 QIMPenCharIterator it(currentSet->characters() );
00240 for ( ; it.current(); ++it ) {
00241 if ( it.current()->character() == currentCode &&
00242 !it.current()->testFlag( QIMPenChar::Deleted ) ) {
00243 setCurrentChar( it.current() );
00244 break;
00245 }
00246 }
00247 if ( !it.current() )
00248 setCurrentChar( 0 );
00249 }
00250
00251 void HandwritingTrainer::selectCharSet( int i )
00252 {
00253 if ( currentSet ) {
00254 refPw->removeCharSet( 0 );
00255 pracPw->removeCharSet( 0 );
00256 result->setText("");
00257 }
00258 currentSet = profile->charSets().at( i );
00259 fillCharList();
00260 refPw->insertCharSet( currentSet );
00261 pracPw->insertCharSet( currentSet );
00262 matcher->setCharSet( currentSet );
00263 if ( charList->count() ) {
00264 charList->setSelected( 0, TRUE );
00265 selectChar(0);
00266 }
00267 }
00268
00269 void HandwritingTrainer::noMatch()
00270 {
00271 result->setText( tr("No match") );
00272 }
00273
00274 void HandwritingTrainer::matched( const QIMPenCharMatchList &ml )
00275 {
00276 int maxErr = 20000 + (*ml.begin()).penChar->strokeLength(0) * 1000;
00277 int baseErr = (*ml.begin()).penChar->strokeLength(0) * 250;
00278 unsigned int numStrokes = (*ml.begin()).penChar->strokeCount();
00279 QIMPenCharMatchList::ConstIterator it;
00280
00281
00282
00283
00284
00285
00286
00287
00288 int i;
00289 QString res;
00290 QTextStream ts(&res, IO_WriteOnly);
00291 ts << "<qt>" << tr("Matched: ");
00292 for ( i = 0, it = ml.begin(); it != ml.end() && i < 4; ++it, i++ ) {
00293 if ( (*it).penChar->strokeCount() == numStrokes ) {
00294 int rate = 100 - ( ((*it).error - baseErr) * 100 ) / maxErr;
00295 if ( it != ml.begin() ) {
00296 if ( rate < -10 )
00297 continue;
00298 ts << "<br>";
00299 ts << tr("Similar to: ");
00300 }
00301 ts << "<big>";
00302 if ( (*it).penChar->character() == currentChar->character() )
00303 ts << "<b>";
00304 ts << Qtopia::escapeString((*it).penChar->name());
00305 ts << " (" << rateString(rate) << ")";
00306 if ( (*it).penChar->character() == currentChar->character() )
00307 ts << "</b>";
00308 ts << "</big>";
00309 }
00310 }
00311 ts << "</qt>";
00312 result->setText( res );
00313 }
00314
00315 QString HandwritingTrainer::rateString( int rate ) const
00316 {
00317 if ( rate < 1 )
00318 rate = 1;
00319 if ( rate > 100 )
00320 rate = 100;
00321 return tr("%1%").arg(rate);
00322 }
00323
00324 void HandwritingTrainer::prevChar()
00325 {
00326 QIMPenChar *pc = findPrev();
00327 if ( pc )
00328 setCurrentChar( pc );
00329 }
00330
00331 void HandwritingTrainer::nextChar()
00332 {
00333 QIMPenChar *pc = findNext();
00334 if ( pc )
00335 setCurrentChar( pc );
00336 }
00337
00338 void HandwritingTrainer::redrawChar()
00339 {
00340 if ( currentChar )
00341 refPw->showCharacter( currentChar );
00342 }
00343
00344 void HandwritingTrainer::beginStroke()
00345 {
00346 redrawTimer->start( 5000 );
00347 }
00348
00349 void HandwritingTrainer::strokeEntered( QIMPenStroke * )
00350 {
00351 pracPw->greyStroke();
00352 }
00353
00354 QIMPenChar *HandwritingTrainer::findPrev()
00355 {
00356 if ( !currentChar )
00357 return 0;
00358 QIMPenCharIterator it( currentSet->characters() );
00359 bool found = FALSE;
00360 for ( it.toLast(); it.current(); --it ) {
00361 if ( !found && it.current() == currentChar )
00362 found = TRUE;
00363 else if ( found && it.current()->character() == currentCode &&
00364 !it.current()->testFlag( QIMPenChar::Deleted ) ) {
00365 return it.current();
00366 }
00367 }
00368
00369 return 0;
00370 }
00371
00372 QIMPenChar *HandwritingTrainer::findNext()
00373 {
00374 if ( !currentChar )
00375 return 0;
00376 QIMPenCharIterator it( currentSet->characters() );
00377 bool found = FALSE;
00378 for ( ; it.current(); ++it ) {
00379 if ( !found && it.current() == currentChar )
00380 found = TRUE;
00381 else if ( found && it.current()->character() == currentCode &&
00382 !it.current()->testFlag( QIMPenChar::Deleted ) ) {
00383 return it.current();
00384 }
00385 }
00386
00387 return 0;
00388 }
00389
00390 void HandwritingTrainer::fillCharList()
00391 {
00392 charList->clear();
00393 QIMPenCharIterator it( currentSet->characters() );
00394 CharListItem *li = 0;
00395 for ( ; it.current(); ++it ) {
00396 uint ch = it.current()->character();
00397 QString n = it.current()->name();
00398 if ( !n.isEmpty() )
00399 li = new CharListItem( n, ch );
00400 if ( li ) {
00401 CharListItem *i = (CharListItem *)charList->findItem( li->text() );
00402 if ( !i || i->code() != ch ) {
00403 charList->insertItem( li );
00404 } else {
00405 delete li;
00406 li = 0;
00407 }
00408 }
00409 }
00410 currentChar = 0;
00411 }
00412