summaryrefslogtreecommitdiff
path: root/inputmethods/handwriting/qimpenwidget.cpp
Unidiff
Diffstat (limited to 'inputmethods/handwriting/qimpenwidget.cpp') (more/less context) (show 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 @@
1/**********************************************************************
2** Copyright (C) 2000 Trolltech AS. All rights reserved.
3**
4** This file is part of Qtopia Environment.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20
21#include <qapplication.h>
22#include <qinputdialog.h>
23#include <qpainter.h>
24#include <qfile.h>
25#include <qdatastream.h>
26#include <qtimer.h>
27#include "qimpenchar.h"
28#include "qimpenwidget.h"
29
30 #define TITLE_WIDTH30 // ### magic
31
32/*!
33 \class QIMPenWidget qimpenwidget.h
34
35 Draws characters and allows input of characters.
36*/
37
38QIMPenWidget::QIMPenWidget( QWidget *parent )
39 : QWidget( parent )
40{
41 charSets.setAutoDelete( TRUE );
42 inputStroke = 0;
43 outputChar = 0;
44 outputStroke = 0;
45 mode = Waiting;
46 currCharSet = 0;
47 readOnly = FALSE;
48 strokes.setAutoDelete( TRUE );
49
50 timer = new QTimer(this);
51 connect( timer, SIGNAL(timeout()), SLOT(timeout()));
52
53 setBackgroundColor( qApp->palette().color( QPalette::Active,
54 QColorGroup::Base ) );
55 strokeColor = black;
56 setFixedHeight( 75 );
57}
58
59void QIMPenWidget::clear()
60{
61 timer->stop();
62 mode = Waiting;
63 QRect r( dirtyRect );
64 QIMPenStrokeIterator it( strokes );
65 while ( it.current() ) {
66 r |= it.current()->boundingRect();
67 ++it;
68 }
69 outputChar = 0;
70 outputStroke = 0;
71 strokes.clear();
72 if ( !r.isNull() ) {
73 r.moveBy( -2, -2 );
74 r.setSize( r.size() + QSize( 4, 4 ) );
75 repaint( r );
76 } else {
77 repaint();
78 }
79}
80
81void QIMPenWidget::removeStroke()
82{
83 QRect r( dirtyRect );
84 QIMPenStroke *st = strokes.getFirst();
85 QRect strokeRect;
86 if ( st )
87 strokeRect = st->boundingRect();
88 r |= strokeRect;
89 strokes.removeFirst();
90 if ( !r.isNull() ) {
91 r.moveBy( -2, -2 );
92 r.setSize( r.size() + QSize( 4, 4 ) );
93 repaint( r );
94 }
95}
96
97void QIMPenWidget::greyStroke()
98{
99 QRect r( dirtyRect );
100 QIMPenStroke *st = strokes.getLast();
101 QRect strokeRect;
102 if ( st )
103 strokeRect = st->boundingRect();
104 r |= strokeRect;
105 QColor oldCol = strokeColor;
106 strokeColor = gray;
107 if ( !r.isNull() ) {
108 r.moveBy( -2, -2 );
109 r.setSize( r.size() + QSize( 4, 4 ) );
110 repaint( r );
111 }
112 strokeColor = oldCol;
113}
114
115/*!
116 Insert a character set into the list.
117*/
118void QIMPenWidget::insertCharSet( QIMPenCharSet *cs, int stretch, int pos )
119{
120 CharSetEntry *e = new CharSetEntry;
121 e->cs = cs;
122 e->stretch = stretch;
123 if ( pos < 0 )
124 pos = charSets.count();
125 charSets.insert( pos, e );
126 currCharSet = 0;
127 emit changeCharSet( currCharSet );
128 emit changeCharSet( charSets.at(currCharSet)->cs );
129 totalStretch = 0;
130 CharSetEntryIterator it( charSets );
131 for ( ; it.current(); ++it )
132 totalStretch += it.current()->stretch;
133 update();
134}
135
136/*!
137 Remove a character set from the list.
138*/
139void QIMPenWidget::removeCharSet( int pos )
140{
141 if ( pos >= 0 && pos < (int)charSets.count() ) {
142 charSets.remove( pos );
143 currCharSet = 0;
144 if ( charSets.count() ) {
145 emit changeCharSet( currCharSet );
146 emit changeCharSet( charSets.at(currCharSet)->cs );
147 }
148 totalStretch = 0;
149 CharSetEntryIterator it( charSets );
150 for ( ; it.current(); ++it )
151 totalStretch += it.current()->stretch;
152 update();
153 }
154}
155
156void QIMPenWidget::changeCharSet( QIMPenCharSet *cs, int pos )
157{
158 if ( pos >= 0 && pos < (int)charSets.count() ) {
159 CharSetEntry *e = new CharSetEntry;
160 e->cs = cs;
161 e->stretch = charSets.at(pos)->stretch;
162 charSets.remove( pos );
163 charSets.insert( pos, e );
164 if ( pos == currCharSet ) {
165 emit changeCharSet( charSets.at(currCharSet)->cs );
166 }
167 update();
168 }
169}
170
171void QIMPenWidget::clearCharSets()
172{
173 charSets.clear();
174 currCharSet = 0;
175 update();
176}
177
178/*!
179 Display a character. \a speed determines how quickly the character is
180 drawn.
181*/
182void QIMPenWidget::showCharacter( QIMPenChar *ch, int speed )
183{
184 outputChar = 0;
185 outputStroke = 0;
186 strokes.clear();
187 mode = Output;
188 repaint();
189 if ( !ch || ch->isEmpty() ) {
190 mode = Waiting;
191 return;
192 }
193
194 outputChar = ch;
195 outputStroke = outputChar->penStrokes().getFirst();
196 if ( speed < 0 ) speed = 0;
197 if ( speed > 20 ) speed = 20;
198 speed = 50 - speed;
199 pointIndex = 0;
200 strokeIndex = 0;
201 lastPoint = outputStroke->startingPoint();
202 QRect br( outputChar->boundingRect() );
203 lastPoint.setX( (width() - br.width()) / 2 + (lastPoint.x () - br.left()) );
204 QPoint offset = lastPoint - outputStroke->startingPoint();
205 br.moveBy( offset.x(), offset.y() );
206 dirtyRect |= br;
207 timer->start( speed );
208}
209
210/*!
211 Handle drawing/clearing of characters.
212*/
213void QIMPenWidget::timeout()
214{
215 if ( mode == Output ) {
216 const QArray<QIMPenGlyphLink> &chain = outputStroke->chain();
217 if ( pointIndex < chain.count() ) {
218 QPainter paint( this );
219 paint.setBrush( Qt::black );
220 for ( unsigned i = 0; i < 3 && pointIndex < chain.count(); i++ ) {
221 lastPoint.rx() += chain[pointIndex].dx;
222 lastPoint.ry() += chain[pointIndex].dy;
223 pointIndex++;
224 paint.drawRect( lastPoint.x()-1, lastPoint.y()-1, 2, 2 );
225 }
226 }
227 if ( pointIndex >= chain.count() ) {
228 QIMPenStrokeList strokes = outputChar->penStrokes();
229 if ( strokeIndex < (int)strokes.count() - 1 ) {
230 pointIndex = 0;
231 strokeIndex++;
232 outputStroke = strokes.at( strokeIndex );
233 lastPoint = outputChar->startingPoint();
234 QRect br( outputChar->boundingRect() );
235 lastPoint.setX( (width() - br.width()) / 2
236 + (lastPoint.x () - br.left()) );
237 QPoint off = lastPoint - outputChar->startingPoint();
238 lastPoint = outputStroke->startingPoint() + off;
239 } else {
240 timer->stop();
241 mode = Waiting;
242 }
243 }
244 } else if ( mode == Waiting ) {
245 QRect r( dirtyRect );
246 if ( !r.isNull() ) {
247 r.moveBy( -2, -2 );
248 r.setSize( r.size() + QSize( 4, 4 ) );
249 repaint( r );
250 }
251 }
252}
253
254/*!
255 If the point \a p is over one of the character set titles, switch
256 to the set and return TRUE.
257*/
258bool QIMPenWidget::selectSet( QPoint p )
259{
260 if ( charSets.count() ) {
261 CharSetEntryIterator it( charSets );
262 int spos = 0;
263 int idx = 0;
264 for ( ; it.current(); ++it, idx++ ) {
265 int setWidth = width() * it.current()->stretch / totalStretch;
266 spos += setWidth;
267 if ( p.x() < spos ) {
268 if ( idx != currCharSet ) {
269 currCharSet = idx;
270 update( 0, 0, width(), 12 );
271 emit changeCharSet( currCharSet );
272 emit changeCharSet( charSets.at(currCharSet)->cs );
273 }
274 break;
275 }
276 }
277 }
278
279 return FALSE;
280}
281
282/*!
283 Hopefully returns a sensible size.
284*/
285QSize QIMPenWidget::sizeHint()
286{
287 return QSize( TITLE_WIDTH * charSets.count(), 75 );
288}
289
290void QIMPenWidget::mousePressEvent( QMouseEvent *e )
291{
292 if ( !readOnly && e->button() == LeftButton && mode == Waiting ) {
293 // if selectSet returns false the click was not over the
294 // char set selectors.
295 if ( !selectSet( e->pos() ) ) {
296 // start of character input
297 timer->stop();
298 if ( outputChar ) {
299 outputChar = 0;
300 outputStroke = 0;
301 repaint();
302 }
303 mode = Input;
304 lastPoint = e->pos();
305 emit beginStroke();
306 inputStroke = new QIMPenStroke;
307 strokes.append( inputStroke );
308 inputStroke->beginInput( e->pos() );
309 QPainter paint( this );
310 paint.setBrush( Qt::black );
311 paint.drawRect( lastPoint.x()-1, lastPoint.y()-1, 2, 2 );
312 }
313 }
314}
315
316void QIMPenWidget::mouseReleaseEvent( QMouseEvent *e )
317{
318 if ( !readOnly && e->button() == LeftButton && mode == Input ) {
319 mode = Waiting;
320 inputStroke->endInput();
321 if ( charSets.count() )
322 emit stroke( inputStroke );
323 inputStroke = 0;
324 }
325}
326
327void QIMPenWidget::mouseMoveEvent( QMouseEvent *e )
328{
329 if ( !readOnly && mode == Input ) {
330 int dx = QABS( e->pos().x() - lastPoint.x() );
331 int dy = QABS( e->pos().y() - lastPoint.y() );
332 if ( dx + dy > 1 ) {
333 if ( inputStroke->addPoint( e->pos() ) ) {
334 QPainter paint( this );
335 paint.setPen( Qt::black );
336 paint.setBrush( Qt::black );
337 const QArray<QIMPenGlyphLink> &chain = inputStroke->chain();
338 QPoint p( e->pos() );
339 for ( int i = (int)chain.count()-1; i >= 0; i-- ) {
340 paint.drawRect( p.x()-1, p.y()-1, 2, 2 );
341 p.rx() -= chain[i].dx;
342 p.ry() -= chain[i].dy;
343 if ( p == lastPoint )
344 break;
345 }
346
347 /* ### use this when thick lines work properly on all devices
348 paint.setPen( QPen( Qt::black, 2 ) );
349 paint.drawLine( lastPoint, e->pos() );
350 */
351 }
352 lastPoint = e->pos();
353 }
354 }
355}
356
357void QIMPenWidget::paintEvent( QPaintEvent * )
358{
359 QPainter paint( this );
360
361 // draw guidelines
362 paint.setPen( Qt::gray );
363 paint.drawLine( 0, 0, width(), 0 );
364 int y = height() / 3;
365 paint.drawLine( 0, y, width(), y );
366 y *= 2;
367 paint.setPen( blue );
368 paint.drawLine( 0, y, width(), y );
369 paint.setPen( Qt::gray );
370
371 if ( !charSets.count() )
372 return;
373
374 // draw the character set titles
375 QFont selFont( "helvetica", 8, QFont::Bold );
376 QFont font( "helvetica", 8 );
377 CharSetEntryIterator it( charSets );
378 int spos = 0;
379 for ( ; it.current(); ++it ) {
380 int setWidth = width() * it.current()->stretch / totalStretch;
381 spos += setWidth;
382 if ( it.current() != charSets.getLast() ) {
383 paint.drawLine( spos, 0, spos, 5 );
384 paint.drawLine( spos, height()-1, spos, height()-6 );
385 }
386 paint.setFont( font );
387 int w = paint.fontMetrics().width( it.current()->cs->title() );
388 int tpos = spos - setWidth / 2;
389 paint.drawText( tpos - w/2, 0, w, 12, QPainter::AlignCenter,
390 it.current()->cs->title() );
391 }
392
393 // draw any character that should be displayed when repainted.
394 QPoint off;
395 const QIMPenStrokeList *stk = 0;
396 if ( outputChar && mode == Waiting ) {
397 stk = &outputChar->penStrokes();
398 QPoint p( outputChar->startingPoint() );
399 QRect br( outputChar->boundingRect() );
400 p.setX( (width() - br.width()) / 2 + (p.x () - br.left()) );
401 off = p - outputChar->startingPoint();
402 } else if ( mode == Waiting ) {
403 stk = &strokes;
404 strokeColor = gray;
405 }
406
407 if ( stk && !stk->isEmpty() ) {
408 paint.setPen( strokeColor );
409 paint.setBrush( strokeColor );
410 QIMPenStrokeIterator it( *stk );
411 while ( it.current() ) {
412 QPoint p = it.current()->startingPoint() + off;
413 paint.drawRect( p.x()-1, p.y()-1, 2, 2 );
414 const QArray<QIMPenGlyphLink> &chain = it.current()->chain();
415 for ( unsigned i = 0; i < chain.count(); i++ ) {
416 p.rx() += chain[i].dx;
417 p.ry() += chain[i].dy;
418 paint.drawRect( p.x()-1, p.y()-1, 2, 2 );
419 }
420 ++it;
421 if ( it.atLast() && mode == Waiting )
422 strokeColor = black;
423 }
424 }
425
426 dirtyRect = QRect();
427
428 // debug
429/*
430 if ( input ) {
431 QArray<int> sig = input->sig();
432 for ( unsigned i = 0; i < sig.count(); i++ ) {
433 paint.drawPoint( 200 + i, height()/2 - sig[i] / 8 );
434 }
435 }
436*/
437}
438
439void QIMPenWidget::resizeEvent( QResizeEvent *e )
440{
441 if ( mode == Output )
442 showCharacter( outputChar, 0 );
443
444 QWidget::resizeEvent( e );
445}
446