/********************************************************************** ** 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 #include #include #include #include #include #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 &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() const { 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 &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 &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 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 ); }