summaryrefslogtreecommitdiff
path: root/inputmethods/handwriting/qimpenwidget.cpp
Side-by-side diff
Diffstat (limited to 'inputmethods/handwriting/qimpenwidget.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--inputmethods/handwriting/qimpenwidget.cpp446
1 files changed, 446 insertions, 0 deletions
diff --git a/inputmethods/handwriting/qimpenwidget.cpp b/inputmethods/handwriting/qimpenwidget.cpp
new file mode 100644
index 0000000..8f8f582
--- a/dev/null
+++ b/inputmethods/handwriting/qimpenwidget.cpp
@@ -0,0 +1,446 @@
+/**********************************************************************
+** 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 <qapplication.h>
+#include <qinputdialog.h>
+#include <qpainter.h>
+#include <qfile.h>
+#include <qdatastream.h>
+#include <qtimer.h>
+#include "qimpenchar.h"
+#include "qimpenwidget.h"
+
+#define TITLE_WIDTH 30 // ### magic
+
+/*!
+ \class QIMPenWidget qimpenwidget.h
+
+ Draws characters and allows input of characters.
+*/
+
+QIMPenWidget::QIMPenWidget( QWidget *parent )
+ : QWidget( parent )
+{
+ charSets.setAutoDelete( TRUE );
+ inputStroke = 0;
+ outputChar = 0;
+ outputStroke = 0;
+ mode = Waiting;
+ currCharSet = 0;
+ readOnly = FALSE;
+ strokes.setAutoDelete( TRUE );
+
+ timer = new QTimer(this);
+ connect( timer, SIGNAL(timeout()), SLOT(timeout()));
+
+ setBackgroundColor( qApp->palette().color( QPalette::Active,
+ QColorGroup::Base ) );
+ strokeColor = black;
+ setFixedHeight( 75 );
+}
+
+void QIMPenWidget::clear()
+{
+ timer->stop();
+ mode = Waiting;
+ QRect r( dirtyRect );
+ QIMPenStrokeIterator it( strokes );
+ while ( it.current() ) {
+ r |= it.current()->boundingRect();
+ ++it;
+ }
+ outputChar = 0;
+ outputStroke = 0;
+ strokes.clear();
+ if ( !r.isNull() ) {
+ r.moveBy( -2, -2 );
+ r.setSize( r.size() + QSize( 4, 4 ) );
+ repaint( r );
+ } else {
+ repaint();
+ }
+}
+
+void QIMPenWidget::removeStroke()
+{
+ QRect r( dirtyRect );
+ QIMPenStroke *st = strokes.getFirst();
+ QRect strokeRect;
+ if ( st )
+ strokeRect = st->boundingRect();
+ r |= strokeRect;
+ strokes.removeFirst();
+ if ( !r.isNull() ) {
+ r.moveBy( -2, -2 );
+ r.setSize( r.size() + QSize( 4, 4 ) );
+ repaint( r );
+ }
+}
+
+void QIMPenWidget::greyStroke()
+{
+ QRect r( dirtyRect );
+ QIMPenStroke *st = strokes.getLast();
+ QRect strokeRect;
+ if ( st )
+ strokeRect = st->boundingRect();
+ r |= strokeRect;
+ QColor oldCol = strokeColor;
+ strokeColor = gray;
+ if ( !r.isNull() ) {
+ r.moveBy( -2, -2 );
+ r.setSize( r.size() + QSize( 4, 4 ) );
+ repaint( r );
+ }
+ strokeColor = oldCol;
+}
+
+/*!
+ Insert a character set into the list.
+*/
+void QIMPenWidget::insertCharSet( QIMPenCharSet *cs, int stretch, int pos )
+{
+ CharSetEntry *e = new CharSetEntry;
+ e->cs = cs;
+ e->stretch = stretch;
+ if ( pos < 0 )
+ pos = charSets.count();
+ charSets.insert( pos, e );
+ currCharSet = 0;
+ emit changeCharSet( currCharSet );
+ emit changeCharSet( charSets.at(currCharSet)->cs );
+ totalStretch = 0;
+ CharSetEntryIterator it( charSets );
+ for ( ; it.current(); ++it )
+ totalStretch += it.current()->stretch;
+ update();
+}
+
+/*!
+ Remove a character set from the list.
+*/
+void QIMPenWidget::removeCharSet( int pos )
+{
+ if ( pos >= 0 && pos < (int)charSets.count() ) {
+ charSets.remove( pos );
+ currCharSet = 0;
+ if ( charSets.count() ) {
+ emit changeCharSet( currCharSet );
+ emit changeCharSet( charSets.at(currCharSet)->cs );
+ }
+ totalStretch = 0;
+ CharSetEntryIterator it( charSets );
+ for ( ; it.current(); ++it )
+ totalStretch += it.current()->stretch;
+ update();
+ }
+}
+
+void QIMPenWidget::changeCharSet( QIMPenCharSet *cs, int pos )
+{
+ if ( pos >= 0 && pos < (int)charSets.count() ) {
+ CharSetEntry *e = new CharSetEntry;
+ e->cs = cs;
+ e->stretch = charSets.at(pos)->stretch;
+ charSets.remove( pos );
+ charSets.insert( pos, e );
+ if ( pos == currCharSet ) {
+ emit changeCharSet( charSets.at(currCharSet)->cs );
+ }
+ update();
+ }
+}
+
+void QIMPenWidget::clearCharSets()
+{
+ charSets.clear();
+ currCharSet = 0;
+ update();
+}
+
+/*!
+ Display a character. \a speed determines how quickly the character is
+ drawn.
+*/
+void QIMPenWidget::showCharacter( QIMPenChar *ch, int speed )
+{
+ outputChar = 0;
+ outputStroke = 0;
+ strokes.clear();
+ mode = Output;
+ repaint();
+ if ( !ch || ch->isEmpty() ) {
+ mode = Waiting;
+ return;
+ }
+
+ outputChar = ch;
+ outputStroke = outputChar->penStrokes().getFirst();
+ if ( speed < 0 ) speed = 0;
+ if ( speed > 20 ) speed = 20;
+ speed = 50 - speed;
+ pointIndex = 0;
+ strokeIndex = 0;
+ lastPoint = outputStroke->startingPoint();
+ QRect br( outputChar->boundingRect() );
+ lastPoint.setX( (width() - br.width()) / 2 + (lastPoint.x () - br.left()) );
+ QPoint offset = lastPoint - outputStroke->startingPoint();
+ br.moveBy( offset.x(), offset.y() );
+ dirtyRect |= br;
+ timer->start( speed );
+}
+
+/*!
+ Handle drawing/clearing of characters.
+*/
+void QIMPenWidget::timeout()
+{
+ if ( mode == Output ) {
+ const QArray<QIMPenGlyphLink> &chain = outputStroke->chain();
+ if ( pointIndex < chain.count() ) {
+ QPainter paint( this );
+ paint.setBrush( Qt::black );
+ for ( unsigned i = 0; i < 3 && pointIndex < chain.count(); i++ ) {
+ lastPoint.rx() += chain[pointIndex].dx;
+ lastPoint.ry() += chain[pointIndex].dy;
+ pointIndex++;
+ paint.drawRect( lastPoint.x()-1, lastPoint.y()-1, 2, 2 );
+ }
+ }
+ if ( pointIndex >= chain.count() ) {
+ QIMPenStrokeList strokes = outputChar->penStrokes();
+ if ( strokeIndex < (int)strokes.count() - 1 ) {
+ pointIndex = 0;
+ strokeIndex++;
+ outputStroke = strokes.at( strokeIndex );
+ lastPoint = outputChar->startingPoint();
+ QRect br( outputChar->boundingRect() );
+ lastPoint.setX( (width() - br.width()) / 2
+ + (lastPoint.x () - br.left()) );
+ QPoint off = lastPoint - outputChar->startingPoint();
+ lastPoint = outputStroke->startingPoint() + off;
+ } else {
+ timer->stop();
+ mode = Waiting;
+ }
+ }
+ } else if ( mode == Waiting ) {
+ QRect r( dirtyRect );
+ if ( !r.isNull() ) {
+ r.moveBy( -2, -2 );
+ r.setSize( r.size() + QSize( 4, 4 ) );
+ repaint( r );
+ }
+ }
+}
+
+/*!
+ If the point \a p is over one of the character set titles, switch
+ to the set and return TRUE.
+*/
+bool QIMPenWidget::selectSet( QPoint p )
+{
+ if ( charSets.count() ) {
+ CharSetEntryIterator it( charSets );
+ int spos = 0;
+ int idx = 0;
+ for ( ; it.current(); ++it, idx++ ) {
+ int setWidth = width() * it.current()->stretch / totalStretch;
+ spos += setWidth;
+ if ( p.x() < spos ) {
+ if ( idx != currCharSet ) {
+ currCharSet = idx;
+ update( 0, 0, width(), 12 );
+ emit changeCharSet( currCharSet );
+ emit changeCharSet( charSets.at(currCharSet)->cs );
+ }
+ break;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/*!
+ Hopefully returns a sensible size.
+*/
+QSize QIMPenWidget::sizeHint()
+{
+ return QSize( TITLE_WIDTH * charSets.count(), 75 );
+}
+
+void QIMPenWidget::mousePressEvent( QMouseEvent *e )
+{
+ if ( !readOnly && e->button() == LeftButton && mode == Waiting ) {
+ // if selectSet returns false the click was not over the
+ // char set selectors.
+ if ( !selectSet( e->pos() ) ) {
+ // start of character input
+ timer->stop();
+ if ( outputChar ) {
+ outputChar = 0;
+ outputStroke = 0;
+ repaint();
+ }
+ mode = Input;
+ lastPoint = e->pos();
+ emit beginStroke();
+ inputStroke = new QIMPenStroke;
+ strokes.append( inputStroke );
+ inputStroke->beginInput( e->pos() );
+ QPainter paint( this );
+ paint.setBrush( Qt::black );
+ paint.drawRect( lastPoint.x()-1, lastPoint.y()-1, 2, 2 );
+ }
+ }
+}
+
+void QIMPenWidget::mouseReleaseEvent( QMouseEvent *e )
+{
+ if ( !readOnly && e->button() == LeftButton && mode == Input ) {
+ mode = Waiting;
+ inputStroke->endInput();
+ if ( charSets.count() )
+ emit stroke( inputStroke );
+ inputStroke = 0;
+ }
+}
+
+void QIMPenWidget::mouseMoveEvent( QMouseEvent *e )
+{
+ if ( !readOnly && mode == Input ) {
+ int dx = QABS( e->pos().x() - lastPoint.x() );
+ int dy = QABS( e->pos().y() - lastPoint.y() );
+ if ( dx + dy > 1 ) {
+ if ( inputStroke->addPoint( e->pos() ) ) {
+ QPainter paint( this );
+ paint.setPen( Qt::black );
+ paint.setBrush( Qt::black );
+ const QArray<QIMPenGlyphLink> &chain = inputStroke->chain();
+ QPoint p( e->pos() );
+ for ( int i = (int)chain.count()-1; i >= 0; i-- ) {
+ paint.drawRect( p.x()-1, p.y()-1, 2, 2 );
+ p.rx() -= chain[i].dx;
+ p.ry() -= chain[i].dy;
+ if ( p == lastPoint )
+ break;
+ }
+
+ /* ### use this when thick lines work properly on all devices
+ paint.setPen( QPen( Qt::black, 2 ) );
+ paint.drawLine( lastPoint, e->pos() );
+ */
+ }
+ lastPoint = e->pos();
+ }
+ }
+}
+
+void QIMPenWidget::paintEvent( QPaintEvent * )
+{
+ QPainter paint( this );
+
+ // draw guidelines
+ paint.setPen( Qt::gray );
+ paint.drawLine( 0, 0, width(), 0 );
+ int y = height() / 3;
+ paint.drawLine( 0, y, width(), y );
+ y *= 2;
+ paint.setPen( blue );
+ paint.drawLine( 0, y, width(), y );
+ paint.setPen( Qt::gray );
+
+ if ( !charSets.count() )
+ return;
+
+ // draw the character set titles
+ QFont selFont( "helvetica", 8, QFont::Bold );
+ QFont font( "helvetica", 8 );
+ CharSetEntryIterator it( charSets );
+ int spos = 0;
+ for ( ; it.current(); ++it ) {
+ int setWidth = width() * it.current()->stretch / totalStretch;
+ spos += setWidth;
+ if ( it.current() != charSets.getLast() ) {
+ paint.drawLine( spos, 0, spos, 5 );
+ paint.drawLine( spos, height()-1, spos, height()-6 );
+ }
+ paint.setFont( font );
+ int w = paint.fontMetrics().width( it.current()->cs->title() );
+ int tpos = spos - setWidth / 2;
+ paint.drawText( tpos - w/2, 0, w, 12, QPainter::AlignCenter,
+ it.current()->cs->title() );
+ }
+
+ // draw any character that should be displayed when repainted.
+ QPoint off;
+ const QIMPenStrokeList *stk = 0;
+ if ( outputChar && mode == Waiting ) {
+ stk = &outputChar->penStrokes();
+ QPoint p( outputChar->startingPoint() );
+ QRect br( outputChar->boundingRect() );
+ p.setX( (width() - br.width()) / 2 + (p.x () - br.left()) );
+ off = p - outputChar->startingPoint();
+ } else if ( mode == Waiting ) {
+ stk = &strokes;
+ strokeColor = gray;
+ }
+
+ if ( stk && !stk->isEmpty() ) {
+ paint.setPen( strokeColor );
+ paint.setBrush( strokeColor );
+ QIMPenStrokeIterator it( *stk );
+ while ( it.current() ) {
+ QPoint p = it.current()->startingPoint() + off;
+ paint.drawRect( p.x()-1, p.y()-1, 2, 2 );
+ const QArray<QIMPenGlyphLink> &chain = it.current()->chain();
+ for ( unsigned i = 0; i < chain.count(); i++ ) {
+ p.rx() += chain[i].dx;
+ p.ry() += chain[i].dy;
+ paint.drawRect( p.x()-1, p.y()-1, 2, 2 );
+ }
+ ++it;
+ if ( it.atLast() && mode == Waiting )
+ strokeColor = black;
+ }
+ }
+
+ dirtyRect = QRect();
+
+ // debug
+/*
+ if ( input ) {
+ QArray<int> sig = input->sig();
+ for ( unsigned i = 0; i < sig.count(); i++ ) {
+ paint.drawPoint( 200 + i, height()/2 - sig[i] / 8 );
+ }
+ }
+*/
+}
+
+void QIMPenWidget::resizeEvent( QResizeEvent *e )
+{
+ if ( mode == Output )
+ showCharacter( outputChar, 0 );
+
+ QWidget::resizeEvent( e );
+}
+