summaryrefslogtreecommitdiff
path: root/inputmethods/handwriting
Unidiff
Diffstat (limited to 'inputmethods/handwriting') (more/less context) (ignore whitespace changes)
-rw-r--r--inputmethods/handwriting/.cvsignore5
-rw-r--r--inputmethods/handwriting/Makefile.in281
-rw-r--r--inputmethods/handwriting/handwriting.pro33
-rw-r--r--inputmethods/handwriting/handwritingimpl.cpp113
-rw-r--r--inputmethods/handwriting/handwritingimpl.h51
-rw-r--r--inputmethods/handwriting/qimpenchar.cpp505
-rw-r--r--inputmethods/handwriting/qimpenchar.h157
-rw-r--r--inputmethods/handwriting/qimpencombining.cpp141
-rw-r--r--inputmethods/handwriting/qimpencombining.h41
-rw-r--r--inputmethods/handwriting/qimpenhelp.cpp410
-rw-r--r--inputmethods/handwriting/qimpenhelp.h85
-rw-r--r--inputmethods/handwriting/qimpeninput.cpp515
-rw-r--r--inputmethods/handwriting/qimpeninput.h94
-rw-r--r--inputmethods/handwriting/qimpenmatch.cpp365
-rw-r--r--inputmethods/handwriting/qimpenmatch.h107
-rw-r--r--inputmethods/handwriting/qimpenprefbase.ui185
-rw-r--r--inputmethods/handwriting/qimpenprofile.cpp245
-rw-r--r--inputmethods/handwriting/qimpenprofile.h70
-rw-r--r--inputmethods/handwriting/qimpensetup.cpp656
-rw-r--r--inputmethods/handwriting/qimpensetup.h124
-rw-r--r--inputmethods/handwriting/qimpenstroke.cpp646
-rw-r--r--inputmethods/handwriting/qimpenstroke.h91
-rw-r--r--inputmethods/handwriting/qimpenwidget.cpp446
-rw-r--r--inputmethods/handwriting/qimpenwidget.h88
-rw-r--r--inputmethods/handwriting/qimpenwordpick.cpp113
-rw-r--r--inputmethods/handwriting/qimpenwordpick.h49
-rw-r--r--inputmethods/handwriting/qpe-handwriting.control9
-rwxr-xr-xinputmethods/handwriting/qpe-handwriting.postinst2
-rwxr-xr-xinputmethods/handwriting/qpe-handwriting.postrm2
29 files changed, 5629 insertions, 0 deletions
diff --git a/inputmethods/handwriting/.cvsignore b/inputmethods/handwriting/.cvsignore
new file mode 100644
index 0000000..285856f
--- a/dev/null
+++ b/inputmethods/handwriting/.cvsignore
@@ -0,0 +1,5 @@
1moc_*
2*.moc
3Makefile
4qimpenprefbase.h
5qimpenprefbase.cpp
diff --git a/inputmethods/handwriting/Makefile.in b/inputmethods/handwriting/Makefile.in
new file mode 100644
index 0000000..aabb5aa
--- a/dev/null
+++ b/inputmethods/handwriting/Makefile.in
@@ -0,0 +1,281 @@
1#############################################################################
2
3####### Compiler, tools and options
4
5 CXX =$(SYSCONF_CXX) $(QT_CXX_MT)
6 CXXFLAGS=$(SYSCONF_CXXFLAGS_QT) $(SYSCONF_CXXFLAGS) $(SYSCONF_CXXFLAGS_LIB)
7 CC =$(SYSCONF_CC) $(QT_C_MT)
8 CFLAGS =$(SYSCONF_CFLAGS) $(SYSCONF_CFLAGS_LIB)
9 INCPATH =-I$(QPEDIR)/include
10 LFLAGS =$(SYSCONF_LFLAGS_QT) $(SYSCONF_RPATH_QT) $(SYSCONF_LFLAGS) $(QT_LFLAGS_MT)
11 LIBS =$(SUBLIBS) -lqpe $(SYSCONF_LIBS_QT) $(SYSCONF_LIBS_QTAPP)
12 MOC =$(SYSCONF_MOC)
13 UIC =$(SYSCONF_UIC)
14
15####### Target
16
17DESTDIR = ../../plugins/inputmethods/
18VER_MAJ = 1
19VER_MIN = 0
20VER_PATCH = 0
21 TARGET= qhandwriting
22TARGET1 = lib$(TARGET).so.$(VER_MAJ)
23
24####### Files
25
26 HEADERS =qimpenchar.h \
27 qimpenprofile.h \
28 qimpencombining.h \
29 qimpenhelp.h \
30 qimpeninput.h \
31 qimpenmatch.h \
32 qimpensetup.h \
33 qimpenstroke.h \
34 qimpenwidget.h \
35 qimpenwordpick.h \
36 handwritingimpl.h
37 SOURCES =qimpenchar.cpp \
38 qimpenprofile.cpp \
39 qimpencombining.cpp \
40 qimpenhelp.cpp \
41 qimpeninput.cpp \
42 qimpenmatch.cpp \
43 qimpensetup.cpp \
44 qimpenstroke.cpp \
45 qimpenwidget.cpp \
46 qimpenwordpick.cpp \
47 handwritingimpl.cpp
48 OBJECTS =qimpenchar.o \
49 qimpenprofile.o \
50 qimpencombining.o \
51 qimpenhelp.o \
52 qimpeninput.o \
53 qimpenmatch.o \
54 qimpensetup.o \
55 qimpenstroke.o \
56 qimpenwidget.o \
57 qimpenwordpick.o \
58 handwritingimpl.o \
59 qimpenprefbase.o
60INTERFACES = qimpenprefbase.ui
61UICDECLS = qimpenprefbase.h
62UICIMPLS = qimpenprefbase.cpp
63 SRCMOC =moc_qimpenhelp.cpp \
64 moc_qimpeninput.cpp \
65 moc_qimpenmatch.cpp \
66 moc_qimpensetup.cpp \
67 moc_qimpenwidget.cpp \
68 moc_qimpenwordpick.cpp \
69 moc_qimpenprefbase.cpp
70 OBJMOC =moc_qimpenhelp.o \
71 moc_qimpeninput.o \
72 moc_qimpenmatch.o \
73 moc_qimpensetup.o \
74 moc_qimpenwidget.o \
75 moc_qimpenwordpick.o \
76 moc_qimpenprefbase.o
77
78
79####### Implicit rules
80
81.SUFFIXES: .cpp .cxx .cc .C .c
82
83.cpp.o:
84 $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
85
86.cxx.o:
87 $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
88
89.cc.o:
90 $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
91
92.C.o:
93 $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
94
95.c.o:
96 $(CC) -c $(CFLAGS) $(INCPATH) -o $@ $<
97
98####### Build rules
99
100
101all: $(DESTDIR)$(SYSCONF_LINK_TARGET)
102
103$(DESTDIR)$(SYSCONF_LINK_TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) $(SUBLIBS)
104 $(SYSCONF_LINK_LIB)
105
106moc: $(SRCMOC)
107
108tmake:
109 tmake handwriting.pro
110
111clean:
112 -rm -f $(OBJECTS) $(OBJMOC) $(SRCMOC) $(UICIMPLS) $(UICDECLS)
113 -rm -f *~ core
114 -rm -f allmoc.cpp
115
116####### Extension Modules
117
118listpromodules:
119 @echo
120
121listallmodules:
122 @echo
123
124listaddonpromodules:
125 @echo
126
127listaddonentmodules:
128 @echo
129
130
131REQUIRES=
132
133####### Sub-libraries
134
135
136###### Combined headers
137
138
139
140####### Compile
141
142qimpenchar.o: qimpenchar.cpp \
143 qimpencombining.h \
144 qimpenchar.h \
145 qimpenstroke.h
146
147qimpenprofile.o: qimpenprofile.cpp \
148 qimpencombining.h \
149 qimpenchar.h \
150 qimpenstroke.h \
151 qimpenprofile.h
152
153qimpencombining.o: qimpencombining.cpp \
154 qimpencombining.h \
155 qimpenchar.h \
156 qimpenstroke.h
157
158qimpenhelp.o: qimpenhelp.cpp \
159 qimpenwidget.h \
160 qimpenchar.h \
161 qimpenstroke.h \
162 qimpencombining.h \
163 qimpenmatch.h \
164 qimpenhelp.h \
165 qimpenprofile.h
166
167qimpeninput.o: qimpeninput.cpp \
168 qimpenwidget.h \
169 qimpenchar.h \
170 qimpenstroke.h \
171 qimpensetup.h \
172 qimpenprofile.h \
173 qimpeninput.h \
174 qimpencombining.h \
175 qimpenwordpick.h \
176 qimpenmatch.h \
177 qimpenhelp.h
178
179qimpenmatch.o: qimpenmatch.cpp \
180 qimpenmatch.h \
181 qimpenchar.h \
182 qimpenstroke.h
183
184qimpensetup.o: qimpensetup.cpp \
185 qimpenwidget.h \
186 qimpenchar.h \
187 qimpenstroke.h \
188 qimpenprefbase.h \
189 qimpensetup.h \
190 qimpenprofile.h
191
192qimpenstroke.o: qimpenstroke.cpp \
193 qimpenstroke.h
194
195qimpenwidget.o: qimpenwidget.cpp \
196 qimpenchar.h \
197 qimpenstroke.h \
198 qimpenwidget.h
199
200qimpenwordpick.o: qimpenwordpick.cpp \
201 qimpenwordpick.h \
202 qimpenmatch.h \
203 qimpenchar.h \
204 qimpenstroke.h
205
206handwritingimpl.o: handwritingimpl.cpp \
207 qimpeninput.h \
208 qimpenprofile.h \
209 qimpenchar.h \
210 qimpenstroke.h \
211 handwritingimpl.h
212
213qimpenprefbase.h: qimpenprefbase.ui
214 $(UIC) qimpenprefbase.ui -o $(INTERFACE_DECL_PATH)/qimpenprefbase.h
215
216qimpenprefbase.cpp: qimpenprefbase.ui
217 $(UIC) qimpenprefbase.ui -i qimpenprefbase.h -o qimpenprefbase.cpp
218
219qimpenprefbase.o: qimpenprefbase.cpp \
220 qimpenprefbase.h \
221 qimpenprefbase.ui
222
223moc_qimpenhelp.o: moc_qimpenhelp.cpp \
224 qimpenhelp.h \
225 qimpenchar.h \
226 qimpenstroke.h \
227 qimpenprofile.h
228
229moc_qimpeninput.o: moc_qimpeninput.cpp \
230 qimpeninput.h \
231 qimpenprofile.h \
232 qimpenchar.h \
233 qimpenstroke.h
234
235moc_qimpenmatch.o: moc_qimpenmatch.cpp \
236 qimpenmatch.h \
237 qimpenchar.h \
238 qimpenstroke.h
239
240moc_qimpensetup.o: moc_qimpensetup.cpp \
241 qimpensetup.h \
242 qimpenprofile.h \
243 qimpenchar.h \
244 qimpenstroke.h
245
246moc_qimpenwidget.o: moc_qimpenwidget.cpp \
247 qimpenwidget.h \
248 qimpenchar.h \
249 qimpenstroke.h
250
251moc_qimpenwordpick.o: moc_qimpenwordpick.cpp \
252 qimpenwordpick.h \
253 qimpenmatch.h \
254 qimpenchar.h \
255 qimpenstroke.h
256
257moc_qimpenprefbase.o: moc_qimpenprefbase.cpp \
258 qimpenprefbase.h
259
260moc_qimpenhelp.cpp: qimpenhelp.h
261 $(MOC) qimpenhelp.h -o moc_qimpenhelp.cpp
262
263moc_qimpeninput.cpp: qimpeninput.h
264 $(MOC) qimpeninput.h -o moc_qimpeninput.cpp
265
266moc_qimpenmatch.cpp: qimpenmatch.h
267 $(MOC) qimpenmatch.h -o moc_qimpenmatch.cpp
268
269moc_qimpensetup.cpp: qimpensetup.h
270 $(MOC) qimpensetup.h -o moc_qimpensetup.cpp
271
272moc_qimpenwidget.cpp: qimpenwidget.h
273 $(MOC) qimpenwidget.h -o moc_qimpenwidget.cpp
274
275moc_qimpenwordpick.cpp: qimpenwordpick.h
276 $(MOC) qimpenwordpick.h -o moc_qimpenwordpick.cpp
277
278moc_qimpenprefbase.cpp: qimpenprefbase.h
279 $(MOC) qimpenprefbase.h -o moc_qimpenprefbase.cpp
280
281
diff --git a/inputmethods/handwriting/handwriting.pro b/inputmethods/handwriting/handwriting.pro
new file mode 100644
index 0000000..999552b
--- a/dev/null
+++ b/inputmethods/handwriting/handwriting.pro
@@ -0,0 +1,33 @@
1 TEMPLATE= lib
2 CONFIG += qt warn_on release
3 HEADERS= qimpenchar.h \
4 qimpenprofile.h \
5 qimpencombining.h \
6 qimpenhelp.h \
7 qimpeninput.h \
8 qimpenmatch.h \
9 qimpensetup.h \
10 qimpenstroke.h \
11 qimpenwidget.h \
12 qimpenwordpick.h \
13 handwritingimpl.h
14 SOURCES= qimpenchar.cpp \
15 qimpenprofile.cpp \
16 qimpencombining.cpp \
17 qimpenhelp.cpp \
18 qimpeninput.cpp \
19 qimpenmatch.cpp \
20 qimpensetup.cpp \
21 qimpenstroke.cpp \
22 qimpenwidget.cpp \
23 qimpenwordpick.cpp \
24 handwritingimpl.cpp
25INTERFACES = qimpenprefbase.ui
26 TARGET = qhandwriting
27 DESTDIR = ../../plugins/inputmethods
28INCLUDEPATH += $(QPEDIR)/include
29DEPENDPATH += ../$(QPEDIR)/include ../../taskbar
30LIBS += -lqpe
31 VERSION = 1.0.0
32
33TRANSLATIONS += ../../i18n/de/libqhandwriting.ts
diff --git a/inputmethods/handwriting/handwritingimpl.cpp b/inputmethods/handwriting/handwritingimpl.cpp
new file mode 100644
index 0000000..c39e1aa
--- a/dev/null
+++ b/inputmethods/handwriting/handwritingimpl.cpp
@@ -0,0 +1,113 @@
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 <qwidget.h>
23#include <qpixmap.h>
24#include "qimpeninput.h"
25#include "handwritingimpl.h"
26
27/* XPM */
28static const char * pen_xpm[] = {
29"28 13 9 1",
30" c None",
31". c #000000",
32"+ c #FFE993",
33"@ c #8292FF",
34"# c #F7C500",
35"$ c #C69F00",
36"% c #0022FF",
37"& c #000F72",
38"* c #A3732C",
39" . ",
40" .+. ",
41" .@#$. ",
42" .@%&. ",
43" .@%&. ",
44" . .@%&. ",
45" . .@%&. ",
46" . .@%&. ",
47" ... ... .. .@%&. ",
48" . . . . . .*.&. ",
49" . . . . . .**. ",
50" ... ... .. ... ",
51" "};
52
53HandwritingImpl::HandwritingImpl()
54 : input(0), icn(0), ref(0)
55{
56}
57
58HandwritingImpl::~HandwritingImpl()
59{
60 delete input;
61 delete icn;
62}
63
64QWidget *HandwritingImpl::inputMethod( QWidget *parent, Qt::WFlags f )
65{
66 if ( !input )
67 input = new QIMPenInput( parent, "Handwriting", f );
68 return input;
69}
70
71void HandwritingImpl::resetState()
72{
73 if ( input )
74 input->resetState();
75}
76
77QPixmap *HandwritingImpl::icon()
78{
79 if ( !icn )
80 icn = new QPixmap( (const char **)pen_xpm );
81 return icn;
82}
83
84QString HandwritingImpl::name()
85{
86 return qApp->translate( "InputMethods", "Handwriting" );
87}
88
89void HandwritingImpl::onKeyPress( QObject *receiver, const char *slot )
90{
91 if ( input )
92 QObject::connect( input, SIGNAL(key(ushort,ushort,ushort,bool,bool)), receiver, slot );
93}
94
95#ifndef QT_NO_COMPONENT
96QRESULT HandwritingImpl::queryInterface( const QUuid &uuid, QUnknownInterface **iface )
97{
98 *iface = 0;
99 if ( uuid == IID_QUnknown )
100 *iface = this;
101 else if ( uuid == IID_InputMethod )
102 *iface = this;
103
104 if ( *iface )
105 (*iface)->addRef();
106 return QS_OK;
107}
108
109Q_EXPORT_INTERFACE()
110{
111 Q_CREATE_INSTANCE( HandwritingImpl )
112}
113#endif
diff --git a/inputmethods/handwriting/handwritingimpl.h b/inputmethods/handwriting/handwritingimpl.h
new file mode 100644
index 0000000..1215853
--- a/dev/null
+++ b/inputmethods/handwriting/handwritingimpl.h
@@ -0,0 +1,51 @@
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#ifndef HANDWRITINGIMPL_H
21#define HANDWRITINGIMPL_H
22
23#include <qpe/inputmethodinterface.h>
24
25class QIMPenInput;
26class QPixmap;
27
28class HandwritingImpl : public InputMethodInterface
29{
30public:
31 HandwritingImpl();
32 virtual ~HandwritingImpl();
33
34#ifndef QT_NO_COMPONENT
35 QRESULT queryInterface( const QUuid&, QUnknownInterface** );
36 Q_REFCOUNT
37#endif
38
39 virtual QWidget *inputMethod( QWidget *parent, Qt::WFlags f );
40 virtual void resetState();
41 virtual QPixmap *icon();
42 virtual QString name();
43 virtual void onKeyPress( QObject *receiver, const char *slot );
44
45private:
46 QIMPenInput *input;
47 QPixmap *icn;
48 ulong ref;
49};
50
51#endif
diff --git a/inputmethods/handwriting/qimpenchar.cpp b/inputmethods/handwriting/qimpenchar.cpp
new file mode 100644
index 0000000..9c38ec9
--- a/dev/null
+++ b/inputmethods/handwriting/qimpenchar.cpp
@@ -0,0 +1,505 @@
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 <qfile.h>
22#include <qtl.h>
23#include <math.h>
24#include <limits.h>
25#include <errno.h>
26#include <qdatastream.h>
27#include "qimpencombining.h"
28#include "qimpenchar.h"
29
30 #define QIMPEN_MATCH_THRESHOLD 200000
31
32const QIMPenSpecialKeys qimpen_specialKeys[] = {
33 { Qt::Key_Escape, "[Esc]" },
34 { Qt::Key_Tab, "[Tab]" },
35 { Qt::Key_Backspace,"[BackSpace]" },
36 { Qt::Key_Return, "[Return]" },
37 { QIMPenChar::Caps, "[Uppercase]" },
38 { QIMPenChar::CapsLock,"[Caps Lock]" },
39 { QIMPenChar::Shortcut,"[Shortcut]" },
40 { QIMPenChar::Punctuation, "[Punctuation]" },
41 { QIMPenChar::Symbol,"[Symbol]" },
42 { QIMPenChar::Extended,"[Extended]" },
43 { Qt::Key_unknown, 0 } };
44
45
46/*!
47 \class QIMPenChar qimpenchar.h
48
49 Handles a single character. Can calculate closeness of match to
50 another character.
51*/
52
53QIMPenChar::QIMPenChar()
54{
55 flags = 0;
56 strokes.setAutoDelete( TRUE );
57}
58
59QIMPenChar::QIMPenChar( const QIMPenChar &chr )
60{
61 strokes.setAutoDelete( TRUE );
62 ch = chr.ch;
63 flags = chr.flags;
64 d = chr.d;
65 QIMPenStrokeIterator it( chr.strokes );
66 while ( it.current() ) {
67 strokes.append( new QIMPenStroke( *it.current() ) );
68 ++it;
69 }
70}
71
72QIMPenChar &QIMPenChar::operator=( const QIMPenChar &chr )
73{
74 strokes.clear();
75 ch = chr.ch;
76 flags = chr.flags;
77 d = chr.d;
78 QIMPenStrokeIterator it( chr.strokes );
79 while ( it.current() ) {
80 strokes.append( new QIMPenStroke( *it.current() ) );
81 ++it;
82 }
83
84 return *this;
85}
86
87QString QIMPenChar::name() const
88{
89 QString n;
90
91 if ( (ch & 0x0000FFFF) == 0 ) {
92 int code = ch >> 16;
93 for ( int i = 0; qimpen_specialKeys[i].code != Qt::Key_unknown; i++ ) {
94 if ( qimpen_specialKeys[i].code == code ) {
95 n = qimpen_specialKeys[i].name;
96 break;
97 }
98 }
99 } else {
100 n = QChar( ch & 0x0000FFFF );
101 }
102
103 return n;
104}
105
106void QIMPenChar::clear()
107{
108 ch = 0;
109 flags = 0;
110 d = QString::null;
111 strokes.clear();
112}
113
114unsigned int QIMPenChar::strokeLength( int s ) const
115{
116 QIMPenStrokeIterator it( strokes );
117 while ( it.current() && s ) {
118 ++it;
119 --s;
120 }
121
122 if ( it.current() )
123 return it.current()->length();
124
125 return 0;
126}
127
128/*!
129 Add a stroke to the character
130*/
131void QIMPenChar::addStroke( QIMPenStroke *st )
132{
133 QIMPenStroke *stroke = new QIMPenStroke( *st );
134 strokes.append( stroke );
135}
136
137/*!
138 Return an indicator of the closeness of this character to \a pen.
139 Lower value is better.
140*/
141int QIMPenChar::match( QIMPenChar *pen )
142{
143/*
144 if ( strokes.count() > pen->strokes.count() )
145 return INT_MAX;
146*/
147 int err = 0;
148 int maxErr = 0;
149 int diff = 0;
150 QIMPenStrokeIterator it1( strokes );
151 QIMPenStrokeIterator it2( pen->strokes );
152 err = it1.current()->match( it2.current() );
153 if ( err > maxErr )
154 maxErr = err;
155 ++it1;
156 ++it2;
157 while ( err < 400000 && it1.current() && it2.current() ) {
158 QPoint p1 = it1.current()->boundingRect().center() -
159 strokes.getFirst()->boundingRect().center();
160 QPoint p2 = it2.current()->boundingRect().center() -
161 pen->strokes.getFirst()->boundingRect().center();
162 int xdiff = QABS( p1.x() - p2.x() ) - 6;
163 int ydiff = QABS( p1.y() - p2.y() ) - 5;
164 if ( xdiff < 0 )
165 xdiff = 0;
166 if ( ydiff < 0 )
167 ydiff = 0;
168 if ( xdiff > 10 || ydiff > 10 ) { // not a chance
169#ifdef DEBUG_QIMPEN
170 qDebug( "char %c, stroke starting pt diff excessive", pen->ch );
171#endif
172 return INT_MAX;
173 }
174 diff += xdiff*xdiff + ydiff*ydiff;
175 err = it1.current()->match( it2.current() );
176 if ( err > maxErr )
177 maxErr = err;
178 ++it1;
179 ++it2;
180 }
181
182 maxErr += diff * diff * 6; // magic weighting :)
183
184#ifdef DEBUG_QIMPEN
185 qDebug( "char: %c, maxErr %d, diff %d, (%d)", pen->ch, maxErr, diff, strokes.count() );
186#endif
187 return maxErr;
188}
189
190/*!
191 Return the bounding rect of this character. It may have sides with
192 negative coords since its origin is where the user started drawing
193 the character.
194*/
195QRect QIMPenChar::boundingRect()
196{
197 QRect br;
198 QIMPenStroke *st = strokes.first();
199 while ( st ) {
200 br |= st->boundingRect();
201 st = strokes.next();
202 }
203
204 return br;
205}
206
207
208/*!
209 Write the character's data to the stream.
210*/
211QDataStream &operator<< (QDataStream &s, const QIMPenChar &ws)
212{
213 s << ws.ch;
214 s << ws.flags;
215 if ( ws.flags & QIMPenChar::Data )
216 s << ws.d;
217 s << ws.strokes.count();
218 QIMPenStrokeIterator it( ws.strokes );
219 while ( it.current() ) {
220 s << *it.current();
221 ++it;
222 }
223
224 return s;
225}
226
227/*!
228 Read the character's data from the stream.
229*/
230QDataStream &operator>> (QDataStream &s, QIMPenChar &ws)
231{
232 s >> ws.ch;
233 s >> ws.flags;
234 if ( ws.flags & QIMPenChar::Data )
235 s >> ws.d;
236 unsigned size;
237 s >> size;
238 for ( unsigned i = 0; i < size; i++ ) {
239 QIMPenStroke *st = new QIMPenStroke();
240 s >> *st;
241 ws.strokes.append( st );
242 }
243
244 return s;
245}
246
247//===========================================================================
248
249bool QIMPenCharMatch::operator>( const QIMPenCharMatch &m )
250{
251 return error > m.error;
252}
253
254bool QIMPenCharMatch::operator<( const QIMPenCharMatch &m )
255{
256 return error < m.error;
257}
258
259bool QIMPenCharMatch::operator<=( const QIMPenCharMatch &m )
260{
261 return error <= m.error;
262}
263
264//===========================================================================
265
266/*!
267 \class QIMPenCharSet qimpenchar.h
268
269 Maintains a set of related characters.
270*/
271
272QIMPenCharSet::QIMPenCharSet()
273{
274 chars.setAutoDelete( TRUE );
275 desc = "Unnamed";
276 csTitle = "abc";
277 csType = Unknown;
278 maxStrokes = 0;
279}
280
281/*!
282 Construct and load a characters set from file \a fn.
283*/
284QIMPenCharSet::QIMPenCharSet( const QString &fn )
285{
286 chars.setAutoDelete( TRUE );
287 desc = "Unnamed";
288 csTitle = "abc";
289 csType = Unknown;
290 maxStrokes = 0;
291 load( fn, System );
292}
293
294const QString &QIMPenCharSet::filename( Domain d ) const
295{
296 if ( d == System )
297 return sysFilename;
298 else
299 return userFilename;
300}
301
302void QIMPenCharSet::setFilename( const QString &fn, Domain d )
303{
304 if ( d == System )
305 sysFilename = fn;
306 else if ( d == User )
307 userFilename = fn;
308}
309
310/*!
311 Load a character set from file \a fn.
312*/
313bool QIMPenCharSet::load( const QString &fn, Domain d )
314{
315 setFilename( fn, d );
316
317 bool ok = FALSE;
318 QFile file( fn );
319 if ( file.open( IO_ReadOnly ) ) {
320 QDataStream ds( &file );
321 QString version;
322 ds >> version;
323 ds >> csTitle;
324 ds >> desc;
325 int major = version.mid( 4, 1 ).toInt();
326 int minor = version.mid( 6 ).toInt();
327 if ( major >= 1 && minor > 0 ) {
328 ds >> (Q_INT8 &)csType;
329 } else {
330 if ( csTitle == "abc" )
331 csType = Lower;
332 else if ( csTitle == "ABC" )
333 csType = Upper;
334 else if ( csTitle == "123" )
335 csType = Numeric;
336 else if ( fn == "Combining" )
337 csType = Combining;
338 }
339 while ( !ds.atEnd() ) {
340 QIMPenChar *pc = new QIMPenChar;
341 ds >> *pc;
342 if ( d == User )
343 markDeleted( pc->character() ); // override system
344 addChar( pc );
345 }
346 if ( file.status() == IO_Ok )
347 ok = TRUE;
348 }
349
350 return ok;
351}
352
353/*!
354 Save this character set.
355*/
356bool QIMPenCharSet::save( Domain d )
357{
358 if ( filename( d ).isEmpty() )
359 return FALSE;
360
361 bool ok = FALSE;
362
363 QString fn = filename( d );
364 QString tmpFn = fn + ".new";
365 QFile file( tmpFn );
366 if ( file.open( IO_WriteOnly|IO_Raw ) ) {
367 QDataStream ds( &file );
368 ds << QString( "QPT 1.1" );
369 ds << csTitle;
370 ds << desc;
371 ds << (Q_INT8)csType;
372 QIMPenCharIterator ci( chars );
373 for ( ; ci.current(); ++ci ) {
374 QIMPenChar *pc = ci.current();
375 if ( ( (d == System) && pc->testFlag( QIMPenChar::System ) ) ||
376 ( (d == User) && !pc->testFlag( QIMPenChar::System ) ) ) {
377 ds << *pc;
378 }
379 if ( file.status() != IO_Ok )
380 break;
381 }
382 if ( file.status() == IO_Ok )
383 ok = TRUE;
384 }
385
386 if ( ok ) {
387 if ( ::rename( tmpFn.latin1(), fn.latin1() ) < 0 ) {
388 qWarning( "problem renaming file %s to %s, errno: %d",
389 tmpFn.latin1(), fn.latin1(), errno );
390 // remove the tmp file, otherwise, it will just lay around...
391 QFile::remove( tmpFn.latin1() );
392 ok = FALSE;
393 }
394 }
395
396 return ok;
397}
398
399QIMPenChar *QIMPenCharSet::at( int i )
400{
401 return chars.at(i);
402}
403
404void QIMPenCharSet::markDeleted( uint ch )
405{
406 QIMPenCharIterator ci( chars );
407 for ( ; ci.current(); ++ci ) {
408 QIMPenChar *pc = ci.current();
409 if ( pc->character() == ch && pc->testFlag( QIMPenChar::System ) )
410 pc->setFlag( QIMPenChar::Deleted );
411 }
412}
413
414/*!
415 Find the best matches for \a ch in this character set.
416*/
417QIMPenCharMatchList QIMPenCharSet::match( QIMPenChar *ch )
418{
419 QIMPenCharMatchList matches;
420
421 QIMPenCharIterator ci( chars );
422 for ( ; ci.current(); ++ci ) {
423 QIMPenChar *tmplChar = ci.current();
424 if ( tmplChar->testFlag( QIMPenChar::Deleted ) ) {
425 continue;
426 }
427 int err;
428 if ( ch->penStrokes().count() <= tmplChar->penStrokes().count() ) {
429 err = ch->match( tmplChar );
430 if ( err <= QIMPEN_MATCH_THRESHOLD ) {
431 if (tmplChar->penStrokes().count() != ch->penStrokes().count())
432 err = QIMPEN_MATCH_THRESHOLD;
433 QIMPenCharMatchList::Iterator it;
434 for ( it = matches.begin(); it != matches.end(); ++it ) {
435 if ( (*it).penChar->character() == tmplChar->character() &&
436 (*it).penChar->penStrokes().count() == tmplChar->penStrokes().count() ) {
437 if ( (*it).error > err )
438 (*it).error = err;
439 break;
440 }
441 }
442 if ( it == matches.end() ) {
443 QIMPenCharMatch m;
444 m.error = err;
445 m.penChar = tmplChar;
446 matches.append( m );
447 }
448 }
449 }
450 }
451 qHeapSort( matches );
452/*
453 QIMPenCharMatchList::Iterator it;
454 for ( it = matches.begin(); it != matches.end(); ++it ) {
455 qDebug( "Match: \'%c\', error %d, strokes %d", (*it).penChar->character(),
456 (*it).error, (*it).penChar->penStrokes().count() );
457 }
458*/
459 return matches;
460}
461
462/*!
463 Add a character \a ch to this set.
464 QIMPenCharSet will delete this character when it is no longer needed.
465*/
466void QIMPenCharSet::addChar( QIMPenChar *ch )
467{
468 if ( ch->penStrokes().count() > maxStrokes )
469 maxStrokes = ch->penStrokes().count();
470 chars.append( ch );
471}
472
473/*!
474 Remove a character by reference \a ch from this set.
475 QIMPenCharSet will delete this character.
476*/
477void QIMPenCharSet::removeChar( QIMPenChar *ch )
478{
479 chars.remove( ch );
480}
481
482/*!
483 Move the character up the list of characters.
484*/
485void QIMPenCharSet::up( QIMPenChar *ch )
486{
487 int idx = chars.findRef( ch );
488 if ( idx > 0 ) {
489 chars.take();
490 chars.insert( idx - 1, ch );
491 }
492}
493
494/*!
495 Move the character down the list of characters.
496*/
497void QIMPenCharSet::down( QIMPenChar *ch )
498{
499 int idx = chars.findRef( ch );
500 if ( idx >= 0 && idx < (int)chars.count() - 1 ) {
501 chars.take();
502 chars.insert( idx + 1, ch );
503 }
504}
505
diff --git a/inputmethods/handwriting/qimpenchar.h b/inputmethods/handwriting/qimpenchar.h
new file mode 100644
index 0000000..9a5f687
--- a/dev/null
+++ b/inputmethods/handwriting/qimpenchar.h
@@ -0,0 +1,157 @@
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#ifndef QIMPENCHAR_H_
22#define QIMPENCHAR_H_
23
24#include <qlist.h>
25#include <qvaluelist.h>
26#include <qcstring.h>
27#include "qimpenstroke.h"
28
29struct QIMPenSpecialKeys {
30 int code;
31 char *name;
32};
33
34extern const QIMPenSpecialKeys qimpen_specialKeys[];
35
36
37class QIMPenChar
38{
39public:
40 QIMPenChar();
41 QIMPenChar( const QIMPenChar & );
42
43 unsigned int character() const { return ch; }
44 void setCharacter( unsigned int c ) { ch = c; }
45
46 const QString &data() const { return d; }
47 void setData( const QString &ba ) { d = ba; }
48
49 QString name() const;
50 bool isEmpty() const { return strokes.isEmpty(); }
51 unsigned int strokeCount() const { return strokes.count(); }
52 unsigned int strokeLength( int s ) const;
53 void clear();
54 int match( QIMPenChar *ch );
55 const QIMPenStrokeList &penStrokes() { return strokes; }
56 QPoint startingPoint() const { return strokes.getFirst()->startingPoint(); }
57 QRect boundingRect();
58
59 void setFlag( int f ) { flags |= f; }
60 void clearFlag( int f ) { flags &= ~f; }
61 bool testFlag( int f ) { return flags & f; }
62
63 enum Flags { System=0x01, Deleted=0x02, CombineRight=0x04, Data=0x08 };
64 // Correspond to codes in template files. Do not change values.
65 enum Mode { ModeBase=0x4000, Caps=0x4001, Shortcut=0x4002, CapsLock=0x4003,
66 Punctuation=0x4004, Symbol=0x4005, Extended=0x4006 };
67
68 QIMPenChar &operator=( const QIMPenChar &s );
69
70 void addStroke( QIMPenStroke * );
71
72protected:
73 unsigned int ch;
74 QString d;
75 Q_UINT8 flags;
76 QIMPenStrokeList strokes;
77
78 friend QDataStream &operator<< (QDataStream &, const QIMPenChar &);
79 friend QDataStream &operator>> (QDataStream &, QIMPenChar &);
80};
81
82typedef QList<QIMPenChar> QIMPenCharList;
83typedef QListIterator<QIMPenChar> QIMPenCharIterator;
84
85QDataStream & operator<< (QDataStream & s, const QIMPenChar &ws);
86QDataStream & operator>> (QDataStream & s, QIMPenChar &ws);
87
88struct QIMPenCharMatch
89{
90 int error;
91 QIMPenChar *penChar;
92
93 bool operator>( const QIMPenCharMatch &m );
94 bool operator<( const QIMPenCharMatch &m );
95 bool operator<=( const QIMPenCharMatch &m );
96};
97
98typedef QValueList<QIMPenCharMatch> QIMPenCharMatchList;
99
100
101class QIMPenCharSet
102{
103public:
104 QIMPenCharSet();
105 QIMPenCharSet( const QString &fn );
106
107 bool isEmpty() const { return chars.isEmpty(); }
108 unsigned int count() const { return chars.count(); }
109 void clear() { chars.clear(); }
110
111 void setDescription( const QString &d ) { desc = d; }
112 QString description() const { return desc; }
113 void setTitle( const QString &t ) { csTitle = t; }
114 QString title() const { return csTitle; }
115
116 QIMPenCharMatchList match( QIMPenChar *ch );
117 void addChar( QIMPenChar *ch );
118 void removeChar( QIMPenChar *ch );
119 QIMPenChar *at( int i );
120
121 unsigned maximumStrokes() const { return maxStrokes; }
122
123 void up( QIMPenChar *ch );
124 void down( QIMPenChar *ch );
125
126 enum Domain { System, User };
127 enum Type { Unknown=0x00, Lower=0x01, Upper=0x02, Combining=0x04,
128 Numeric=0x08, Punctuation=0x10, Symbol=0x20, Shortcut=0x40 };
129
130 const QIMPenCharList &characters() const { return chars; }
131
132 void setType( Type t ) { csType = t; }
133 Type type() const { return csType; }
134
135 const QString &filename( Domain d ) const;
136 void setFilename( const QString &fn, Domain d=System );
137 bool load( const QString &fn, Domain d=System );
138 bool save( Domain d=System );
139
140protected:
141 void markDeleted( uint ch );
142
143protected:
144 QString csTitle;
145 QString desc;
146 QString sysFilename;
147 QString userFilename;
148 Type csType;
149 unsigned maxStrokes;
150 QIMPenCharList chars;
151 QIMPenCharMatchList matches;
152};
153
154typedef QList<QIMPenCharSet> QIMPenCharSetList;
155typedef QListIterator<QIMPenCharSet> QIMPenCharSetIterator;
156
157#endif
diff --git a/inputmethods/handwriting/qimpencombining.cpp b/inputmethods/handwriting/qimpencombining.cpp
new file mode 100644
index 0000000..30459e7
--- a/dev/null
+++ b/inputmethods/handwriting/qimpencombining.cpp
@@ -0,0 +1,141 @@
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 <qfile.h>
22#include <qtl.h>
23#include <math.h>
24#include <limits.h>
25#include <qdatastream.h>
26#include "qimpencombining.h"
27
28static unsigned int combiningSymbols[] = { '\\', '/', '^', '~', '\"', 'o' };
29static unsigned int combiningChars[][7] = {
30 // \ / ^ ~ "
31 { 'A', 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5 },
32 { 'O', 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0000 },
33 { 'U', 0x00D9, 0x00DA, 0x00DB, 0x0000, 0x00DC, 0x0000 },
34 { 'E', 0x00C8, 0x00C9, 0x00CA, 0x0000, 0x00CB, 0x0000 },
35 { 'I', 0x00CC, 0x00CD, 0x00CE, 0x0000, 0x00CF, 0x0000 },
36 { 'a', 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5 },
37 { 'e', 0x00E8, 0x00E9, 0x00EA, 0x0000, 0x00EB, 0x0000 },
38 { 'i', 0x00EC, 0x00ED, 0x00EE, 0x0000, 0x00EF, 0x0000 },
39 { 'n', 0x0000, 0x0000, 0x0000, 0x00F1, 0x0000, 0x0000 },
40 { 'o', 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0000 },
41 { 'u', 0x00F9, 0x00FA, 0x00FB, 0x0000, 0x00FC, 0x0000 },
42 { 'y', 0x0000, 0x00FD, 0x0000, 0x0000, 0x00FF, 0x0000 },
43 { 0, 0, 0, 0, 0, 0, 0 }
44};
45
46
47QIMPenCombining::QIMPenCombining()
48{
49}
50
51QIMPenCombining::QIMPenCombining( const QString &fn )
52 : QIMPenCharSet( fn )
53{
54}
55
56void QIMPenCombining::addCombined( QIMPenCharSet *cs )
57{
58 unsigned int count = cs->count();
59 QIMPenCharIterator it( cs->characters() );
60 for ( ; it.current() && count; ++it, --count ) {
61 QIMPenChar *pc = it.current();
62 if ( pc->testFlag( QIMPenChar::Deleted ) )
63 continue;
64 int charIdx = findCombining( pc->character() );
65 if ( charIdx < 0 )
66 continue;
67 for ( int i = 0; i < 6; i++ ) {
68 if ( combiningChars[charIdx][i+1] ) {
69 QIMPenCharIterator cit( chars );
70 for ( ; cit.current(); ++cit ) {
71 QIMPenChar *accentPc = cit.current();
72 if ( accentPc->character() == combiningSymbols[i] ) {
73 QIMPenChar *combined = combine( pc, accentPc );
74 combined->setCharacter( combiningChars[charIdx][i+1] );
75 cs->addChar( combined );
76 }
77 }
78 }
79 }
80 }
81}
82
83int QIMPenCombining::findCombining( unsigned int ch ) const
84{
85 int i = 0;
86 while ( combiningChars[i][0] ) {
87 if ( combiningChars[i][0] == ch )
88 return i;
89 i++;
90 }
91
92 return -1;
93}
94
95QIMPenChar *QIMPenCombining::combine( QIMPenChar *base, QIMPenChar *accent )
96{
97 QRect brect = base->boundingRect();
98 QRect arect = accent->boundingRect();
99 int offset;
100 if ( accent->testFlag( QIMPenChar::CombineRight ) )
101 offset = brect.left() - arect.left() + brect.width() + 2;
102 else
103 offset = brect.left() - arect.left() + (brect.width() - arect.width())/2;
104 QIMPenChar *combined = 0;
105 if ( base->character() == 'i' ) {
106 // Hack to remove the dot from i's when combining.
107 if ( base->penStrokes().count() > 1 ) {
108 combined = new QIMPenChar;
109 QIMPenStrokeIterator it( base->penStrokes() );
110 for ( unsigned int i = 0; i < base->penStrokes().count()-1; ++it, i++ ) {
111 QIMPenStroke *st = new QIMPenStroke( *(it.current()) );
112 combined->addStroke( st );
113 }
114 combined->setFlag( QIMPenChar::System );
115 }
116 }
117 if ( !combined )
118 combined = new QIMPenChar( *base );
119 QIMPenStrokeIterator it( accent->penStrokes() );
120 for ( ; it.current(); ++it ) {
121 QIMPenStroke *st = new QIMPenStroke( *(it.current()) );
122 st->setStartingPoint( st->startingPoint() + QPoint(offset, 0 ));
123 combined->addStroke( st );
124 delete st;
125 }
126
127 return combined;
128}
129
130QIMPenChar *QIMPenCombining::penChar( int type )
131{
132 QIMPenCharIterator it( chars );
133 for ( ; it.current(); ++it ) {
134 QIMPenChar *pc = it.current();
135 if ( pc->character() == combiningSymbols[type] )
136 return pc;
137 }
138
139 return 0;
140}
141
diff --git a/inputmethods/handwriting/qimpencombining.h b/inputmethods/handwriting/qimpencombining.h
new file mode 100644
index 0000000..778cb8f
--- a/dev/null
+++ b/inputmethods/handwriting/qimpencombining.h
@@ -0,0 +1,41 @@
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#ifndef QIMPENCOMBINING_H_
22#define QIMPENCOMBINING_H_
23
24#include <qlist.h>
25#include "qimpenchar.h"
26
27class QIMPenCombining : public QIMPenCharSet
28{
29public:
30 QIMPenCombining();
31 QIMPenCombining( const QString &fn );
32
33 void addCombined( QIMPenCharSet * );
34
35protected:
36 int findCombining( unsigned int ch ) const;
37 QIMPenChar *combine( QIMPenChar *base, QIMPenChar *accent );
38 QIMPenChar *penChar( int type );
39};
40
41#endif
diff --git a/inputmethods/handwriting/qimpenhelp.cpp b/inputmethods/handwriting/qimpenhelp.cpp
new file mode 100644
index 0000000..5ee46a2
--- a/dev/null
+++ b/inputmethods/handwriting/qimpenhelp.cpp
@@ -0,0 +1,410 @@
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 "qimpenwidget.h"
22#include "qimpencombining.h"
23#include "qimpenmatch.h"
24#include "qimpenhelp.h"
25
26#include <qpe/qpeapplication.h>
27#include <qpe/global.h>
28#include <qpe/config.h>
29#include <qpe/stringutil.h>
30
31#include <qtextview.h>
32#include <qlabel.h>
33#include <qlistbox.h>
34#include <qcombobox.h>
35#include <qpushbutton.h>
36#include <qlayout.h>
37#include <qtimer.h>
38#include <qtextstream.h>
39
40/* XPM */
41static const char * const left_xpm[] = {
42"16 16 2 1",
43" c None",
44". c #000000",
45" ",
46" ",
47" ",
48" . ",
49" .. ",
50" ... ",
51" .... ",
52" ..... ",
53" ...... ",
54" ..... ",
55" .... ",
56" ... ",
57" .. ",
58" . ",
59" ",
60" "};
61
62
63/* XPM */
64static const char * const right_xpm[] = {
65"16 16 2 1",
66" c None",
67". c #000000",
68" ",
69" ",
70" ",
71" . ",
72" .. ",
73" ... ",
74" .... ",
75" ..... ",
76" ...... ",
77" ..... ",
78" .... ",
79" ... ",
80" .. ",
81" . ",
82" ",
83" "};
84
85class CharListItem : public QListBoxText
86{
87public:
88 CharListItem( const QString &text, uint c )
89 : QListBoxText( text )
90 {
91 _code = c;
92 }
93
94 uint code() const { return _code; }
95
96protected:
97 uint _code;
98};
99
100HandwritingHelp::HandwritingHelp( QIMPenProfile *p, QWidget *parent, const char *name, WFlags f )
101 : QTabWidget( parent, name, f )
102{
103 setCaption( tr("Handwriting Help") );
104 QTextView *help = new QTextView( this );
105 help->setFrameStyle( QFrame::NoFrame );
106 help->setText(
107 tr( "<ul><li>When you start to use the handwriting recogniser "
108 "write slowly, accurately and firmly."
109 "<li>Use the guide lines when drawing your characters."
110 "<li>When drawing a character with multiple strokes, each "
111 "successive stroke must be drawn before the grayed strokes are erased."
112 "<li>Practice your handwriting using the handwriting trainer."
113 "<li>When adding your own character templates make sure they "
114 "are sufficiently different from other characters' templates."
115 "</ul>") );
116
117 addTab( help, tr("Tips") );
118
119 HandwritingTrainer *trainer = new HandwritingTrainer( p, this );
120 addTab( trainer, tr("Trainer") );
121}
122
123void HandwritingHelp::showEvent( QShowEvent * )
124{
125 Global::hideInputMethod();
126}
127
128void HandwritingHelp::hideEvent( QHideEvent * )
129{
130 Global::showInputMethod();
131}
132
133//---------------------------------------------------------------------------
134
135HandwritingTrainer::HandwritingTrainer( QIMPenProfile *p, QWidget *parent, const char *name )
136 : QWidget( parent, name ), profile(p)
137{
138 QGridLayout *gl = new QGridLayout( this, 5, 2, 0, 4 );
139 gl->setRowStretch( 1, 1 );
140 gl->setRowStretch( 2, 1 );
141 gl->setColStretch( 1, 1 );
142
143 charSetCombo = new QComboBox( this );
144 gl->addMultiCellWidget( charSetCombo, 0, 0, 0, 1 );
145 connect( charSetCombo, SIGNAL(activated(int)), SLOT(selectCharSet(int)));
146 QIMPenCharSetIterator it( profile->charSets() );
147 for ( ; it.current(); ++it ) {
148 charSetCombo->insertItem( it.current()->description() );
149 }
150
151 charList = new QListBox( this );
152 charList->setHScrollBarMode( QListBox::AlwaysOff );
153 charList->setFixedWidth( 80 );
154 connect( charList, SIGNAL(highlighted(int)), this, SLOT(selectChar(int)) );
155 gl->addMultiCellWidget( charList, 1, 2, 0, 0 );
156
157 QLabel *help = new QLabel( this );
158 help->setAlignment( AlignLeft | AlignVCenter | WordBreak );
159 gl->addWidget( help, 1, 1 );
160 help->setText(
161 tr( "Select a character from the list. The writing area on the left "
162 "shows the reference character. Practice writing in the area on "
163 "the right.") );
164
165 result = new QLabel( this );
166 gl->addMultiCellWidget( result, 2, 3, 1, 1 );
167
168 matcher = new QIMPenMatch( this );
169 matcher->setCharSet( currentSet );
170 connect( matcher, SIGNAL(noMatch()), this, SLOT(noMatch()) );
171 connect( matcher, SIGNAL(matchedCharacters(const QIMPenCharMatchList &)),
172 this, SLOT(matched(const QIMPenCharMatchList &)) );
173
174 QHBoxLayout *hb = new QHBoxLayout();
175 gl->addLayout( hb, 3, 0 );
176 prevBtn = new QPushButton( this );
177 prevBtn->setPixmap( QPixmap( (const char **)left_xpm ) );
178 connect( prevBtn, SIGNAL(clicked()), SLOT(prevChar()));
179 hb->addWidget( prevBtn );
180
181 nextBtn = new QPushButton( this );
182 nextBtn->setPixmap( QPixmap( (const char **)right_xpm ) );
183 connect( nextBtn, SIGNAL(clicked()), SLOT(nextChar()));
184 hb->addWidget( nextBtn );
185
186 refPw = new QIMPenWidget( this );
187 refPw->setReadOnly( TRUE );
188 gl->addWidget( refPw, 4, 0 );
189
190 pracPw = new QIMPenWidget( this );
191 connect( matcher, SIGNAL(removeStroke()), pracPw, SLOT(removeStroke()) );
192 connect( pracPw, SIGNAL(beginStroke()),
193 this, SLOT(beginStroke()) );
194 connect( pracPw, SIGNAL(stroke( QIMPenStroke * )),
195 this, SLOT(strokeEntered( QIMPenStroke * )) );
196 connect( pracPw, SIGNAL(beginStroke()),
197 matcher, SLOT(beginStroke()) );
198 connect( pracPw, SIGNAL(stroke( QIMPenStroke * )),
199 matcher, SLOT(strokeEntered( QIMPenStroke * )) );
200 gl->addWidget( pracPw, 4, 1 );
201
202 redrawTimer = new QTimer( this );
203 connect( redrawTimer, SIGNAL(timeout()), this, SLOT(redrawChar()) );
204 redrawTimer->start( 5000 );
205
206 currentSet = 0;
207 charSetCombo->setCurrentItem( 1 );
208 selectCharSet( 1 );
209}
210
211HandwritingTrainer::~HandwritingTrainer()
212{
213}
214
215void HandwritingTrainer::showEvent( QShowEvent * )
216{
217 redrawChar();
218 redrawTimer->start( 5000 );
219}
220
221void HandwritingTrainer::setCurrentChar( QIMPenChar *c )
222{
223 currentChar = c;
224 refPw->showCharacter( currentChar );
225 pracPw->clear();
226 if ( currentChar ) {
227 prevBtn->setEnabled( findPrev() != 0 );
228 nextBtn->setEnabled( findNext() != 0 );
229 }
230 result->setText( "" );
231 redrawTimer->start( 5000 );
232}
233
234void HandwritingTrainer::selectChar( int i )
235{
236 currentChar = 0;
237 currentCode = ((CharListItem *)charList->item(i))->code();
238 QIMPenCharIterator it(currentSet->characters() );
239 for ( ; it.current(); ++it ) {
240 if ( it.current()->character() == currentCode &&
241 !it.current()->testFlag( QIMPenChar::Deleted ) ) {
242 setCurrentChar( it.current() );
243 break;
244 }
245 }
246 if ( !it.current() )
247 setCurrentChar( 0 );
248}
249
250void HandwritingTrainer::selectCharSet( int i )
251{
252 if ( currentSet ) {
253 refPw->removeCharSet( 0 );
254 pracPw->removeCharSet( 0 );
255 }
256 currentSet = profile->charSets().at( i );
257 fillCharList();
258 refPw->insertCharSet( currentSet );
259 pracPw->insertCharSet( currentSet );
260 matcher->setCharSet( currentSet );
261 if ( charList->count() ) {
262 charList->setSelected( 0, TRUE );
263 selectChar(0);
264 }
265}
266
267void HandwritingTrainer::noMatch()
268{
269 result->setText( "No match" );
270}
271
272void HandwritingTrainer::matched( const QIMPenCharMatchList &ml )
273{
274 int maxErr = 20000 + (*ml.begin()).penChar->strokeLength(0) * 1000;
275 int baseErr = (*ml.begin()).penChar->strokeLength(0) * 250;
276 unsigned int numStrokes = (*ml.begin()).penChar->strokeCount();
277 QIMPenCharMatchList::ConstIterator it;
278 /*
279 for ( it = ml.begin(); it != ml.end(); ++it ) {
280 if ( (*it).penChar->strokeCount() == numStrokes ) {
281 if ( (*it).error > maxErr )
282 maxErr = (*it).error;
283 }
284 }
285 */
286 int i;
287 QString res;
288 QTextStream ts(&res, IO_WriteOnly);
289 ts << "<qt>" << tr("Matched: ");
290 for ( i = 0, it = ml.begin(); it != ml.end() && i < 4; ++it, i++ ) {
291 if ( (*it).penChar->strokeCount() == numStrokes ) {
292 int rate = 100 - ( ((*it).error - baseErr) * 100 ) / maxErr;
293 if ( it != ml.begin() ) {
294 if ( rate < -10 )
295 continue;
296 ts << "<br>";
297 ts << tr("Similar to: ");
298 }
299 ts << "<big>";
300 if ( (*it).penChar->character() == currentChar->character() )
301 ts << "<b>";
302 ts << Qtopia::escapeString((*it).penChar->name());
303 ts << " (" << rateString(rate) << ")";
304 if ( (*it).penChar->character() == currentChar->character() )
305 ts << "</b>";
306 ts << "</big>";
307 }
308 }
309 ts << "</qt>";
310 result->setText( res );
311}
312
313QString HandwritingTrainer::rateString( int rate ) const
314{
315 if ( rate < 1 )
316 rate = 1;
317 if ( rate > 100 )
318 rate = 100;
319 return tr("%1%").arg(rate);
320}
321
322void HandwritingTrainer::prevChar()
323{
324 QIMPenChar *pc = findPrev();
325 if ( pc )
326 setCurrentChar( pc );
327}
328
329void HandwritingTrainer::nextChar()
330{
331 QIMPenChar *pc = findNext();
332 if ( pc )
333 setCurrentChar( pc );
334}
335
336void HandwritingTrainer::redrawChar()
337{
338 if ( currentChar )
339 refPw->showCharacter( currentChar );
340}
341
342void HandwritingTrainer::beginStroke()
343{
344 redrawTimer->start( 5000 );
345}
346
347void HandwritingTrainer::strokeEntered( QIMPenStroke * )
348{
349 pracPw->greyStroke();
350}
351
352QIMPenChar *HandwritingTrainer::findPrev()
353{
354 if ( !currentChar )
355 return 0;
356 QIMPenCharIterator it( currentSet->characters() );
357 bool found = FALSE;
358 for ( it.toLast(); it.current(); --it ) {
359 if ( !found && it.current() == currentChar )
360 found = TRUE;
361 else if ( found && it.current()->character() == currentCode &&
362 !it.current()->testFlag( QIMPenChar::Deleted ) ) {
363 return it.current();
364 }
365 }
366
367 return 0;
368}
369
370QIMPenChar *HandwritingTrainer::findNext()
371{
372 if ( !currentChar )
373 return 0;
374 QIMPenCharIterator it( currentSet->characters() );
375 bool found = FALSE;
376 for ( ; it.current(); ++it ) {
377 if ( !found && it.current() == currentChar )
378 found = TRUE;
379 else if ( found && it.current()->character() == currentCode &&
380 !it.current()->testFlag( QIMPenChar::Deleted ) ) {
381 return it.current();
382 }
383 }
384
385 return 0;
386}
387
388void HandwritingTrainer::fillCharList()
389{
390 charList->clear();
391 QIMPenCharIterator it( currentSet->characters() );
392 CharListItem *li = 0;
393 for ( ; it.current(); ++it ) {
394 uint ch = it.current()->character();
395 QString n = it.current()->name();
396 if ( !n.isEmpty() )
397 li = new CharListItem( n, ch );
398 if ( li ) {
399 CharListItem *i = (CharListItem *)charList->findItem( li->text() );
400 if ( !i || i->code() != ch ) {
401 charList->insertItem( li );
402 } else {
403 delete li;
404 li = 0;
405 }
406 }
407 }
408 currentChar = 0;
409}
410
diff --git a/inputmethods/handwriting/qimpenhelp.h b/inputmethods/handwriting/qimpenhelp.h
new file mode 100644
index 0000000..07cb035
--- a/dev/null
+++ b/inputmethods/handwriting/qimpenhelp.h
@@ -0,0 +1,85 @@
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#include <qtabwidget.h>
21#include <qlist.h>
22#include "qimpenchar.h"
23#include "qimpenprofile.h"
24
25class QListBox;
26class QLabel;
27class QComboBox;
28class QPushButton;
29class QTimer;
30class QIMPenWidget;
31class QIMPenMatch;
32
33class HandwritingHelp : public QTabWidget
34{
35 Q_OBJECT
36public:
37 HandwritingHelp( QIMPenProfile *p, QWidget *parent=0, const char *name=0, WFlags f=0 );
38
39protected:
40 virtual void showEvent( QShowEvent * );
41 virtual void hideEvent( QHideEvent * );
42};
43
44class HandwritingTrainer : public QWidget
45{
46 Q_OBJECT
47public:
48 HandwritingTrainer( QIMPenProfile *p, QWidget *parent=0, const char *name=0 );
49 ~HandwritingTrainer();
50
51private slots:
52 void selectChar( int );
53 void selectCharSet( int );
54 void noMatch();
55 void matched( const QIMPenCharMatchList &ml );
56 void prevChar();
57 void nextChar();
58 void redrawChar();
59 void beginStroke();
60 void strokeEntered( QIMPenStroke * );
61
62private:
63 virtual void showEvent( QShowEvent * );
64 QString rateString( int rate ) const;
65 void setCurrentChar( QIMPenChar *c );
66 void fillCharList();
67 QIMPenChar *findPrev();
68 QIMPenChar *findNext();
69
70private:
71 QIMPenMatch *matcher;
72 QIMPenCharSet *currentSet;
73 QIMPenChar *currentChar;
74 QIMPenProfile *profile;
75 uint currentCode;
76 QIMPenWidget *refPw;
77 QIMPenWidget *pracPw;
78 QComboBox *charSetCombo;
79 QListBox *charList;
80 QLabel *result;
81 QPushButton *prevBtn;
82 QPushButton *nextBtn;
83 QTimer *redrawTimer;
84};
85
diff --git a/inputmethods/handwriting/qimpeninput.cpp b/inputmethods/handwriting/qimpeninput.cpp
new file mode 100644
index 0000000..6718b26
--- a/dev/null
+++ b/inputmethods/handwriting/qimpeninput.cpp
@@ -0,0 +1,515 @@
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 "qimpenwidget.h"
22#include "qimpensetup.h"
23#include "qimpeninput.h"
24#include "qimpencombining.h"
25#include "qimpenwordpick.h"
26#include "qimpenmatch.h"
27#include "qimpenhelp.h"
28
29#include <qpe/qpeapplication.h>
30#include <qpe/qdawg.h>
31#include <qpe/config.h>
32#include <qpe/global.h>
33
34#include <qlayout.h>
35#include <qpushbutton.h>
36#include <qlabel.h>
37#include <qtimer.h>
38#include <qdir.h>
39
40#include <limits.h>
41
42// We'll use little pixmaps for the buttons to save screen space.
43
44/* XPM */
45static const char * const pen_xpm[] = {
46"12 12 4 1",
47 " c None",
48 ".c #000000",
49 "+c #FFFFFF",
50 "@c #808080",
51" . ",
52" .+. ",
53" ..@@.",
54" .+@.. ",
55" .+@@. ",
56" .+@@. ",
57" .+@@. ",
58" .@.@. ",
59" .@@. ",
60" .... ",
61" .. ",
62" "};
63
64
65/* XPM */
66static char * bs_xpm[] = {
67"12 12 5 1",
68 " c None",
69 ".c #333333",
70 "+c #000000",
71 "@c #FFFFFF",
72 "#c #666666",
73" ",
74" ",
75" ",
76" . ",
77" ++ ",
78" +@#+++++. ",
79" +@@@@@@@@+ ",
80" +@#+++++. ",
81" ++ ",
82" . ",
83" ",
84" "};
85
86
87/* XPM */
88static char * enter_xpm[] = {
89"12 12 5 1",
90 " c None",
91 ".c #333333",
92 "+c #000000",
93 "@c #FFFFFF",
94 "#c #666666",
95" ",
96" .+. ",
97" +@+ ",
98" . +@+ ",
99" ++ +@+ ",
100" +@#++++@+ ",
101" +@@@@@@@@+ ",
102" +@#+++++. ",
103" ++ ",
104" . ",
105" ",
106" "};
107
108
109
110/* XPM */
111static char * help_xpm[] = {
112"12 12 5 1",
113 " c None",
114 ".c #000000",
115 "+c #FFFFFF",
116 "@c #666666",
117 "#c #333333",
118" ",
119" ... ",
120" .+++. ",
121" .+..@+. ",
122" #.# .+. ",
123" .+. ",
124" .+. ",
125" .+. ",
126" .+. ",
127" #.# ",
128" .+. ",
129" #.# "};
130
131
132/*!
133 \class QIMPenInput qimpeninput.h
134
135 Pen input widget.
136*/
137QIMPenInput::QIMPenInput( QWidget *parent, const char *name, WFlags f )
138 : QFrame( parent, name, f ), helpDlg(0), profile(0)
139{
140 setFrameStyle( Box | Plain );
141
142 profileList.setAutoDelete( true );
143
144 matcher = new QIMPenMatch( this );
145 connect( matcher, SIGNAL(keypress(uint)), this, SLOT(keypress(uint)) );
146 connect( matcher, SIGNAL(erase()), this, SLOT(erase()) );
147
148 QGridLayout *gl = new QGridLayout( this, 5, 2, 1, 0 );
149 gl->setColStretch( 0, 1 );
150
151 wordPicker = new QIMPenWordPick( this );
152 connect( wordPicker, SIGNAL(wordClicked(const QString &)),
153 this, SLOT(wordPicked(const QString &)) );
154 connect( matcher, SIGNAL(matchedCharacters(const QIMPenCharMatchList &)),
155 this, SLOT(matchedCharacters(const QIMPenCharMatchList &)) );
156 connect( matcher, SIGNAL(matchedWords(const QIMPenMatch::MatchWordList&)),
157 wordPicker, SLOT(setWords(const QIMPenMatch::MatchWordList&)) );
158 QFont f("smallsmooth",9);
159 QFontInfo fi( f );
160 wordPicker->setFont( f );
161 wordPicker->setBackgroundColor( white );
162 gl->addMultiCellWidget( wordPicker, 0, 0, 0, 1 );
163 if ( !Global::fixedDawg().root() || !matcher->isWordMatchingEnabled() )
164 wordPicker->hide();
165
166 pw = new QIMPenWidget( this );
167 gl->addMultiCellWidget( pw, 1, 4, 0, 0 );
168
169 int bh = pw->sizeHint().height()/4;
170
171 QPushButton *b = new QPushButton( this );
172 b->setFocusPolicy( NoFocus );
173 b->setPixmap( QPixmap( (const char **)bs_xpm ) );
174 b->setFixedHeight(pw->sizeHint().height()-3*bh); // left-over space goes here
175 b->setAutoRepeat( TRUE );
176 gl->addWidget( b, 1, 1 );
177 connect( b, SIGNAL(clicked()), SLOT(backspace()));
178
179 b = new QPushButton( this );
180 b->setFocusPolicy( NoFocus );
181 b->setPixmap( QPixmap( (const char **)enter_xpm ) );
182 b->setFixedHeight(bh);
183 b->setAutoRepeat( TRUE );
184 gl->addWidget( b, 2, 1 );
185 connect( b, SIGNAL(clicked()), SLOT(enter()));
186
187 helpBtn = new QPushButton( this );
188 helpBtn->setFocusPolicy( NoFocus );
189 helpBtn->setPixmap( QPixmap( (const char **)help_xpm ) );
190 helpBtn->setFixedHeight(bh);
191 gl->addWidget( helpBtn, 3, 1 );
192 connect( helpBtn, SIGNAL(clicked()), SLOT(help()));
193
194 QPixmap pm( (const char **)pen_xpm );
195 setupBtn = new QPushButton( this );
196 setupBtn->setFocusPolicy( NoFocus );
197 setupBtn->setPixmap( pm );
198 setupBtn->setFixedHeight(bh);
199 gl->addWidget( setupBtn, 4, 1 );
200 connect( setupBtn, SIGNAL(clicked()), SLOT(setup()));
201
202 connect( matcher, SIGNAL(removeStroke()), pw, SLOT(removeStroke()) );
203 connect( pw, SIGNAL(changeCharSet( QIMPenCharSet * )),
204 matcher, SLOT(setCharSet( QIMPenCharSet * )) );
205 connect( pw, SIGNAL(changeCharSet( int )),
206 this, SLOT(selectCharSet( int )) );
207 connect( pw, SIGNAL(beginStroke()),
208 matcher, SLOT(beginStroke()) );
209 connect( pw, SIGNAL(stroke( QIMPenStroke * )),
210 this, SLOT(strokeEntered( QIMPenStroke * )) );
211 connect( pw, SIGNAL(stroke( QIMPenStroke * )),
212 matcher, SLOT(strokeEntered( QIMPenStroke * )) );
213
214 shortcutCharSet = 0;
215 currCharSet = 0;
216 setupDlg = 0;
217 profile = 0;
218 mode = Normal;
219
220 loadProfiles();
221}
222
223QIMPenInput::~QIMPenInput()
224{
225 delete (HandwritingHelp*) helpDlg;
226}
227
228QSize QIMPenInput::sizeHint() const
229{
230 int fw = frameWidth();
231 int ps = wordPicker->isHidden() ? 0 : wordPicker->sizeHint().height();
232 return pw->sizeHint() + QSize( fw*2, fw*2+ps );
233}
234
235void QIMPenInput::loadProfiles()
236{
237 profileList.clear();
238 profile = 0;
239 delete shortcutCharSet;
240 shortcutCharSet = new QIMPenCharSet();
241 shortcutCharSet->setTitle( "Shortcut" );
242 QString path = QPEApplication::qpeDir() + "etc/qimpen";
243 QDir dir( path, "*.conf" );
244 QStringList list = dir.entryList();
245 QStringList::Iterator it;
246 for ( it = list.begin(); it != list.end(); ++it ) {
247 QIMPenProfile *p = new QIMPenProfile( path + "/" + *it );
248 profileList.append( p );
249 if ( p->shortcut() ) {
250 QIMPenCharIterator it( p->shortcut()->characters() );
251 for ( ; it.current(); ++it ) {
252 shortcutCharSet->addChar( new QIMPenChar(*it.current()) );
253 }
254 }
255 }
256
257 Config config( "handwriting" );
258 config.setGroup( "Settings" );
259 QString prof = config.readEntry( "Profile", "Default" );
260 selectProfile( prof );
261}
262
263void QIMPenInput::selectProfile( const QString &name )
264{
265 QListIterator<QIMPenProfile> it( profileList );
266 for ( ; it.current(); ++it ) {
267 if ( it.current()->name() == name ) {
268 profile = it.current();
269 break;
270 }
271 }
272
273 if ( !it.current() )
274 return;
275
276 pw->clearCharSets();
277 baseSets.clear();
278
279 matcher->setMultiStrokeTimeout( profile->multiStrokeTimeout() );
280 matcher->setWordMatchingEnabled( profile->matchWords() );
281
282 if ( !Global::fixedDawg().root() || !matcher->isWordMatchingEnabled() )
283 wordPicker->hide();
284 else
285 wordPicker->show();
286
287 if ( profile->uppercase() && profile->style() == QIMPenProfile::BothCases ) {
288 baseSets.append( profile->uppercase() );
289 pw->insertCharSet( profile->uppercase() );
290 }
291
292 if ( profile->lowercase() ) {
293 baseSets.append( profile->lowercase() );
294 pw->insertCharSet( profile->lowercase(), profile->style() == QIMPenProfile::BothCases ? 1 : 2 );
295 }
296
297 if ( profile->numeric() ) {
298 baseSets.append( profile->numeric() );
299 pw->insertCharSet( profile->numeric() );
300 }
301
302 if ( helpDlg )
303 delete (HandwritingHelp*) helpDlg;
304}
305
306void QIMPenInput::wordPicked( const QString &w )
307{
308 int bs = matcher->word().length();
309 for ( int i = 0; i < bs; i++ )
310 keypress( Qt::Key_Backspace << 16 );
311
312 for ( unsigned int i = 0; i < w.length(); i++ )
313 keypress( w[i].unicode() );
314
315 matcher->resetState();
316 wordPicker->clear();
317}
318
319void QIMPenInput::selectCharSet( int idx )
320{
321 if ( mode == Switch ) {
322 //qDebug( "Switch back to normal" );
323 pw->changeCharSet( baseSets.at(currCharSet), currCharSet );
324 mode = Normal;
325 }
326 currCharSet = idx;
327}
328
329void QIMPenInput::beginStroke()
330{
331}
332
333void QIMPenInput::strokeEntered( QIMPenStroke * )
334{
335 pw->greyStroke();
336}
337
338void QIMPenInput::erase()
339{
340 keypress( Qt::Key_Backspace << 16 );
341}
342
343void QIMPenInput::matchedCharacters( const QIMPenCharMatchList &cl )
344{
345 const QIMPenChar *ch = cl.first().penChar;
346 int scan = ch->character() >> 16;
347
348 if ( scan < QIMPenChar::ModeBase )
349 return;
350
351 // We matched a special character...
352
353 switch ( scan ) {
354 case QIMPenChar::Caps:
355 if ( profile->style() == QIMPenProfile::ToggleCases ) {
356 // qDebug( "Caps" );
357 if ( mode == SwitchLock ) {
358 // qDebug( "Switch to normal" );
359 pw->changeCharSet( profile->lowercase(), currCharSet );
360 mode = Switch;
361 } else {
362 // qDebug( "Switch to upper" );
363 pw->changeCharSet( profile->uppercase(), currCharSet );
364 mode = Switch;
365 }
366 }
367 break;
368 case QIMPenChar::CapsLock:
369 if ( profile->style() == QIMPenProfile::ToggleCases ) {
370 // qDebug( "CapsLock" );
371 if ( mode == Switch &&
372 baseSets.at(currCharSet) == profile->uppercase() ) {
373 // qDebug( "Switch to normal" );
374 pw->changeCharSet( profile->lowercase(), currCharSet );
375 // change our base set back to lower.
376 baseSets.remove( currCharSet );
377 baseSets.insert( currCharSet, profile->lowercase() );
378 mode = Normal;
379 } else {
380 // qDebug( "Switch to caps lock" );
381 pw->changeCharSet( profile->uppercase(), currCharSet );
382 // change our base set to upper.
383 baseSets.remove( currCharSet );
384 baseSets.insert( currCharSet, profile->uppercase() );
385 mode = SwitchLock;
386 }
387 }
388 break;
389 case QIMPenChar::Punctuation:
390 if ( profile->punctuation() ) {
391 //qDebug( "Switch to punctuation" );
392 pw->changeCharSet( profile->punctuation(), currCharSet );
393 mode = Switch;
394 }
395 break;
396 case QIMPenChar::Symbol:
397 if ( profile->symbol() ) {
398 //qDebug( "Switch to symbol" );
399 pw->changeCharSet( profile->symbol(), currCharSet );
400 mode = Switch;
401 }
402 break;
403 case QIMPenChar::Shortcut:
404 if ( shortcutCharSet ) {
405 pw->changeCharSet( shortcutCharSet, currCharSet );
406 mode = Switch;
407 }
408 break;
409 case QIMPenChar::Extended:
410 handleExtended( ch->data() );
411 break;
412 }
413}
414
415void QIMPenInput::keypress( uint scan_uni )
416{
417 int scan = scan_uni >> 16;
418 if ( !scan ) {
419 if ( scan_uni >= 'a' && scan_uni <= 'z' ) {
420 scan = Qt::Key_A + scan_uni - 'a';
421 } else if ( scan_uni >= 'A' && scan_uni <= 'Z' ) {
422 scan = Qt::Key_A + scan_uni - 'A';
423 } else if ( scan_uni == ' ' ) {
424 scan = Qt::Key_Space;
425 }
426 }
427
428 switch ( scan ) {
429 case Key_Tab:
430 scan_uni = 9;
431 break;
432 case Key_Return:
433 scan_uni = 13;
434 break;
435 case Key_Backspace:
436 scan_uni = 8;
437 break;
438 case Key_Escape:
439 scan_uni = 27;
440 break;
441 default:
442 break;
443 }
444
445 if ( mode == Switch ) {
446 //qDebug( "Switch back to normal" );
447 pw->changeCharSet( baseSets.at(currCharSet), currCharSet );
448 if ( baseSets.at(currCharSet) == profile->uppercase() )
449 mode = SwitchLock;
450 else
451 mode = Normal;
452 }
453
454 emit key( scan_uni&0xffff, scan, 0, true, false );
455 emit key( scan_uni&0xffff, scan, 0, false, false );
456}
457
458void QIMPenInput::handleExtended( const QString &ex )
459{
460 if ( ex.find( "Select" ) == 0 ) {
461 QString set = ex.mid( 7 );
462 qDebug( "Select new profile: %s", set.latin1() );
463 selectProfile( set );
464 }
465}
466
467void QIMPenInput::help()
468{
469 if ( helpDlg )
470 delete (HandwritingHelp*) helpDlg;
471 helpDlg = new HandwritingHelp( profile, 0, 0, WDestructiveClose );
472 helpDlg->showMaximized();
473 helpDlg->show();
474 helpDlg->raise();
475}
476
477/*!
478 Open the setup dialog
479*/
480void QIMPenInput::setup()
481{
482 if ( !setupDlg ) {
483 // We are working with our copy of the char sets here.
484 setupDlg = new QIMPenSetup( profile, 0, 0, TRUE );
485 setupDlg->editor()->selectCharSet( profile->charSets().at(1) );// lower case? This is crap.
486 if ( qApp->desktop()->width() < 640 )
487 setupDlg->showMaximized();
488 Global::hideInputMethod();
489 setupDlg->exec();
490 loadProfiles();
491 delete setupDlg;
492 setupDlg = 0;
493 Global::showInputMethod();
494 } else {
495 setupDlg->raise();
496 }
497}
498
499void QIMPenInput::backspace()
500{
501 keypress( Qt::Key_Backspace << 16 );
502 matcher->backspace();
503}
504
505void QIMPenInput::enter()
506{
507 keypress( Qt::Key_Return << 16 );
508 matcher->resetState();
509}
510
511
512void QIMPenInput::resetState()
513{
514 matcher->resetState();
515}
diff --git a/inputmethods/handwriting/qimpeninput.h b/inputmethods/handwriting/qimpeninput.h
new file mode 100644
index 0000000..b4e4006
--- a/dev/null
+++ b/inputmethods/handwriting/qimpeninput.h
@@ -0,0 +1,94 @@
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#ifndef _QIMPENINPUT_H_
22#define _QIMPENINPUT_H_
23
24#include "qimpenprofile.h"
25
26#include <qpe/qdawg.h>
27
28#include <qframe.h>
29#include <qlist.h>
30#include <qguardedptr.h>
31
32class QPushButton;
33class QTimer;
34class QIMPenWidget;
35class QIMPenSetup;
36class QIMPenWordPick;
37class QIMPenMatch;
38class HandwritingHelp;
39
40class QIMPenInput : public QFrame
41{
42 Q_OBJECT
43public:
44 QIMPenInput( QWidget *parent = 0, const char *name = 0, WFlags f = 0 );
45 virtual ~QIMPenInput();
46
47 void resetState();
48
49 QSize sizeHint() const;
50
51signals:
52 void key( ushort, ushort, ushort, bool, bool );
53
54private slots:
55 void wordPicked( const QString & );
56 void selectCharSet( int );
57 void beginStroke();
58 void strokeEntered( QIMPenStroke *st );
59 void matchedCharacters( const QIMPenCharMatchList &cl );
60 void keypress( uint scan_uni );
61 void erase();
62 void help();
63 void setup();
64 void backspace();
65 void enter();
66
67private:
68 void loadProfiles();
69 void selectProfile( const QString &name );
70 void handleExtended( const QString & );
71 void updateWordMatch( QIMPenCharMatchList &ml );
72 void matchWords();
73 void scanDict( const QDawg::Node* n, int ipos, const QString& str, int error );
74
75 enum Mode { Normal, Switch, SwitchLock };
76
77private:
78 Mode mode;
79 QRect prefRect;
80 QIMPenWidget *pw;
81 QPushButton *helpBtn;
82 QPushButton *setupBtn;
83 QIMPenSetup *setupDlg;
84 QIMPenMatch *matcher;
85 QGuardedPtr<HandwritingHelp> helpDlg;
86 QIMPenProfile *profile;
87 QList<QIMPenProfile> profileList;
88 QIMPenCharSet *shortcutCharSet;
89 QIMPenCharSetList baseSets;
90 int currCharSet;
91 QIMPenWordPick *wordPicker;
92};
93
94#endif // _QIMPENINPUT_H_
diff --git a/inputmethods/handwriting/qimpenmatch.cpp b/inputmethods/handwriting/qimpenmatch.cpp
new file mode 100644
index 0000000..0d3e25a
--- a/dev/null
+++ b/inputmethods/handwriting/qimpenmatch.cpp
@@ -0,0 +1,365 @@
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 "qimpenmatch.h"
22
23#include <qpe/qdawg.h>
24#include <qpe/global.h>
25
26#include <qapplication.h>
27#include <qtimer.h>
28
29#include <limits.h>
30
31 #define ERROR_THRESHOLD 200000
32 #define LOOKAHEAD_ERROR 2500
33//#define DEBUG_QIMPEN
34
35QIMPenMatch::QIMPenMatch( QObject *parent, const char *name )
36 : QObject( parent, name )
37{
38 strokes.setAutoDelete( TRUE );
39 wordChars.setAutoDelete( TRUE );
40 wordMatches.setAutoDelete( TRUE );
41
42 multiTimer = new QTimer( this );
43 connect( multiTimer, SIGNAL(timeout()), this, SLOT(endMulti()) );
44
45 prevMatchChar = 0;
46 prevMatchError = INT_MAX;
47 charSet = 0;
48 multiCharSet = 0;
49 multiTimeout = 500;
50 canErase = FALSE;
51 doWordMatching = true;
52}
53
54QIMPenMatch::~QIMPenMatch()
55{
56}
57
58void QIMPenMatch::setCharSet( QIMPenCharSet *cs )
59{
60 charSet = cs;
61}
62
63void QIMPenMatch::beginStroke()
64{
65 multiTimer->stop();
66}
67
68void QIMPenMatch::strokeEntered( QIMPenStroke *st )
69{
70#ifdef DEBUG_QIMPEN
71 qDebug( "---------- new stroke -------------" );
72#endif
73 strokes.append( new QIMPenStroke( *st ) );
74
75 QIMPenChar testChar;
76 QIMPenStrokeIterator it(strokes);
77 for ( ; it.current(); ++it ) {
78 testChar.addStroke( it.current() );
79 }
80
81 QIMPenCharMatchList ml;
82 if ( strokes.count() > 1 && multiCharSet ) {
83#ifdef DEBUG_QIMPEN
84 qDebug( "Matching against multi set" );
85#endif
86 ml = multiCharSet->match( &testChar );
87 } else {
88#ifdef DEBUG_QIMPEN
89 qDebug( "Matching against single set" );
90#endif
91 ml = charSet->match( &testChar );
92 }
93
94 processMatches( ml );
95}
96
97void QIMPenMatch::processMatches( QIMPenCharMatchList &ml )
98{
99#ifdef DEBUG_QIMPEN
100 qDebug( "Entering strokes.count() = %d", strokes.count() );
101#endif
102 QIMPenCharMatch candidate1 = { INT_MAX, 0 };
103 QIMPenCharMatch candidate2 = { INT_MAX, 0 };
104 QIMPenCharMatchList ml2;
105
106 if ( ml.count() ) {//&&
107 // ml.first().penChar->penStrokes().count() == strokes.count() ) {
108 candidate1 = ml.first();
109#ifdef DEBUG_QIMPEN
110 qDebug( QString("Candidate1 = %1").arg(QChar(candidate1.penChar->character())) );
111#endif
112 }
113
114 if ( strokes.count() > 1 ) {
115 // See if the last stroke can match a new character
116 QIMPenChar testChar;
117 QIMPenStroke *st = strokes.at(strokes.count()-1);
118 testChar.addStroke( st );
119 ml2 = charSet->match( &testChar );
120 if ( ml2.count() ) {
121 candidate2 = ml2.first();
122#ifdef DEBUG_QIMPEN
123 qDebug( QString("Candidate2 = %1").arg(QChar(candidate2.penChar->character())) );
124#endif
125 }
126 }
127
128 bool eraseLast = FALSE;
129 bool output = TRUE;
130
131 if ( candidate1.penChar && candidate2.penChar ) {
132 // Hmmm, a multi-stroke or a new character are both possible.
133 // Bias the multi-stroke case.
134 if ( QMAX(candidate2.error, prevMatchError)*3 < candidate1.error ) {
135 int i = strokes.count()-1;
136 while ( i-- ) {
137 strokes.removeFirst();
138 emit removeStroke();
139 }
140 prevMatchChar = candidate2.penChar;
141 prevMatchError = candidate2.error;
142 multiCharSet = charSet;
143 ml = ml2;
144#ifdef DEBUG_QIMPEN
145 qDebug( "** Using Candidate2" );
146#endif
147 } else {
148 if ( (prevMatchChar->character() >> 16) != Qt::Key_Backspace &&
149 (prevMatchChar->character() >> 16) < QIMPenChar::ModeBase )
150 eraseLast = TRUE;
151 prevMatchChar = candidate1.penChar;
152 prevMatchError = candidate1.error;
153#ifdef DEBUG_QIMPEN
154 qDebug( "** Using Candidate1, with erase" );
155#endif
156 }
157 } else if ( candidate1.penChar ) {
158 if ( strokes.count() != 1 )
159 eraseLast = TRUE;
160 else
161 multiCharSet = charSet;
162 prevMatchChar = candidate1.penChar;
163 prevMatchError = candidate1.error;
164#ifdef DEBUG_QIMPEN
165 qDebug( "** Using Candidate1" );
166#endif
167 } else if ( candidate2.penChar ) {
168 int i = strokes.count()-1;
169 while ( i-- ) {
170 strokes.removeFirst();
171 emit removeStroke();
172 }
173 prevMatchChar = candidate2.penChar;
174 prevMatchError = candidate2.error;
175 multiCharSet = charSet;
176 ml = ml2;
177#ifdef DEBUG_QIMPEN
178 qDebug( "** Using Candidate2" );
179#endif
180 } else {
181 if ( !ml.count() ) {
182#ifdef DEBUG_QIMPEN
183 qDebug( "** Failed" );
184#endif
185 canErase = FALSE;
186 } else {
187#ifdef DEBUG_QIMPEN
188 qDebug( "Need more strokes" );
189#endif
190 if ( strokes.count() == 1 )
191 canErase = FALSE;
192 multiCharSet = charSet;
193 }
194 output = FALSE;
195 emit noMatch();
196 }
197
198 if ( eraseLast && canErase ) {
199#ifdef DEBUG_QIMPEN
200 qDebug( "deleting last" );
201#endif
202 emit erase();
203 wordChars.removeLast();
204 wordEntered.truncate( wordEntered.length() - 1 );
205 }
206
207 if ( output ) {
208 emit matchedCharacters( ml );
209 uint code = prevMatchChar->character() >> 16;
210 if ( code < QIMPenChar::ModeBase ) {
211 updateWordMatch( ml );
212 emit keypress( prevMatchChar->character() );
213 }
214 canErase = TRUE;
215 }
216
217 if ( strokes.count() )
218 multiTimer->start( multiTimeout, TRUE );
219}
220
221void QIMPenMatch::updateWordMatch( QIMPenCharMatchList &ml )
222{
223 if ( !ml.count() || !doWordMatching )
224 return;
225 int ch = ml.first().penChar->character();
226 QChar qch( ch );
227 int code = ch >> 16;
228 if ( qch.isPunct() || qch.isSpace() ||
229 code == Qt::Key_Enter || code == Qt::Key_Return ||
230 code == Qt::Key_Tab || code == Qt::Key_Escape ) {
231 //qDebug( "Word Matching: Clearing word" );
232 wordChars.clear();
233 wordMatches.clear();
234 wordEntered = QString();
235 } else if ( code == Qt::Key_Backspace ) {
236 //qDebug( "Word Matching: Handle backspace" );
237 wordChars.removeLast();
238 wordEntered.truncate( wordEntered.length() - 1 );
239 matchWords();
240 } else {
241 QIMPenChar *matchCh;
242
243 wordChars.append( new QIMPenCharMatchList() );
244 wordEntered += ml.first().penChar->character();
245
246 QIMPenCharMatchList::Iterator it;
247 for ( it = ml.begin(); it != ml.end(); ++it ) {
248 matchCh = (*it).penChar;
249
250 if ( matchCh->penStrokes().count() == strokes.count() ) {
251 QChar ch(matchCh->character());
252 if ( !ch.isPunct() && !ch.isSpace() ) {
253 wordChars.last()->append( QIMPenCharMatch( (*it) ) );
254 }
255 }
256 }
257 matchWords();
258 }
259 if ( !wordMatches.count() || wordMatches.getFirst()->word != wordEntered )
260 wordMatches.prepend( new MatchWord( wordEntered, 0 ) );
261 emit matchedWords( wordMatches );
262}
263
264void QIMPenMatch::matchWords()
265{
266 if ( wordEntered.length() > 0 ) {
267 // more leaniency if we don't have many matches
268 if ( badMatches < 200 )
269 errorThreshold += (200 - badMatches) * 100;
270 } else
271 errorThreshold = ERROR_THRESHOLD;
272 wordMatches.clear();
273 goodMatches = 0;
274 badMatches = 0;
275 if ( wordChars.count() > 0 ) {
276 maxGuess = (int)wordChars.count() * 2;
277 if ( maxGuess < 3 )
278 maxGuess = 3;
279 QString str;
280 scanDict( Global::fixedDawg().root(), 0, str, 0 );
281/*
282 QListIterator<MatchWord> it( wordMatches);
283 for ( ; it.current(); ++it ) {
284 qDebug( QString("Match word: %1").arg(it.current()->word) );
285 }
286*/
287 }
288 //qDebug( "Possibles: Good %d, total %d", goodMatches, wordMatches.count() );
289 wordMatches.sort();
290}
291
292void QIMPenMatch::scanDict( const QDawg::Node* n, int ipos, const QString& str, int error )
293{
294 if ( !n )
295 return;
296 if ( error / (ipos+1) > errorThreshold )
297 return;
298
299 while (n) {
300 if ( goodMatches > 20 )
301 break;
302 if ( ipos < (int)wordChars.count() ) {
303 int i;
304 QChar testCh = QChar(n->letter());
305 QIMPenCharMatchList::Iterator it;
306 for ( i = 0, it = wordChars.at(ipos)->begin();
307 it != wordChars.at(ipos)->end() && i < 8; ++it, i++ ) {
308 QChar ch( (*it).penChar->character() );
309 if ( ch == testCh || ( !ipos && ch.lower() == testCh.lower() ) ) {
310 int newerr = error + (*it).error;
311 if ( testCh.category() == QChar::Letter_Uppercase )
312 ch = testCh;
313 QString newstr( str + ch );
314 if ( n->isWord() && ipos == (int)wordChars.count() - 1 ) {
315 wordMatches.append( new MatchWord( newstr, newerr ) );
316 goodMatches++;
317 }
318 scanDict( n->jump(), ipos+1, newstr, newerr );
319 }
320 }
321 } else if ( badMatches < 200 && ipos < maxGuess ) {
322 int d = ipos - wordChars.count();
323 int newerr = error + ERROR_THRESHOLD + LOOKAHEAD_ERROR*d;
324 QString newstr( str + n->letter() );
325 if ( n->isWord() ) {
326 wordMatches.append( new MatchWord( newstr, newerr ) );
327 badMatches++;
328 }
329 scanDict( n->jump(), ipos+1, newstr, newerr );
330 }
331 n = n->next();
332 }
333}
334
335void QIMPenMatch::backspace()
336{
337 wordChars.removeLast();
338 wordEntered.truncate( wordEntered.length() - 1 );
339 matchWords();
340 if ( !wordMatches.count() || wordMatches.getFirst()->word != wordEntered )
341 wordMatches.prepend( new MatchWord( wordEntered, 0 ) );
342 emit matchedWords( wordMatches );
343 if ( wordEntered.length() )
344 canErase = TRUE;
345}
346
347void QIMPenMatch::endMulti()
348{
349 int i = strokes.count();
350 while ( i-- )
351 emit removeStroke();
352 strokes.clear();
353 multiCharSet = 0;
354}
355
356void QIMPenMatch::resetState()
357{
358 if ( !wordEntered.isEmpty() ) {
359 wordChars.clear();
360 wordMatches.clear();
361 wordEntered = QString();
362 emit matchedWords( wordMatches );
363 canErase = FALSE;
364 }
365}
diff --git a/inputmethods/handwriting/qimpenmatch.h b/inputmethods/handwriting/qimpenmatch.h
new file mode 100644
index 0000000..d4a730e
--- a/dev/null
+++ b/inputmethods/handwriting/qimpenmatch.h
@@ -0,0 +1,107 @@
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#ifndef _QIMPENMATCH_H_
22#define _QIMPENMATCH_H_
23
24#include "qimpenchar.h"
25
26#include <qpe/qdawg.h>
27
28#include <qlist.h>
29
30class QTimer;
31class QIMPenWidget;
32class QIMPenSetup;
33class QIMPenWordPick;
34
35class QIMPenMatch : public QObject
36{
37 Q_OBJECT
38public:
39 QIMPenMatch( QObject *parent=0, const char *name=0 );
40 virtual ~QIMPenMatch();
41
42 void resetState();
43 void backspace();
44 void setMultiStrokeTimeout( int t ) { multiTimeout = t; }
45
46 const QString &word() const { return wordEntered; }
47
48 void setWordMatchingEnabled( bool e ) { doWordMatching = e; }
49 bool isWordMatchingEnabled() const { return doWordMatching; }
50
51 struct MatchWord {
52 MatchWord( const QString &w, int e ) { word = w; error = e; }
53 QString word;
54 int error;
55 };
56
57 class MatchWordList : public QList<MatchWord>
58 {
59 public:
60 int compareItems( QCollection::Item item1, QCollection::Item item2 ) {
61 MatchWord *m1 = (MatchWord *)item1;
62 MatchWord *m2 = (MatchWord *)item2;
63 return m1->error - m2->error;
64 }
65 };
66
67public slots:
68 void setCharSet( QIMPenCharSet * );
69 void beginStroke();
70 void strokeEntered( QIMPenStroke *st );
71
72signals:
73 void erase();
74 void noMatch();
75 void removeStroke();
76 void keypress( uint ch );
77 void matchedCharacters( const QIMPenCharMatchList & );
78 void matchedWords( const QIMPenMatch::MatchWordList & );
79
80protected slots:
81 void processMatches( QIMPenCharMatchList &ml );
82 void endMulti();
83
84protected:
85 void updateWordMatch( QIMPenCharMatchList &ml );
86 void matchWords();
87 void scanDict( const QDawg::Node* n, int ipos, const QString& str, int error );
88
89 QList<QIMPenStroke> strokes;
90 QIMPenChar *prevMatchChar;
91 int prevMatchError;
92 QIMPenCharSet *charSet;
93 QIMPenCharSet *multiCharSet;
94 QList<QIMPenCharMatchList> wordChars;
95 MatchWordList wordMatches;
96 QString wordEntered;
97 bool doWordMatching;
98 bool canErase;
99 int errorThreshold;
100 int goodMatches;
101 int badMatches;
102 int maxGuess;
103 QTimer *multiTimer;
104 int multiTimeout;
105};
106
107#endif // _QIMPENINPUT_H_
diff --git a/inputmethods/handwriting/qimpenprefbase.ui b/inputmethods/handwriting/qimpenprefbase.ui
new file mode 100644
index 0000000..1639d1a
--- a/dev/null
+++ b/inputmethods/handwriting/qimpenprefbase.ui
@@ -0,0 +1,185 @@
1<!DOCTYPE UI><UI>
2<class>QIMPenPrefBase</class>
3<widget>
4 <class>QWidget</class>
5 <property stdset="1">
6 <name>name</name>
7 <cstring>QIMPenPrefBase</cstring>
8 </property>
9 <property stdset="1">
10 <name>geometry</name>
11 <rect>
12 <x>0</x>
13 <y>0</y>
14 <width>247</width>
15 <height>280</height>
16 </rect>
17 </property>
18 <property stdset="1">
19 <name>caption</name>
20 <string>Form1</string>
21 </property>
22 <vbox>
23 <property stdset="1">
24 <name>margin</name>
25 <number>11</number>
26 </property>
27 <property stdset="1">
28 <name>spacing</name>
29 <number>6</number>
30 </property>
31 <widget>
32 <class>QLabel</class>
33 <property stdset="1">
34 <name>name</name>
35 <cstring>TextLabel1</cstring>
36 </property>
37 <property stdset="1">
38 <name>text</name>
39 <string>Multi-stroke character timeout:</string>
40 </property>
41 </widget>
42 <widget>
43 <class>QLayoutWidget</class>
44 <property stdset="1">
45 <name>name</name>
46 <cstring>Layout2</cstring>
47 </property>
48 <hbox>
49 <property stdset="1">
50 <name>margin</name>
51 <number>0</number>
52 </property>
53 <property stdset="1">
54 <name>spacing</name>
55 <number>6</number>
56 </property>
57 <widget>
58 <class>QSlider</class>
59 <property stdset="1">
60 <name>name</name>
61 <cstring>multiStrokeSlider</cstring>
62 </property>
63 <property stdset="1">
64 <name>minValue</name>
65 <number>250</number>
66 </property>
67 <property stdset="1">
68 <name>maxValue</name>
69 <number>1000</number>
70 </property>
71 <property stdset="1">
72 <name>lineStep</name>
73 <number>10</number>
74 </property>
75 <property stdset="1">
76 <name>pageStep</name>
77 <number>50</number>
78 </property>
79 <property stdset="1">
80 <name>value</name>
81 <number>500</number>
82 </property>
83 <property stdset="1">
84 <name>orientation</name>
85 <enum>Horizontal</enum>
86 </property>
87 <property stdset="1">
88 <name>tickmarks</name>
89 <enum>Right</enum>
90 </property>
91 </widget>
92 <widget>
93 <class>QLabel</class>
94 <property stdset="1">
95 <name>name</name>
96 <cstring>multiStrokeLabel</cstring>
97 </property>
98 <property stdset="1">
99 <name>minimumSize</name>
100 <size>
101 <width>45</width>
102 <height>0</height>
103 </size>
104 </property>
105 <property stdset="1">
106 <name>text</name>
107 <string>ms</string>
108 </property>
109 <property stdset="1">
110 <name>alignment</name>
111 <set>AlignVCenter|AlignRight</set>
112 </property>
113 <property>
114 <name>hAlign</name>
115 </property>
116 </widget>
117 </hbox>
118 </widget>
119 <widget>
120 <class>QButtonGroup</class>
121 <property stdset="1">
122 <name>name</name>
123 <cstring>inputStyle</cstring>
124 </property>
125 <property stdset="1">
126 <name>title</name>
127 <string>Input areas displayed</string>
128 </property>
129 <vbox>
130 <property stdset="1">
131 <name>margin</name>
132 <number>11</number>
133 </property>
134 <property stdset="1">
135 <name>spacing</name>
136 <number>6</number>
137 </property>
138 <widget>
139 <class>QRadioButton</class>
140 <property stdset="1">
141 <name>name</name>
142 <cstring>bothCasesRadio</cstring>
143 </property>
144 <property stdset="1">
145 <name>text</name>
146 <string>Upper and lower case areas</string>
147 </property>
148 </widget>
149 <widget>
150 <class>QRadioButton</class>
151 <property stdset="1">
152 <name>name</name>
153 <cstring>toggleCaseRadio</cstring>
154 </property>
155 <property stdset="1">
156 <name>text</name>
157 <string>Lower case (toggle Upper case)</string>
158 </property>
159 </widget>
160 </vbox>
161 </widget>
162 <spacer>
163 <property>
164 <name>name</name>
165 <cstring>Spacer2</cstring>
166 </property>
167 <property stdset="1">
168 <name>orientation</name>
169 <enum>Vertical</enum>
170 </property>
171 <property stdset="1">
172 <name>sizeType</name>
173 <enum>Expanding</enum>
174 </property>
175 <property>
176 <name>sizeHint</name>
177 <size>
178 <width>20</width>
179 <height>20</height>
180 </size>
181 </property>
182 </spacer>
183 </vbox>
184</widget>
185</UI>
diff --git a/inputmethods/handwriting/qimpenprofile.cpp b/inputmethods/handwriting/qimpenprofile.cpp
new file mode 100644
index 0000000..4b5bb83
--- a/dev/null
+++ b/inputmethods/handwriting/qimpenprofile.cpp
@@ -0,0 +1,245 @@
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 "qimpencombining.h"
22#include "qimpenprofile.h"
23
24#include <qpe/qpeapplication.h>
25#include <qpe/config.h>
26#include <qpe/global.h>
27
28
29QIMPenProfile::QIMPenProfile( const QString &fn )
30 : filename( fn )
31{
32 sets.setAutoDelete( true );
33
34 Config config( filename, Config::File );
35 config.setGroup( "Handwriting" );
36
37 pname = config.readEntry( "Name" );
38 pdesc = config.readEntry( "Description" );
39
40 tstyle = config.readBoolEntry( "CanSelectStyle", false );
41
42 wordMatch = config.readBoolEntry( "MatchWords", true );
43
44 config.setGroup( "Settings" );
45
46 pstyle = BothCases;
47 QString s = config.readEntry( "Style", "BothCases" );
48 if ( s == "ToggleCases" )
49 pstyle = ToggleCases;
50
51 msTimeout = config.readNumEntry( "MultiTimeout", 500 );
52
53 // Read user configuration
54 Config usrConfig( userConfig() );
55 usrConfig.setGroup( "Settings" );
56 msTimeout = usrConfig.readNumEntry( "MultiTimeout", msTimeout );
57
58 if ( tstyle && usrConfig.hasKey( "Style" ) ) {
59 pstyle = BothCases;
60 QString s = usrConfig.readEntry( "Style", "BothCases" );
61 if ( s == "ToggleCases" )
62 pstyle = ToggleCases;
63 }
64}
65
66void QIMPenProfile::setStyle( Style s )
67{
68 if ( tstyle && s != pstyle ) {
69 pstyle = s;
70 Config config( userConfig() );
71 config.setGroup( "Settings" );
72 QString s = pstyle == ToggleCases ? "ToggleCases" : "BothCases";
73 config.writeEntry( "Style", s );
74 }
75}
76
77void QIMPenProfile::setMultiStrokeTimeout( int t )
78{
79 if ( t != msTimeout ) {
80 msTimeout = t;
81 Config config( userConfig() );
82 config.setGroup( "Settings" );
83 config.writeEntry( "MultiTimeout", msTimeout );
84 }
85}
86
87QString QIMPenProfile::userConfig()
88{
89 QString un = filename;
90 int pos = un.findRev( '/' );
91 if ( pos >= 0 )
92 un = un.mid( pos + 1 );
93 pos = un.find( '.' );
94 if ( pos > 0 )
95 un.truncate( pos );
96
97 un = "handwriting-" + un;
98
99 return un;
100}
101
102void QIMPenProfile::loadData()
103{
104 Config config( filename, Config::File );
105 config.setGroup( "CharSets" );
106
107 QString baseDir = QPEApplication::qpeDir();
108 baseDir += "/etc/";
109 // accents
110 QIMPenCombining *combining = 0;
111 QString s = config.readEntry( "Combining" );
112 if ( !s.isEmpty() ) {
113 combining = new QIMPenCombining( baseDir + "qimpen/" + s );
114 if ( combining->isEmpty() ) {
115 delete combining;
116 combining = 0;
117 }
118 }
119 // uppercase latin1
120 QIMPenCharSet *cs = 0;
121 s = config.readEntry( "Uppercase" );
122 if ( !s.isEmpty() ) {
123 cs = new QIMPenCharSet( baseDir + "qimpen/" + s );
124 cs->load( Global::applicationFileName("qimpen",s), QIMPenCharSet::User );
125 if ( !cs->isEmpty() ) {
126 if ( combining )
127 combining->addCombined( cs );
128 sets.append( cs );
129 } else {
130 delete cs;
131 }
132 }
133 // lowercase latin1
134 s = config.readEntry( "Lowercase" );
135 if ( !s.isEmpty() ) {
136 cs = new QIMPenCharSet( baseDir + "qimpen/" + s );
137 cs->load( Global::applicationFileName("qimpen",s), QIMPenCharSet::User );
138 if ( !cs->isEmpty() ) {
139 if ( combining )
140 combining->addCombined( cs );
141 sets.append( cs );
142 } else {
143 delete cs;
144 }
145 }
146 // numeric (may comtain punctuation and symbols)
147 s = config.readEntry( "Numeric" );
148 if ( !s.isEmpty() ) {
149 cs = new QIMPenCharSet( baseDir + "qimpen/" + s );
150 cs->load( Global::applicationFileName("qimpen",s), QIMPenCharSet::User );
151 if ( !cs->isEmpty() ) {
152 sets.append( cs );
153 } else {
154 delete cs;
155 }
156 }
157 // punctuation
158 s = config.readEntry( "Punctuation" );
159 if ( !s.isEmpty() ) {
160 cs = new QIMPenCharSet( baseDir + "qimpen/" + s );
161 cs->load( Global::applicationFileName("qimpen",s), QIMPenCharSet::User );
162 if ( !cs->isEmpty() ) {
163 sets.append( cs );
164 } else {
165 delete cs;
166 }
167 }
168 // symbol
169 s = config.readEntry( "Symbol" );
170 if ( !s.isEmpty() ) {
171 cs = new QIMPenCharSet( baseDir + "qimpen/" + s );
172 cs->load( Global::applicationFileName("qimpen",s), QIMPenCharSet::User );
173 if ( !cs->isEmpty() ) {
174 sets.append( cs );
175 } else {
176 delete cs;
177 }
178 }
179 // shortcut
180 s = config.readEntry( "Shortcut" );
181 if ( !s.isEmpty() ) {
182 cs = new QIMPenCharSet( baseDir + "qimpen/" + s );
183 cs->load( Global::applicationFileName("qimpen",s), QIMPenCharSet::User );
184 if ( !cs->isEmpty() ) {
185 sets.append( cs );
186 } else {
187 delete cs;
188 }
189 }
190
191 if ( combining )
192 delete combining;
193}
194
195QIMPenCharSet *QIMPenProfile::uppercase()
196{
197 return find( QIMPenCharSet::Upper );
198}
199
200QIMPenCharSet *QIMPenProfile::lowercase()
201{
202 return find( QIMPenCharSet::Lower );
203}
204
205QIMPenCharSet *QIMPenProfile::numeric()
206{
207 return find( QIMPenCharSet::Numeric );
208}
209
210QIMPenCharSet *QIMPenProfile::punctuation()
211{
212 return find( QIMPenCharSet::Punctuation );
213}
214
215QIMPenCharSet *QIMPenProfile::symbol()
216{
217 return find( QIMPenCharSet::Symbol );
218}
219
220QIMPenCharSet *QIMPenProfile::shortcut()
221{
222 return find( QIMPenCharSet::Shortcut );
223}
224
225QIMPenCharSetList &QIMPenProfile::charSets()
226{
227 if ( sets.isEmpty() )
228 loadData();
229 return sets;
230}
231
232QIMPenCharSet *QIMPenProfile::find( QIMPenCharSet::Type t )
233{
234 if ( sets.isEmpty() )
235 loadData();
236 QIMPenCharSetIterator it( sets );
237 for ( ; it.current(); ++it ) {
238 if ( it.current()->type() == t )
239 return it.current();
240 }
241
242 return 0;
243}
244
245
diff --git a/inputmethods/handwriting/qimpenprofile.h b/inputmethods/handwriting/qimpenprofile.h
new file mode 100644
index 0000000..4ce4367
--- a/dev/null
+++ b/inputmethods/handwriting/qimpenprofile.h
@@ -0,0 +1,70 @@
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#ifndef QIMPENPROFILE_H_
22#define QIMPENPROFILE_H_
23
24#include "qimpenchar.h"
25
26class QIMPenProfile
27{
28public:
29 QIMPenProfile( const QString &fn );
30
31 const QString &name() const { return pname; }
32 const QString &description() const { return pdesc; }
33
34 enum Style { ToggleCases, BothCases };
35 Style style() const { return pstyle; }
36 void setStyle( Style s );
37
38 bool canSelectStyle() const { return tstyle; }
39
40 int multiStrokeTimeout() const { return msTimeout; }
41 void setMultiStrokeTimeout( int t );
42
43 bool matchWords() const { return wordMatch; }
44
45 QIMPenCharSet *uppercase();
46 QIMPenCharSet *lowercase();
47 QIMPenCharSet *numeric();
48 QIMPenCharSet *punctuation();
49 QIMPenCharSet *symbol();
50 QIMPenCharSet *shortcut();
51 QIMPenCharSet *find( QIMPenCharSet::Type t );
52
53 QIMPenCharSetList &charSets();
54
55private:
56 QString userConfig();
57 void loadData();
58
59private:
60 QIMPenCharSetList sets;
61 QString filename;
62 QString pname;
63 QString pdesc;
64 Style pstyle;
65 bool tstyle;
66 int msTimeout;
67 bool wordMatch;
68};
69
70#endif
diff --git a/inputmethods/handwriting/qimpensetup.cpp b/inputmethods/handwriting/qimpensetup.cpp
new file mode 100644
index 0000000..a6ae3a8
--- a/dev/null
+++ b/inputmethods/handwriting/qimpensetup.cpp
@@ -0,0 +1,656 @@
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 "qimpenwidget.h"
22#include "qimpenprefbase.h"
23#include "qimpensetup.h"
24
25#include <qpe/qpeapplication.h>
26#include <qpe/config.h>
27
28#include <qcombobox.h>
29#include <qlistbox.h>
30#include <qlabel.h>
31#include <qpushbutton.h>
32#include <qlayout.h>
33#include <qpixmap.h>
34#include <qbuttongroup.h>
35#include <qslider.h>
36#include <qtabwidget.h>
37#include <qdir.h>
38#include <qmessagebox.h>
39
40
41/* XPM */
42static const char * const left_xpm[] = {
43"16 16 2 1",
44 " c None",
45 ".c #000000",
46" ",
47" ",
48" ",
49" . ",
50" .. ",
51" ... ",
52" .... ",
53" ..... ",
54" ...... ",
55" ..... ",
56" .... ",
57" ... ",
58" .. ",
59" . ",
60" ",
61" "};
62
63
64/* XPM */
65static const char * const right_xpm[] = {
66"16 16 2 1",
67 " c None",
68 ".c #000000",
69" ",
70" ",
71" ",
72" . ",
73" .. ",
74" ... ",
75" .... ",
76" ..... ",
77" ...... ",
78" ..... ",
79" .... ",
80" ... ",
81" .. ",
82" . ",
83" ",
84" "};
85
86
87
88QIMPenSetup::QIMPenSetup( QIMPenProfile *p, QWidget *parent,
89 const char *name, bool modal, int WFlags )
90 : QDialog( parent, name, modal, WFlags ), profileCombo(0), profile(p)
91{
92 setCaption( tr("Setup Handwriting Input") );
93
94 QVBoxLayout *vb = new QVBoxLayout( this );
95
96#if 0
97 profileList.setAutoDelete( true );
98 QHBoxLayout *hb = new QHBoxLayout( vb );
99 hb->setMargin( 6 );
100 QLabel *l = new QLabel( tr("Character Profile:"), this );
101 hb->addWidget( l );
102 profileCombo = new QComboBox( this );
103 connect( profileCombo, SIGNAL(activated(const QString &)),
104 this, SLOT(selectProfile(const QString &)) );
105 hb->addWidget( profileCombo );
106 loadProfiles();
107#else
108 profileList.append( profile );
109#endif
110
111 QTabWidget *tw = new QTabWidget( this );
112 vb->addWidget( tw );
113
114 pref = new QIMPenPrefBase( this );
115 tw->addTab( pref, tr("Preferences") );
116
117 pref->inputStyle->setExclusive( TRUE );
118
119 style = profile->style() == QIMPenProfile::ToggleCases ? 1 : 0;
120 pref->inputStyle->setButton( style );
121 connect( pref->inputStyle, SIGNAL(clicked(int)),
122 this, SLOT(styleClicked(int)) );
123 pref->inputStyle->setEnabled( profile->canSelectStyle() );
124
125 multiTimeout = profile->multiStrokeTimeout();
126 pref->multiStrokeSlider->setValue( multiTimeout );
127 multiTimeoutChanged( multiTimeout );
128 connect( pref->multiStrokeSlider, SIGNAL(valueChanged(int)),
129 this, SLOT(multiTimeoutChanged(int)) );
130
131 edit = new QIMPenEdit( p, tw );
132 tw->addTab( edit, tr("Customize") );
133}
134
135void QIMPenSetup::loadProfiles()
136{
137 QString path = QPEApplication::qpeDir() + "etc/qimpen";
138 QDir dir( path, "*.conf" );
139 QStringList list = dir.entryList();
140 QStringList::Iterator it;
141 for ( it = list.begin(); it != list.end(); ++it ) {
142 QIMPenProfile *p = new QIMPenProfile( path + "/" + *it );
143 profileList.append( p );
144 profileCombo->insertItem( p->name() );
145 if ( p->name() == profile->name() ) {
146 profileCombo->setCurrentItem( profileCombo->count()-1 );
147 profile = p;
148 }
149 }
150}
151
152void QIMPenSetup::styleClicked( int id )
153{
154 style = id;
155}
156
157void QIMPenSetup::multiTimeoutChanged( int v )
158{
159 multiTimeout = v;
160 pref->multiStrokeLabel->setText( tr("%1 ms").arg(v) );
161}
162
163void QIMPenSetup::selectProfile( const QString &p )
164{
165 if ( p == profile->name() )
166 return;
167
168 profile->setStyle( style ? QIMPenProfile::ToggleCases : QIMPenProfile::BothCases );
169 profile->setMultiStrokeTimeout( multiTimeout );
170
171 for ( int i = 0; i < (int)profileList.count(); i++ ) {
172 if ( profileList.at(i)->name() == p ) {
173 profile = profileList.at(i);
174 style = profile->style() == QIMPenProfile::ToggleCases ? 1 : 0;
175 pref->inputStyle->setButton( style );
176 pref->inputStyle->setEnabled( profile->canSelectStyle() );
177 multiTimeout = profile->multiStrokeTimeout();
178 pref->multiStrokeSlider->setValue( multiTimeout );
179 multiTimeoutChanged( multiTimeout );
180 edit->setProfile( profile );
181 break;
182 }
183 }
184}
185
186void QIMPenSetup::accept()
187{
188 profile->setStyle( style ? QIMPenProfile::ToggleCases : QIMPenProfile::BothCases );
189 profile->setMultiStrokeTimeout( multiTimeout );
190 // Save current profile
191 if ( profileCombo ) {
192 Config config( "handwriting" );
193 config.setGroup( "Settings" );
194 config.writeEntry( "Profile", profileCombo->currentText() );
195 }
196 // Save charsets
197 bool ok = TRUE;
198 for ( int i = 0; i < (int)profileList.count(); i++ ) {
199 QIMPenProfile *prof = profileList.at(i);
200 QIMPenCharSetIterator it(prof->charSets());
201 for ( ; it.current(); ++it ) {
202 if ( !(it.current()->save( QIMPenCharSet::User )) ) {
203 ok = FALSE;
204 break;
205 }
206 }
207 }
208 if ( !ok ) {
209 if ( QMessageBox::critical( 0, tr( "Out of space" ),
210 tr("Unable to save information.\n"
211 "Free up some space\n"
212 "and try again.\n"
213 "\nQuit anyway?"),
214 QMessageBox::Yes|QMessageBox::Escape,
215 QMessageBox::No|QMessageBox::Default )
216 != QMessageBox::No ) {
217 QDialog::accept();
218 }
219 } else {
220 QDialog::accept();
221 }
222}
223
224//---------------------------------------------------------------------------
225
226QIMPenInputCharDlg::QIMPenInputCharDlg( QWidget *parent, const char *name,
227 bool modal, int WFlags)
228 : QDialog( parent, name, modal, WFlags )
229{
230 setCaption( tr("Enter new character") );
231 uni = 0;
232
233 QVBoxLayout *vb = new QVBoxLayout( this, 10 );
234
235 QHBoxLayout *hb = new QHBoxLayout();
236 vb->addLayout( hb );
237
238 QLabel *label = new QLabel( "Character:", this );
239 hb->addWidget( label );
240
241 QComboBox *cb = new QComboBox( TRUE, this );
242 connect( cb, SIGNAL(activated(int)), SLOT(setSpecial(int)) );
243 connect( cb, SIGNAL(textChanged(const QString &)),
244 SLOT(setCharacter(const QString &)) );
245 addSpecial( cb );
246 cb->setEditText( "" );
247 hb->addWidget( cb );
248
249 hb = new QHBoxLayout();
250 vb->addLayout( hb );
251
252 QPushButton *pb = new QPushButton( "OK", this );
253 connect( pb, SIGNAL(clicked()), SLOT(accept()));
254 hb->addWidget( pb );
255 pb = new QPushButton( "Cancel", this );
256 connect( pb, SIGNAL(clicked()), SLOT(reject()));
257 hb->addWidget( pb );
258
259 cb->setFocus();
260}
261
262void QIMPenInputCharDlg::addSpecial( QComboBox *cb )
263{
264 int i = 0;
265 while ( qimpen_specialKeys[i].code != Key_unknown ) {
266 cb->insertItem( qimpen_specialKeys[i].name );
267 i++;
268 }
269}
270
271void QIMPenInputCharDlg::setSpecial( int sp )
272{
273 uni = qimpen_specialKeys[sp].code << 16;
274}
275
276void QIMPenInputCharDlg::setCharacter( const QString &string )
277{
278 uni = string[0].unicode();
279}
280
281//---------------------------------------------------------------------------
282
283class CharListItem : public QListBoxText
284{
285public:
286 CharListItem( const QString &text, uint c )
287 : QListBoxText( text )
288 {
289 _code = c;
290 }
291
292 uint code() const { return _code; }
293
294protected:
295 uint _code;
296};
297
298/*!
299 \class QIMPenEdit qimpensetup.h
300
301 Class to allow users to input totally useless character definitions
302 which could match any number of the default set.
303*/
304
305QIMPenEdit::QIMPenEdit( QIMPenProfile *p, QWidget *parent,
306 const char *name )
307 : QWidget( parent, name ), profile(p)
308{
309 currentChar = 0;
310 currentCode = 0;
311 inputChar = new QIMPenChar();
312
313 QVBoxLayout *tvb = new QVBoxLayout( this, 5 );
314
315 QGridLayout *gl = new QGridLayout( tvb, 4, 2 );
316 gl->setRowStretch( 1, 1 );
317 gl->addRowSpacing( 2, 35 );
318 gl->addRowSpacing( 3, 35 );
319
320 charSetCombo = new QComboBox( this );
321 gl->addMultiCellWidget( charSetCombo, 0, 0, 0, 1 );
322 connect( charSetCombo, SIGNAL(activated(int)), SLOT(selectCharSet(int)));
323 QIMPenCharSetIterator it( profile->charSets() );
324 for ( ; it.current(); ++it ) {
325 charSetCombo->insertItem( it.current()->description() );
326 }
327
328 charList = new QListBox( this );
329 charList->setMinimumHeight( charList->sizeHint().height() );
330 connect( charList, SIGNAL(highlighted(int)), SLOT(selectChar(int)) );
331 gl->addWidget( charList, 1, 0 );
332
333 pw = new QIMPenWidget( this );
334 pw->setFixedHeight( 75 );
335 gl->addMultiCellWidget( pw, 2, 3, 0, 0 );
336 connect( pw, SIGNAL(stroke(QIMPenStroke *)),
337 SLOT(newStroke(QIMPenStroke *)) );
338
339 QVBoxLayout *vb = new QVBoxLayout();
340 gl->addLayout( vb, 1, 1 );
341 newBtn = new QPushButton( tr("New..."), this );
342 connect( newBtn, SIGNAL(clicked()), SLOT(addNewChar()) );
343 vb->addWidget( newBtn );
344
345 addBtn = new QPushButton( tr("Add"), this );
346 connect( addBtn, SIGNAL(clicked()), SLOT(addChar()) );
347 vb->addWidget( addBtn );
348
349 removeBtn = new QPushButton( tr("Remove"), this );
350 connect( removeBtn, SIGNAL(clicked()), SLOT(removeChar()) );
351 vb->addWidget( removeBtn );
352
353 QPushButton *pb = new QPushButton( tr("Default"), this );
354 connect( pb, SIGNAL(clicked()), SLOT(defaultChars()) );
355 vb->addWidget( pb );
356
357 QHBoxLayout *hb = new QHBoxLayout();
358 gl->addLayout( hb, 2, 1 );
359 prevBtn = new QPushButton( this );
360 prevBtn->setPixmap( QPixmap( (const char **)left_xpm ) );
361 connect( prevBtn, SIGNAL(clicked()), SLOT(prevChar()));
362 hb->addWidget( prevBtn );
363
364 nextBtn = new QPushButton( this );
365 nextBtn->setPixmap( QPixmap( (const char **)right_xpm ) );
366 connect( nextBtn, SIGNAL(clicked()), SLOT(nextChar()));
367 hb->addWidget( nextBtn );
368
369 pb = new QPushButton( tr("Clear"), this );
370 connect( pb, SIGNAL(clicked()), SLOT(clearChar()) );
371 gl->addWidget( pb, 3, 1 );
372
373 //--
374#if !defined(Q_WS_QWS)
375 hb = new QHBoxLayout( tvb );
376 pb = new QPushButton( "OK", this );
377 connect( pb, SIGNAL(clicked()), SLOT(accept()) );
378 hb->addWidget( pb );
379
380 pb = new QPushButton( "Cancel", this );
381 connect( pb, SIGNAL(clicked()), SLOT(reject()) );
382 hb->addWidget( pb );
383#endif
384 selectCharSet( 0 );
385 charList->setFocus();
386
387 resize( minimumSize() );
388 enableButtons();
389}
390
391void QIMPenEdit::setProfile( QIMPenProfile *p )
392{
393 profile = p;
394 charSetCombo->clear();
395 QIMPenCharSetIterator it( profile->charSets() );
396 for ( ; it.current(); ++it ) {
397 charSetCombo->insertItem( it.current()->description() );
398 }
399 selectCharSet( 0 );
400 charList->setFocus();
401 enableButtons();
402}
403
404void QIMPenEdit::selectCharSet( QIMPenCharSet *c )
405{
406 int i = 0;
407 QIMPenCharSetIterator it( profile->charSets() );
408 for ( ; it.current(); ++it, i++ ) {
409 if ( it.current() == c ) {
410 charSetCombo->setCurrentItem( i );
411 selectCharSet( i );
412 }
413 }
414}
415
416
417/*!
418 Fill the character list box with the characters. Duplicates are not
419 inserted.
420*/
421void QIMPenEdit::fillCharList()
422{
423 charList->clear();
424 QIMPenCharIterator it( currentSet->characters() );
425 CharListItem *li = 0;
426 for ( ; it.current(); ++it ) {
427 uint ch = it.current()->character();
428 QString n = it.current()->name();
429 if ( !n.isEmpty() )
430 li = new CharListItem( n, ch );
431 if ( li ) {
432 CharListItem *i = (CharListItem *)charList->findItem( li->text() );
433 if ( !i || i->code() != ch ) {
434 charList->insertItem( li );
435 } else {
436 delete li;
437 li = 0;
438 }
439 }
440 }
441 currentChar = 0;
442}
443
444void QIMPenEdit::enableButtons()
445{
446 bool add = !inputChar->isEmpty();
447 newBtn->setEnabled( add );
448 addBtn->setEnabled( add );
449 removeBtn->setEnabled( currentChar );
450}
451
452/*!
453 Find the previous character with the same code as the current one.
454 returns 0 if there is no previous character.
455*/
456QIMPenChar *QIMPenEdit::findPrev()
457{
458 if ( !currentChar )
459 return 0;
460 QIMPenCharIterator it( currentSet->characters() );
461 bool found = FALSE;
462 for ( it.toLast(); it.current(); --it ) {
463 if ( !found && it.current() == currentChar )
464 found = TRUE;
465 else if ( found && it.current()->character() == currentCode &&
466 !it.current()->testFlag( QIMPenChar::Deleted ) ) {
467 return it.current();
468 }
469 }
470
471 return 0;
472}
473
474/*!
475 Find the next character with the same code as the current one.
476 returns 0 if there is no next character.
477*/
478QIMPenChar *QIMPenEdit::findNext()
479{
480 if ( !currentChar )
481 return 0;
482 QIMPenCharIterator it( currentSet->characters() );
483 bool found = FALSE;
484 for ( ; it.current(); ++it ) {
485 if ( !found && it.current() == currentChar )
486 found = TRUE;
487 else if ( found && it.current()->character() == currentCode &&
488 !it.current()->testFlag( QIMPenChar::Deleted ) ) {
489 return it.current();
490 }
491 }
492
493 return 0;
494}
495
496void QIMPenEdit::setCurrentChar( QIMPenChar *pc )
497{
498 currentChar = pc;
499 pw->showCharacter( currentChar );
500 if ( currentChar ) {
501 prevBtn->setEnabled( findPrev() != 0 );
502 nextBtn->setEnabled( findNext() != 0 );
503 }
504}
505
506void QIMPenEdit::prevChar()
507{
508 QIMPenChar *pc = findPrev();
509 if ( pc )
510 setCurrentChar( pc );
511}
512
513void QIMPenEdit::nextChar()
514{
515 QIMPenChar *pc = findNext();
516 if ( pc )
517 setCurrentChar( pc );
518}
519
520void QIMPenEdit::clearChar()
521{
522 inputChar->clear();
523 pw->clear();
524 enableButtons();
525}
526
527void QIMPenEdit::selectChar( int i )
528{
529 currentChar = 0;
530 currentCode = ((CharListItem *)charList->item(i))->code();
531 QIMPenCharIterator it(currentSet->characters() );
532 for ( ; it.current(); ++it ) {
533 if ( it.current()->character() == currentCode &&
534 !it.current()->testFlag( QIMPenChar::Deleted ) ) {
535 setCurrentChar( it.current() );
536 break;
537 }
538 }
539 if ( !it.current() )
540 setCurrentChar( 0 );
541 inputChar->clear();
542}
543
544void QIMPenEdit::selectCharSet( int i )
545{
546 if ( currentSet )
547 pw->removeCharSet( 0 );
548 currentSet = profile->charSets().at( i );
549 fillCharList();
550 pw->insertCharSet( currentSet );
551 inputChar->clear();
552 if ( charList->count() ) {
553 charList->setSelected( 0, TRUE );
554 selectChar(0);
555 }
556}
557
558void QIMPenEdit::addChar()
559{
560 if ( !inputChar->isEmpty() ) {
561 QIMPenChar *pc = new QIMPenChar( *inputChar );
562 pc->setCharacter( currentCode );
563
564 // User characters override all matching system characters.
565 // Copy and mark deleted identical system characters.
566 QIMPenCharIterator it(currentSet->characters() );
567 QIMPenChar *sc = 0;
568 while ( (sc = it.current()) != 0 ) {
569 ++it;
570 if ( sc->character() == currentCode &&
571 sc->testFlag( QIMPenChar::System ) &&
572 !sc->testFlag( QIMPenChar::Deleted ) ) {
573 QIMPenChar *cc = new QIMPenChar( *sc );
574 cc->clearFlag( QIMPenChar::System );
575 currentSet->addChar( cc );
576 sc->setFlag( QIMPenChar::Deleted );
577 }
578 }
579
580 currentSet->addChar( pc );
581 setCurrentChar( pc );
582 inputChar->clear();
583 }
584}
585
586void QIMPenEdit::addNewChar()
587{
588 if ( !inputChar->isEmpty() ) {
589 QIMPenInputCharDlg dlg( 0, 0, TRUE );
590 if ( dlg.exec() ) {
591 currentCode = dlg.unicode();
592 addChar();
593 fillCharList();
594 for ( unsigned int i = 0; i < charList->count(); i++ ) {
595 CharListItem *li = (CharListItem *)charList->item(i);
596 if ( li->code() == dlg.unicode() ) {
597 charList->setSelected( i, TRUE );
598 break;
599 }
600 }
601 }
602 }
603}
604
605void QIMPenEdit::removeChar()
606{
607 if ( currentChar ) {
608 QIMPenChar *pc = findPrev();
609 if ( !pc ) pc = findNext();
610 if ( currentChar->testFlag( QIMPenChar::System ) )
611 currentChar->setFlag( QIMPenChar::Deleted );
612 else
613 currentSet->removeChar( currentChar );
614 setCurrentChar( pc );
615 }
616}
617
618void QIMPenEdit::defaultChars()
619{
620 if ( currentCode ) {
621 currentChar = 0;
622 bool haveSystem = FALSE;
623 QIMPenCharIterator it(currentSet->characters() );
624 for ( ; it.current(); ++it ) {
625 if ( it.current()->character() == currentCode &&
626 it.current()->testFlag( QIMPenChar::System ) ) {
627 haveSystem = TRUE;
628 break;
629 }
630 }
631 if ( haveSystem ) {
632 it.toFirst();
633 while ( it.current() ) {
634 QIMPenChar *pc = it.current();
635 ++it;
636 if ( pc->character() == currentCode ) {
637 if ( pc->testFlag( QIMPenChar::System ) ) {
638 pc->clearFlag( QIMPenChar::Deleted );
639 if ( !currentChar )
640 currentChar = pc;
641 } else {
642 currentSet->removeChar( pc );
643 }
644 }
645 }
646 setCurrentChar( currentChar );
647 }
648 }
649}
650
651void QIMPenEdit::newStroke( QIMPenStroke *st )
652{
653 inputChar->addStroke( st );
654 enableButtons();
655}
656
diff --git a/inputmethods/handwriting/qimpensetup.h b/inputmethods/handwriting/qimpensetup.h
new file mode 100644
index 0000000..5d3064b
--- a/dev/null
+++ b/inputmethods/handwriting/qimpensetup.h
@@ -0,0 +1,124 @@
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 <qdialog.h>
22#include <qlist.h>
23#include "qimpenprofile.h"
24
25class QListBox;
26class QPushButton;
27class QComboBox;
28class QIMPenWidget;
29class QIMPenEdit;
30class QIMPenPrefBase;
31
32class QIMPenSetup : public QDialog
33{
34 Q_OBJECT
35public:
36 QIMPenSetup( QIMPenProfile *p, QWidget *parent=0,
37 const char *name=0, bool modal=FALSE, int WFlags=0 );
38
39 QIMPenEdit *editor() { return edit; }
40
41protected:
42 void loadProfiles();
43 virtual void accept();
44
45private slots:
46 void styleClicked( int );
47 void multiTimeoutChanged( int );
48 void selectProfile( const QString &p );
49
50private:
51 QComboBox *profileCombo;
52 QIMPenEdit *edit;
53 QIMPenPrefBase *pref;
54 int style;
55 int multiTimeout;
56 QIMPenProfile *profile;
57 QList<QIMPenProfile> profileList;
58};
59
60class QIMPenInputCharDlg : public QDialog
61{
62 Q_OBJECT
63public:
64 QIMPenInputCharDlg( QWidget *parent = 0, const char *name = 0,
65 bool modal = FALSE, int WFlags = 0 );
66
67 unsigned int unicode() const { return uni; }
68
69protected:
70 void addSpecial( QComboBox *cb );
71
72protected slots:
73 void setSpecial( int sp );
74 void setCharacter( const QString &string );
75
76protected:
77 uint uni;
78};
79
80class QIMPenEdit : public QWidget
81{
82 Q_OBJECT
83public:
84 QIMPenEdit( QIMPenProfile *p, QWidget *parent=0,
85 const char *name=0 );
86
87 void setProfile( QIMPenProfile *p );
88 void selectCharSet( QIMPenCharSet *c );
89
90protected:
91 void fillCharList();
92 void enableButtons();
93 QIMPenChar *findPrev();
94 QIMPenChar *findNext();
95 void setCurrentChar( QIMPenChar * );
96
97protected slots:
98 void prevChar();
99 void nextChar();
100 void clearChar();
101 void selectChar( int );
102 void selectCharSet( int );
103 void addChar();
104 void addNewChar();
105 void removeChar();
106 void defaultChars();
107 void newStroke( QIMPenStroke * );
108
109protected:
110 QIMPenWidget *pw;
111 QComboBox *charSetCombo;
112 QListBox *charList;
113 QPushButton *newBtn;
114 QPushButton *addBtn;
115 QPushButton *removeBtn;
116 QPushButton *prevBtn;
117 QPushButton *nextBtn;
118 uint currentCode;
119 QIMPenChar *currentChar;
120 QIMPenChar *inputChar;
121 QIMPenCharSet *currentSet;
122 QIMPenProfile *profile;
123};
124
diff --git a/inputmethods/handwriting/qimpenstroke.cpp b/inputmethods/handwriting/qimpenstroke.cpp
new file mode 100644
index 0000000..3567d6d
--- a/dev/null
+++ b/inputmethods/handwriting/qimpenstroke.cpp
@@ -0,0 +1,646 @@
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 <qfile.h>
22#include <qtl.h>
23#include <math.h>
24#include <limits.h>
25#include <qdatastream.h>
26#include "qimpenstroke.h"
27
28#define QIMPEN_CORRELATION_POINTS 25
29//#define DEBUG_QIMPEN
30
31/*!
32 \class QIMPenStroke qimpenstroke.h
33
34 Handles a single stroke. Can calculate closeness of match to
35 another stroke.
36*/
37
38QIMPenStroke::QIMPenStroke()
39{
40}
41
42QIMPenStroke::QIMPenStroke( const QIMPenStroke &st )
43{
44 startPoint = st.startPoint;
45 lastPoint = st.lastPoint;
46 links = st.links.copy();
47}
48
49QIMPenStroke &QIMPenStroke::operator=( const QIMPenStroke &s )
50{
51 clear();
52 //qDebug( "copy strokes %d", s.links.count() );
53 startPoint = s.startPoint;
54 lastPoint = s.lastPoint;
55 links = s.links.copy();
56
57 return *this;
58}
59
60void QIMPenStroke::clear()
61{
62 startPoint = QPoint(0,0);
63 lastPoint = QPoint( 0, 0 );
64 links.resize( 0 );
65 tsig.resize( 0 );
66 dsig.resize( 0 );
67 asig.resize( 0 );
68}
69
70/*!
71 Begin inputting a new stroke.
72*/
73void QIMPenStroke::beginInput( QPoint p )
74{
75 clear();
76 startPoint = p;
77 bounding = QRect();
78 internalAddPoint( p );
79}
80
81/*!
82 Add a point to the stroke's shape.
83 Returns TRUE if the point was successfully added.
84*/
85bool QIMPenStroke::addPoint( QPoint p )
86{
87 if ( links.count() > 500 ) // sanity check (that the user is sane).
88 return FALSE;
89
90 int dx = p.x() - lastPoint.x();
91 int dy = p.y() - lastPoint.y();
92 if ( QABS( dx ) > 1 || QABS( dy ) > 1 ) {
93 // The point is not adjacent to the previous point, so we fill
94 // in with a straight line. Some kind of non-linear
95 // interpolation might be better.
96 int x = lastPoint.x();
97 int y = lastPoint.y();
98 int ix = 1;
99 int iy = 1;
100 if ( dx < 0 ) {
101 ix = -1;
102 dx = -dx;
103 }
104 if ( dy < 0 ) {
105 iy = -1;
106 dy = -dy;
107 }
108 int d = 0;
109 if ( dx < dy ) {
110 d = dx;
111 do {
112 y += iy;
113 d += dx;
114 if ( d > dy ) {
115 x += ix;
116 d -= dy;
117 }
118 internalAddPoint( QPoint( x, y ) );
119 } while ( y != p.y() );
120 } else {
121 d = dy;
122 do {
123 x += ix;
124 d += dy;
125 if ( d > dx ) {
126 y += iy;
127 d -= dx;
128 }
129 internalAddPoint( QPoint( x, y ) );
130 } while ( x != p.x() );
131 }
132 } else {
133 internalAddPoint( p );
134 }
135
136 return TRUE;
137}
138
139/*!
140 Finish inputting a stroke.
141*/
142void QIMPenStroke::endInput()
143{
144 if ( links.count() < 3 ) {
145 QIMPenGlyphLink gl;
146 links.resize(1);
147 gl.dx = 1;
148 gl.dy = 0;
149 links[0] = gl;
150 }
151
152 //qDebug("Points: %d", links.count() );
153}
154
155/*!
156 Return an indicator of the closeness of this stroke to \a pen.
157 Lower value is better.
158*/
159unsigned int QIMPenStroke::match( QIMPenStroke *pen )
160{
161 double lratio;
162
163 if ( links.count() > pen->links.count() )
164 lratio = (links.count()+2) / (pen->links.count()+2);
165 else
166 lratio = (pen->links.count()+2) / (links.count()+2);
167
168 lratio -= 1.0;
169
170 if ( lratio > 2.0 ) {
171#ifdef DEBUG_QIMPEN
172 qDebug( "stroke length too different" );
173#endif
174 return 400000;
175 }
176
177 createSignatures();
178 pen->createSignatures();
179
180 // Starting point offset
181 int vdiff = QABS(startPoint.y() - pen->startPoint.y());
182
183 // Insanely offset?
184 if ( vdiff > 18 ) {
185 return 400000;
186 }
187
188 vdiff -= 4;
189 if ( vdiff < 0 )
190 vdiff = 0;
191
192 // Ending point offset
193 int evdiff = QABS(lastPoint.y() - pen->lastPoint.y());
194 // Insanely offset?
195 if ( evdiff > 20 ) {
196 return 400000;
197 }
198
199 evdiff -= 5;
200 if ( evdiff < 0 )
201 evdiff = 0;
202
203 // do a correlation with the three available signatures.
204 int err1 = INT_MAX;
205 int err2 = INT_MAX;
206 int err3 = INT_MAX;
207
208 // base has extra points at the start and end to enable
209 // correlation of a sliding window with the pen supplied.
210 QArray<int> base = createBase( tsig, 2 );
211 for ( int i = 0; i < 4; i++ ) {
212 int e = calcError( base, pen->tsig, i, TRUE );
213 if ( e < err1 )
214 err1 = e;
215 }
216 if ( err1 > 40 ) { // no need for more matching
217#ifdef DEBUG_QIMPEN
218 qDebug( "tsig too great: %d", err1 );
219#endif
220 return 400000;
221 }
222
223 // maybe a sliding window is worthwhile for these too.
224 err2 = calcError( dsig, pen->dsig, 0, FALSE );
225 if ( err2 > 100 ) {
226#ifdef DEBUG_QIMPEN
227 qDebug( "dsig too great: %d", err2 );
228#endif
229 return 400000;
230 }
231
232 err3 = calcError( asig, pen->asig, 0, TRUE );
233 if ( err3 > 60 ) {
234#ifdef DEBUG_QIMPEN
235 qDebug( "asig too great: %d", err3 );
236#endif
237 return 400000;
238 }
239
240 // Some magic numbers here - the addition reduces the weighting of
241 // the error and compensates for the different error scales. I
242 // consider the tangent signature to be the best indicator, so it
243 // has the most weight. This ain't rocket science.
244 // Basically, these numbers are the tuning factors.
245 unsigned int err = (err1+1) * ( err2 + 60 ) * ( err3 + 20 ) +
246 vdiff * 1000 + evdiff * 500 +
247 (unsigned int)(lratio * 5000.0);
248
249#ifdef DEBUG_QIMPEN
250 qDebug( "err %d ( %d, %d, %d, %d)", err, err1, err2, err3, vdiff );
251#endif
252
253 return err;
254}
255
256/*!
257 Return the bounding rect of this stroke.
258*/
259QRect QIMPenStroke::boundingRect()
260{
261 if ( !bounding.isValid() ) {
262 int x = startPoint.x();
263 int y = startPoint.y();
264 bounding = QRect( x, y, 1, 1 );
265
266 for ( unsigned i = 0; i < links.count(); i++ ) {
267 x += links[i].dx;
268 y += links[i].dy;
269 if ( x < bounding.left() )
270 bounding.setLeft( x );
271 if ( x > bounding.right() )
272 bounding.setRight( x );
273 if ( y < bounding.top() )
274 bounding.setTop( y );
275 if ( y > bounding.bottom() )
276 bounding.setBottom( y );
277 }
278 }
279
280 return bounding;
281}
282
283
284/*!
285 Perform a correlation of the supplied arrays. \a base should have
286 win.count() + 2 * off points to enable sliding \a win over the
287 \a base data. If \a t is TRUE, the comparison takes into account
288 the circular nature of the angular data.
289 Returns the best (lowest error) match.
290*/
291
292int QIMPenStroke::calcError( const QArray<int> &base,
293 const QArray<int> &win, int off, bool t )
294{
295 int err = 0;
296
297 for ( unsigned i = 0; i < win.count(); i++ ) {
298 int d = QABS( base[i+off] - win[i] );
299 if ( t && d > 128 )
300 d -= 256;
301 err += QABS( d );
302 }
303
304 err /= win.count();
305
306 return err;
307}
308
309/*!
310 Creates signatures used in matching if not already created.
311*/
312void QIMPenStroke::createSignatures()
313{
314 if ( tsig.isEmpty() )
315 createTanSignature();
316 if ( asig.isEmpty() )
317 createAngleSignature();
318 if ( dsig.isEmpty() )
319 createDistSignature();
320}
321
322/*!
323 Create a signature of the tangents to the user's stroke.
324*/
325void QIMPenStroke::createTanSignature()
326{
327 int dist = 5; // number of points to include in calculation
328 if ( (int)links.count() <= dist ) {
329 tsig.resize(1);
330 int dx = 0;
331 int dy = 0;
332 for ( unsigned j = 0; j < links.count(); j++ ) {
333 dx += links[j].dx;
334 dy += links[j].dy;
335 }
336 tsig[0] = arcTan( dy, dx );
337 } else {
338 tsig.resize( (links.count()-dist+1) / 2 );
339 int idx = 0;
340 for ( unsigned i = 0; i < links.count() - dist; i += 2 ) {
341 int dx = 0;
342 int dy = 0;
343 for ( int j = 0; j < dist; j++ ) {
344 dx += links[i+j].dx;
345 dy += links[i+j].dy;
346 }
347 tsig[idx++] = arcTan( dy, dx );
348 }
349 }
350
351 tsig = scale( tsig, QIMPEN_CORRELATION_POINTS, TRUE );
352// smooth(tsig);
353}
354
355/*!
356 Create a signature of the change in angle.
357*/
358void QIMPenStroke::createAngleSignature()
359{
360 QPoint c = calcCenter();
361
362 int dist = 3; // number of points to include in calculation
363 if ( (int)links.count() <= dist ) {
364 asig.resize(1);
365 asig[0] = 1;
366 } else {
367 asig.resize( links.count() );
368 QPoint current(0, 0);
369 int idx = 0;
370 for ( unsigned i = 0; i < links.count(); i++ ) {
371 int dx = c.x() - current.x();
372 int dy = c.y() - current.y();
373 int md = QMAX( QABS(dx), QABS(dy) );
374 if ( md > 5 ) {
375 dx = dx * 5 / md;
376 dy = dy * 5 / md;
377 }
378 asig[idx++] = arcTan( dy, dx );
379 current += QPoint( links[i].dx, links[i].dy );
380 }
381 }
382
383 asig = scale( asig, QIMPEN_CORRELATION_POINTS, TRUE );
384
385/*
386 if ( tsig.isEmpty() )
387 createTanSignature();
388
389 if ( tsig.count() < 5 ) {
390 asig.resize( 1 );
391 asig[0] = 0;
392 } else {
393 asig.resize( tsig.count() - 5 );
394
395 for ( unsigned i = 0; i < asig.count(); i++ ) {
396 asig[i] = QABS(tsig[i] - tsig[i+5]);
397 }
398 }
399*/
400}
401
402/*!
403 Create a signature of the distance from the char's center of gravity
404 to its points.
405*/
406void QIMPenStroke::createDistSignature()
407{
408 dsig.resize( (links.count()+1)/2 );
409 QPoint c = calcCenter();
410 QPoint pt( 0, 0 );
411
412 int minval = INT_MAX;
413 int maxval = 0;
414 int idx = 0;
415 for ( unsigned i = 0; i < links.count(); i += 2 ) {
416 int dx = c.x() - pt.x();
417 int dy = c.y() - pt.y();
418 if ( dx == 0 && dy == 0 )
419 dsig[idx] = 0;
420 else
421 dsig[idx] = dx*dx + dy*dy;
422
423 if ( dsig[idx] > maxval )
424 maxval = dsig[idx];
425 if ( dsig[idx] < minval )
426 minval = dsig[idx];
427 pt.rx() += links[i].dx;
428 pt.ry() += links[i].dy;
429 idx++;
430 }
431
432 // normalise 0-255
433 int div = maxval - minval;
434 if ( div == 0 ) div = 1;
435 for ( unsigned i = 0; i < dsig.count(); i++ ) {
436 dsig[i] = (dsig[i] - minval ) * 255 / div;
437 }
438
439 dsig = scale( dsig, QIMPEN_CORRELATION_POINTS );
440}
441
442
443/*!
444 Scale the points in a array to \a count points.
445 This is braindead at the moment (no smooth scaling) and fixing this is
446 probably one of the simpler ways to improve performance.
447*/
448QArray<int> QIMPenStroke::scale( const QArray<int> &s, unsigned count, bool t )
449{
450 QArray<int> d(count);
451
452 unsigned si = 0;
453 if ( s.count() > count ) {
454 unsigned next = 0;
455 for ( unsigned i = 0; i < count; i++ ) {
456 next = (i+1) * s.count() / count;
457 int maxval = 0;
458 if ( t ) {
459 for ( unsigned j = si; j < next; j++ ) {
460 maxval = s[j] > maxval ? s[j] : maxval;
461 }
462 }
463 int sum = 0;
464 for ( unsigned j = si; j < next; j++ ) {
465 if ( t && maxval - s[j] > 128 )
466 sum += 256;
467 sum += s[j];
468 }
469 d[i] = sum / (next-si);
470 if ( t && d[i] > 256 )
471 d[i] %= 256;
472 si = next;
473 }
474 } else {
475 for ( unsigned i = 0; i < count; i++ ) {
476 si = i * s.count() / count;
477 d[i] = s[si];
478 }
479 }
480
481 return d;
482}
483
484/*!
485 Add another point to the stroke's shape.
486*/
487void QIMPenStroke::internalAddPoint( QPoint p )
488{
489 if ( p == lastPoint )
490 return;
491
492 if ( !lastPoint.isNull() ) {
493 QIMPenGlyphLink gl;
494 gl.dx = p.x() - lastPoint.x();
495 gl.dy = p.y() - lastPoint.y();
496 links.resize( links.size() + 1 ); //### resize by 1 is bad
497 links[links.size() - 1] = gl;
498 }
499
500 lastPoint = p;
501 bounding = QRect();
502}
503
504/*!
505 Calculate the center of gravity of the stroke.
506*/
507QPoint QIMPenStroke::calcCenter()
508{
509 QPoint pt( 0, 0 );
510 int ax = 0;
511 int ay = 0;
512
513 for ( unsigned i = 0; i < links.count(); i++ ) {
514 pt.rx() += links[i].dx;
515 pt.ry() += links[i].dy;
516 ax += pt.x();
517 ay += pt.y();
518 }
519
520 ax /= (int)links.count();
521 ay /= (int)links.count();
522
523 return QPoint( ax, ay );
524}
525
526/*!
527 Calculate the arctan of the lengths supplied.
528 The angle returned is in the range 0-255.
529 \a dy and \a dx MUST be in the range 0-5 - I dont even check :-P
530*/
531int QIMPenStroke::arcTan( int dy, int dx )
532{
533 if ( dx == 0 ) {
534 if ( dy >= 0 )
535 return 64;
536 else
537 return 192;
538 }
539
540 if ( dy == 0 ) {
541 if ( dx >= 0 )
542 return 0;
543 else
544 return 128;
545 }
546
547 static int table[5][5] = {
548 { 32, 19, 13, 10, 8 },
549 { 45, 32, 24, 19, 16 },
550 { 51, 40, 32, 26, 22 },
551 { 54, 45, 37, 32, 27 },
552 { 56, 49, 42, 37, 32 } };
553
554 if ( dy > 0 ) {
555 if ( dx > 0 )
556 return table[dy-1][dx-1];
557 else
558 return 128 - table[dy-1][QABS(dx)-1];
559 } else {
560 if ( dx > 0 )
561 return 256 - table[QABS(dy)-1][dx-1];
562 else
563 return 128 + table[QABS(dy)-1][QABS(dx)-1];
564 }
565
566 return 0;
567}
568
569
570/*!
571 Silly name. Create an array that has \a e points extra at the start and
572 end to enable a sliding correlation to be performed.
573*/
574QArray<int> QIMPenStroke::createBase( const QArray<int> a, int e )
575{
576 QArray<int> ra( a.count() + 2*e );
577
578 for ( int i = 0; i < e; i++ ) {
579 ra[i] = a[e - i - 1];
580 ra[a.count() + i] = a[a.count() - i - 1];
581 }
582 for ( unsigned i = 0; i < a.count(); i++ ) {
583 ra[i+e] = a[i];
584 }
585
586 return ra;
587}
588
589
590/*!
591 Smooth the points in an array. Probably a bad idea.
592*/
593void QIMPenStroke::smooth( QArray<int> &sig)
594{
595 QArray<int> nsig = sig.copy();
596
597 int a;
598 for ( unsigned i = 1; i < sig.count()-2; i++ ) {
599 a = 0;
600 for ( int j = -1; j <= 1; j++ ) {
601 a += sig[ i + j ];
602 }
603 nsig[i] = a / 3;
604 }
605
606 sig = nsig;
607}
608
609/*!
610 Write the character's data to the stream.
611*/
612QDataStream &operator<< (QDataStream &s, const QIMPenStroke &ws)
613{
614 s << ws.startPoint;
615 s << ws.links.count();
616 for ( unsigned i = 0; i < ws.links.count(); i++ ) {
617 s << (Q_INT8)ws.links[i].dx;
618 s << (Q_INT8)ws.links[i].dy;
619 }
620
621 return s;
622}
623
624/*!
625 Read the character's data from the stream.
626*/
627QDataStream &operator>> (QDataStream &s, QIMPenStroke &ws)
628{
629 Q_INT8 i8;
630 s >> ws.startPoint;
631 ws.lastPoint = ws.startPoint;
632 unsigned size;
633 s >> size;
634 ws.links.resize( size );
635 for ( unsigned i = 0; i < size; i++ ) {
636 s >> i8;
637 ws.links[i].dx = i8;
638 s >> i8;
639 ws.links[i].dy = i8;
640 ws.lastPoint += QPoint( ws.links[i].dx, ws.links[i].dy );
641 }
642
643 return s;
644}
645
646
diff --git a/inputmethods/handwriting/qimpenstroke.h b/inputmethods/handwriting/qimpenstroke.h
new file mode 100644
index 0000000..bd5ee0e
--- a/dev/null
+++ b/inputmethods/handwriting/qimpenstroke.h
@@ -0,0 +1,91 @@
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#ifndef QIMPENSTROKE_H_
22#define QIMPENSTROKE_H_
23
24#include <qobject.h>
25#include <qarray.h>
26#include <qlist.h>
27
28struct Q_PACKED QIMPenGlyphLink
29{
30 signed char dx;
31 signed char dy;
32};
33
34class QIMPenStroke
35{
36public:
37 QIMPenStroke();
38 QIMPenStroke( const QIMPenStroke & );
39
40 void clear();
41 bool isEmpty() const { return links.isEmpty(); }
42 unsigned int length() const { return links.count(); }
43 unsigned int match( QIMPenStroke *st );
44 const QArray<QIMPenGlyphLink> &chain() const { return links; }
45 QPoint startingPoint() const { return startPoint; }
46 void setStartingPoint( const QPoint &p ) { startPoint = p; }
47 QRect boundingRect();
48
49 QIMPenStroke &operator=( const QIMPenStroke &s );
50
51 void beginInput( QPoint p );
52 bool addPoint( QPoint p );
53 void endInput();
54
55 QArray<int> sig() { createTanSignature(); return tsig; } // for debugging
56
57protected:
58 void createSignatures();
59 void createTanSignature();
60 void createAngleSignature();
61 void createDistSignature();
62 int calcError( const QArray<int> &base, const QArray<int> &win,
63 int off, bool t );
64 QArray<int> scale( const QArray<int> &s, unsigned count, bool t = FALSE );
65 void internalAddPoint( QPoint p );
66 QPoint calcCenter();
67 int arcTan( int dy, int dx );
68 QArray<int> createBase( const QArray<int> a, int e );
69 void smooth( QArray<int> &);
70
71protected:
72 QPoint startPoint;
73 QPoint lastPoint;
74 QArray<QIMPenGlyphLink> links;
75 QArray<int> tsig;
76 QArray<int> asig;
77 QArray<int> dsig;
78 QRect bounding;
79
80 friend QDataStream &operator<< (QDataStream &, const QIMPenStroke &);
81 friend QDataStream &operator>> (QDataStream &, QIMPenStroke &);
82};
83
84typedef QList<QIMPenStroke> QIMPenStrokeList;
85typedef QListIterator<QIMPenStroke> QIMPenStrokeIterator;
86
87QDataStream & operator<< (QDataStream & s, const QIMPenStroke &ws);
88QDataStream & operator>> (QDataStream & s, const QIMPenStroke &ws);
89
90#endif
91
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
diff --git a/inputmethods/handwriting/qimpenwidget.h b/inputmethods/handwriting/qimpenwidget.h
new file mode 100644
index 0000000..98d7f5c
--- a/dev/null
+++ b/inputmethods/handwriting/qimpenwidget.h
@@ -0,0 +1,88 @@
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 <qwidget.h>
22#include <qlist.h>
23#include "qimpenchar.h"
24
25class QIMPenWidget : public QWidget
26{
27 Q_OBJECT
28public:
29 QIMPenWidget( QWidget *parent );
30
31 void clear();
32 void greyStroke();
33 void setReadOnly( bool r ) { readOnly = r; }
34
35 void insertCharSet( QIMPenCharSet *cs, int stretch=1, int pos=-1 );
36 void removeCharSet( int );
37 void changeCharSet( QIMPenCharSet *cs, int pos );
38 void clearCharSets();
39 void showCharacter( QIMPenChar *, int speed = 10 );
40 virtual QSize sizeHint();
41
42public slots:
43 void removeStroke();
44
45signals:
46 void changeCharSet( QIMPenCharSet *cs );
47 void changeCharSet( int );
48 void beginStroke();
49 void stroke( QIMPenStroke *ch );
50
51protected slots:
52 void timeout();
53
54protected:
55 enum Mode { Waiting, Input, Output };
56 bool selectSet( QPoint );
57 virtual void mousePressEvent( QMouseEvent *e );
58 virtual void mouseReleaseEvent( QMouseEvent *e );
59 virtual void mouseMoveEvent( QMouseEvent *e );
60 virtual void paintEvent( QPaintEvent *e );
61 virtual void resizeEvent( QResizeEvent *e );
62
63 struct CharSetEntry {
64 QIMPenCharSet *cs;
65 int stretch;
66 };
67 typedef QList<CharSetEntry> CharSetEntryList;
68 typedef QListIterator<CharSetEntry> CharSetEntryIterator;
69
70protected:
71 Mode mode;
72 bool autoHide;
73 bool readOnly;
74 QPoint lastPoint;
75 unsigned pointIndex;
76 int strokeIndex;
77 int currCharSet;
78 QTimer *timer;
79 QColor strokeColor;
80 QRect dirtyRect;
81 QIMPenChar *outputChar;
82 QIMPenStroke *outputStroke;
83 QIMPenStroke *inputStroke;
84 QIMPenStrokeList strokes;
85 CharSetEntryList charSets;
86 int totalStretch;
87};
88
diff --git a/inputmethods/handwriting/qimpenwordpick.cpp b/inputmethods/handwriting/qimpenwordpick.cpp
new file mode 100644
index 0000000..8ee103d
--- a/dev/null
+++ b/inputmethods/handwriting/qimpenwordpick.cpp
@@ -0,0 +1,113 @@
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 <qpainter.h>
22#include "qimpenwordpick.h"
23
24QIMPenWordPick::QIMPenWordPick( QWidget *parent, const char *name, WFlags f )
25 : QFrame( parent, name, f )
26{
27 clickWord = -1;
28 setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) );
29}
30
31void QIMPenWordPick::clear()
32{
33 words.clear();
34 repaint();
35}
36
37QSize QIMPenWordPick::sizeHint() const
38{
39 return QSize( -1, font().pixelSize()+2 );
40}
41
42void QIMPenWordPick::setWords( const QIMPenMatch::MatchWordList &w )
43{
44 words.clear();
45 QListIterator<QIMPenMatch::MatchWord> it( w );
46 for ( ; it.current(); ++it ) {
47 words.append( it.current()->word );
48 }
49 repaint();
50}
51
52int QIMPenWordPick::onWord( QPoint p )
53{
54 int x = 2;
55 int idx = 0;
56 for ( QStringList::Iterator it = words.begin(); it != words.end(); ++it ) {
57 QString word = *it;
58 int w = fontMetrics().width( word );
59 if ( x + w > width() )
60 break;
61 if ( p.x() > x-2 && p.x() < x + w + 2 )
62 return idx;
63 x += w + 5;
64 if ( !idx )
65 x += 3;
66 idx++;
67 }
68
69 return -1;
70}
71
72void QIMPenWordPick::paintEvent( QPaintEvent * )
73{
74 QPainter p(this);
75 int x = 2;
76 int h = p.fontMetrics().ascent() + 1;
77 int idx = 0;
78 for ( QStringList::Iterator it = words.begin(); it != words.end(); ++it ) {
79 QString word = *it;
80 int w = p.fontMetrics().width( word );
81 if ( x + w > width() )
82 break;
83 if ( idx == clickWord ) {
84 p.fillRect( x, 0, w, height(), black );
85 p.setPen( white );
86 } else {
87 p.setPen( colorGroup().text() );
88 }
89 p.drawText( x, h, word );
90 x += w + 5;
91 if ( !idx )
92 x += 3;
93 idx++;
94 }
95}
96
97void QIMPenWordPick::mousePressEvent( QMouseEvent *e )
98{
99 clickWord = onWord( e->pos() );
100 repaint();
101}
102
103void QIMPenWordPick::mouseReleaseEvent( QMouseEvent *e )
104{
105 int wordIdx = onWord( e->pos() );
106 if ( wordIdx >= 0 && wordIdx == clickWord ) {
107 //qDebug( "Clicked %s", words[wordIdx].latin1() );
108 emit wordClicked( words[wordIdx] );
109 }
110 clickWord = -1;
111 repaint();
112}
113
diff --git a/inputmethods/handwriting/qimpenwordpick.h b/inputmethods/handwriting/qimpenwordpick.h
new file mode 100644
index 0000000..376288e
--- a/dev/null
+++ b/inputmethods/handwriting/qimpenwordpick.h
@@ -0,0 +1,49 @@
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 <qframe.h>
22#include "qimpenmatch.h"
23
24class QIMPenWordPick : public QFrame
25{
26 Q_OBJECT
27public:
28 QIMPenWordPick( QWidget *parent = 0, const char *name = 0, WFlags f = 0 );
29
30 void clear();
31 virtual QSize sizeHint() const;
32
33public slots:
34 void setWords( const QIMPenMatch::MatchWordList &w );
35
36signals:
37 void wordClicked( const QString & );
38
39protected:
40 int onWord( QPoint p );
41 virtual void paintEvent( QPaintEvent * );
42 virtual void mousePressEvent( QMouseEvent * );
43 virtual void mouseReleaseEvent( QMouseEvent * );
44
45private:
46 QStringList words;
47 int clickWord;
48};
49
diff --git a/inputmethods/handwriting/qpe-handwriting.control b/inputmethods/handwriting/qpe-handwriting.control
new file mode 100644
index 0000000..f1648c6
--- a/dev/null
+++ b/inputmethods/handwriting/qpe-handwriting.control
@@ -0,0 +1,9 @@
1Files: plugins/inputmethods/libqhandwriting.so*
2Priority: optional
3Section: qpe/inputmethods
4Maintainer: Martin Jones <mjones@trolltech.com>
5Architecture: arm
6Version: $QPE_VERSION-3
7Depends: qpe-base ($QPE_VERSION)
8Description: Handwriting input method
9 Handwriting recognition input method for the Qtopia environment.
diff --git a/inputmethods/handwriting/qpe-handwriting.postinst b/inputmethods/handwriting/qpe-handwriting.postinst
new file mode 100755
index 0000000..c254b01
--- a/dev/null
+++ b/inputmethods/handwriting/qpe-handwriting.postinst
@@ -0,0 +1,2 @@
1#!/bin/sh
2/opt/QtPalmtop/bin/qcop QPE/TaskBar "reloadInputMethods()"
diff --git a/inputmethods/handwriting/qpe-handwriting.postrm b/inputmethods/handwriting/qpe-handwriting.postrm
new file mode 100755
index 0000000..c254b01
--- a/dev/null
+++ b/inputmethods/handwriting/qpe-handwriting.postrm
@@ -0,0 +1,2 @@
1#!/bin/sh
2/opt/QtPalmtop/bin/qcop QPE/TaskBar "reloadInputMethods()"