summaryrefslogtreecommitdiff
path: root/inputmethods/handwriting
Side-by-side diff
Diffstat (limited to 'inputmethods/handwriting') (more/less context) (show 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 @@
+moc_*
+*.moc
+Makefile
+qimpenprefbase.h
+qimpenprefbase.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 @@
+#############################################################################
+
+####### Compiler, tools and options
+
+CXX = $(SYSCONF_CXX) $(QT_CXX_MT)
+CXXFLAGS= $(SYSCONF_CXXFLAGS_QT) $(SYSCONF_CXXFLAGS) $(SYSCONF_CXXFLAGS_LIB)
+CC = $(SYSCONF_CC) $(QT_C_MT)
+CFLAGS = $(SYSCONF_CFLAGS) $(SYSCONF_CFLAGS_LIB)
+INCPATH = -I$(QPEDIR)/include
+LFLAGS = $(SYSCONF_LFLAGS_QT) $(SYSCONF_RPATH_QT) $(SYSCONF_LFLAGS) $(QT_LFLAGS_MT)
+LIBS = $(SUBLIBS) -lqpe $(SYSCONF_LIBS_QT) $(SYSCONF_LIBS_QTAPP)
+MOC = $(SYSCONF_MOC)
+UIC = $(SYSCONF_UIC)
+
+####### Target
+
+DESTDIR = ../../plugins/inputmethods/
+VER_MAJ = 1
+VER_MIN = 0
+VER_PATCH = 0
+TARGET = qhandwriting
+TARGET1 = lib$(TARGET).so.$(VER_MAJ)
+
+####### Files
+
+HEADERS = qimpenchar.h \
+ qimpenprofile.h \
+ qimpencombining.h \
+ qimpenhelp.h \
+ qimpeninput.h \
+ qimpenmatch.h \
+ qimpensetup.h \
+ qimpenstroke.h \
+ qimpenwidget.h \
+ qimpenwordpick.h \
+ handwritingimpl.h
+SOURCES = qimpenchar.cpp \
+ qimpenprofile.cpp \
+ qimpencombining.cpp \
+ qimpenhelp.cpp \
+ qimpeninput.cpp \
+ qimpenmatch.cpp \
+ qimpensetup.cpp \
+ qimpenstroke.cpp \
+ qimpenwidget.cpp \
+ qimpenwordpick.cpp \
+ handwritingimpl.cpp
+OBJECTS = qimpenchar.o \
+ qimpenprofile.o \
+ qimpencombining.o \
+ qimpenhelp.o \
+ qimpeninput.o \
+ qimpenmatch.o \
+ qimpensetup.o \
+ qimpenstroke.o \
+ qimpenwidget.o \
+ qimpenwordpick.o \
+ handwritingimpl.o \
+ qimpenprefbase.o
+INTERFACES = qimpenprefbase.ui
+UICDECLS = qimpenprefbase.h
+UICIMPLS = qimpenprefbase.cpp
+SRCMOC = moc_qimpenhelp.cpp \
+ moc_qimpeninput.cpp \
+ moc_qimpenmatch.cpp \
+ moc_qimpensetup.cpp \
+ moc_qimpenwidget.cpp \
+ moc_qimpenwordpick.cpp \
+ moc_qimpenprefbase.cpp
+OBJMOC = moc_qimpenhelp.o \
+ moc_qimpeninput.o \
+ moc_qimpenmatch.o \
+ moc_qimpensetup.o \
+ moc_qimpenwidget.o \
+ moc_qimpenwordpick.o \
+ moc_qimpenprefbase.o
+
+
+####### Implicit rules
+
+.SUFFIXES: .cpp .cxx .cc .C .c
+
+.cpp.o:
+ $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
+
+.cxx.o:
+ $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
+
+.cc.o:
+ $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
+
+.C.o:
+ $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
+
+.c.o:
+ $(CC) -c $(CFLAGS) $(INCPATH) -o $@ $<
+
+####### Build rules
+
+
+all: $(DESTDIR)$(SYSCONF_LINK_TARGET)
+
+$(DESTDIR)$(SYSCONF_LINK_TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC) $(SUBLIBS)
+ $(SYSCONF_LINK_LIB)
+
+moc: $(SRCMOC)
+
+tmake:
+ tmake handwriting.pro
+
+clean:
+ -rm -f $(OBJECTS) $(OBJMOC) $(SRCMOC) $(UICIMPLS) $(UICDECLS)
+ -rm -f *~ core
+ -rm -f allmoc.cpp
+
+####### Extension Modules
+
+listpromodules:
+ @echo
+
+listallmodules:
+ @echo
+
+listaddonpromodules:
+ @echo
+
+listaddonentmodules:
+ @echo
+
+
+REQUIRES=
+
+####### Sub-libraries
+
+
+###### Combined headers
+
+
+
+####### Compile
+
+qimpenchar.o: qimpenchar.cpp \
+ qimpencombining.h \
+ qimpenchar.h \
+ qimpenstroke.h
+
+qimpenprofile.o: qimpenprofile.cpp \
+ qimpencombining.h \
+ qimpenchar.h \
+ qimpenstroke.h \
+ qimpenprofile.h
+
+qimpencombining.o: qimpencombining.cpp \
+ qimpencombining.h \
+ qimpenchar.h \
+ qimpenstroke.h
+
+qimpenhelp.o: qimpenhelp.cpp \
+ qimpenwidget.h \
+ qimpenchar.h \
+ qimpenstroke.h \
+ qimpencombining.h \
+ qimpenmatch.h \
+ qimpenhelp.h \
+ qimpenprofile.h
+
+qimpeninput.o: qimpeninput.cpp \
+ qimpenwidget.h \
+ qimpenchar.h \
+ qimpenstroke.h \
+ qimpensetup.h \
+ qimpenprofile.h \
+ qimpeninput.h \
+ qimpencombining.h \
+ qimpenwordpick.h \
+ qimpenmatch.h \
+ qimpenhelp.h
+
+qimpenmatch.o: qimpenmatch.cpp \
+ qimpenmatch.h \
+ qimpenchar.h \
+ qimpenstroke.h
+
+qimpensetup.o: qimpensetup.cpp \
+ qimpenwidget.h \
+ qimpenchar.h \
+ qimpenstroke.h \
+ qimpenprefbase.h \
+ qimpensetup.h \
+ qimpenprofile.h
+
+qimpenstroke.o: qimpenstroke.cpp \
+ qimpenstroke.h
+
+qimpenwidget.o: qimpenwidget.cpp \
+ qimpenchar.h \
+ qimpenstroke.h \
+ qimpenwidget.h
+
+qimpenwordpick.o: qimpenwordpick.cpp \
+ qimpenwordpick.h \
+ qimpenmatch.h \
+ qimpenchar.h \
+ qimpenstroke.h
+
+handwritingimpl.o: handwritingimpl.cpp \
+ qimpeninput.h \
+ qimpenprofile.h \
+ qimpenchar.h \
+ qimpenstroke.h \
+ handwritingimpl.h
+
+qimpenprefbase.h: qimpenprefbase.ui
+ $(UIC) qimpenprefbase.ui -o $(INTERFACE_DECL_PATH)/qimpenprefbase.h
+
+qimpenprefbase.cpp: qimpenprefbase.ui
+ $(UIC) qimpenprefbase.ui -i qimpenprefbase.h -o qimpenprefbase.cpp
+
+qimpenprefbase.o: qimpenprefbase.cpp \
+ qimpenprefbase.h \
+ qimpenprefbase.ui
+
+moc_qimpenhelp.o: moc_qimpenhelp.cpp \
+ qimpenhelp.h \
+ qimpenchar.h \
+ qimpenstroke.h \
+ qimpenprofile.h
+
+moc_qimpeninput.o: moc_qimpeninput.cpp \
+ qimpeninput.h \
+ qimpenprofile.h \
+ qimpenchar.h \
+ qimpenstroke.h
+
+moc_qimpenmatch.o: moc_qimpenmatch.cpp \
+ qimpenmatch.h \
+ qimpenchar.h \
+ qimpenstroke.h
+
+moc_qimpensetup.o: moc_qimpensetup.cpp \
+ qimpensetup.h \
+ qimpenprofile.h \
+ qimpenchar.h \
+ qimpenstroke.h
+
+moc_qimpenwidget.o: moc_qimpenwidget.cpp \
+ qimpenwidget.h \
+ qimpenchar.h \
+ qimpenstroke.h
+
+moc_qimpenwordpick.o: moc_qimpenwordpick.cpp \
+ qimpenwordpick.h \
+ qimpenmatch.h \
+ qimpenchar.h \
+ qimpenstroke.h
+
+moc_qimpenprefbase.o: moc_qimpenprefbase.cpp \
+ qimpenprefbase.h
+
+moc_qimpenhelp.cpp: qimpenhelp.h
+ $(MOC) qimpenhelp.h -o moc_qimpenhelp.cpp
+
+moc_qimpeninput.cpp: qimpeninput.h
+ $(MOC) qimpeninput.h -o moc_qimpeninput.cpp
+
+moc_qimpenmatch.cpp: qimpenmatch.h
+ $(MOC) qimpenmatch.h -o moc_qimpenmatch.cpp
+
+moc_qimpensetup.cpp: qimpensetup.h
+ $(MOC) qimpensetup.h -o moc_qimpensetup.cpp
+
+moc_qimpenwidget.cpp: qimpenwidget.h
+ $(MOC) qimpenwidget.h -o moc_qimpenwidget.cpp
+
+moc_qimpenwordpick.cpp: qimpenwordpick.h
+ $(MOC) qimpenwordpick.h -o moc_qimpenwordpick.cpp
+
+moc_qimpenprefbase.cpp: qimpenprefbase.h
+ $(MOC) qimpenprefbase.h -o moc_qimpenprefbase.cpp
+
+
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 @@
+TEMPLATE = lib
+CONFIG += qt warn_on release
+HEADERS = qimpenchar.h \
+ qimpenprofile.h \
+ qimpencombining.h \
+ qimpenhelp.h \
+ qimpeninput.h \
+ qimpenmatch.h \
+ qimpensetup.h \
+ qimpenstroke.h \
+ qimpenwidget.h \
+ qimpenwordpick.h \
+ handwritingimpl.h
+SOURCES = qimpenchar.cpp \
+ qimpenprofile.cpp \
+ qimpencombining.cpp \
+ qimpenhelp.cpp \
+ qimpeninput.cpp \
+ qimpenmatch.cpp \
+ qimpensetup.cpp \
+ qimpenstroke.cpp \
+ qimpenwidget.cpp \
+ qimpenwordpick.cpp \
+ handwritingimpl.cpp
+INTERFACES = qimpenprefbase.ui
+TARGET = qhandwriting
+DESTDIR = ../../plugins/inputmethods
+INCLUDEPATH += $(QPEDIR)/include
+DEPENDPATH += ../$(QPEDIR)/include ../../taskbar
+LIBS += -lqpe
+VERSION = 1.0.0
+
+TRANSLATIONS += ../../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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include <qapplication.h>
+#include <qwidget.h>
+#include <qpixmap.h>
+#include "qimpeninput.h"
+#include "handwritingimpl.h"
+
+/* XPM */
+static const char * pen_xpm[] = {
+"28 13 9 1",
+" c None",
+". c #000000",
+"+ c #FFE993",
+"@ c #8292FF",
+"# c #F7C500",
+"$ c #C69F00",
+"% c #0022FF",
+"& c #000F72",
+"* c #A3732C",
+" . ",
+" .+. ",
+" .@#$. ",
+" .@%&. ",
+" .@%&. ",
+" . .@%&. ",
+" . .@%&. ",
+" . .@%&. ",
+" ... ... .. .@%&. ",
+" . . . . . .*.&. ",
+" . . . . . .**. ",
+" ... ... .. ... ",
+" "};
+
+HandwritingImpl::HandwritingImpl()
+ : input(0), icn(0), ref(0)
+{
+}
+
+HandwritingImpl::~HandwritingImpl()
+{
+ delete input;
+ delete icn;
+}
+
+QWidget *HandwritingImpl::inputMethod( QWidget *parent, Qt::WFlags f )
+{
+ if ( !input )
+ input = new QIMPenInput( parent, "Handwriting", f );
+ return input;
+}
+
+void HandwritingImpl::resetState()
+{
+ if ( input )
+ input->resetState();
+}
+
+QPixmap *HandwritingImpl::icon()
+{
+ if ( !icn )
+ icn = new QPixmap( (const char **)pen_xpm );
+ return icn;
+}
+
+QString HandwritingImpl::name()
+{
+ return qApp->translate( "InputMethods", "Handwriting" );
+}
+
+void HandwritingImpl::onKeyPress( QObject *receiver, const char *slot )
+{
+ if ( input )
+ QObject::connect( input, SIGNAL(key(ushort,ushort,ushort,bool,bool)), receiver, slot );
+}
+
+#ifndef QT_NO_COMPONENT
+QRESULT HandwritingImpl::queryInterface( const QUuid &uuid, QUnknownInterface **iface )
+{
+ *iface = 0;
+ if ( uuid == IID_QUnknown )
+ *iface = this;
+ else if ( uuid == IID_InputMethod )
+ *iface = this;
+
+ if ( *iface )
+ (*iface)->addRef();
+ return QS_OK;
+}
+
+Q_EXPORT_INTERFACE()
+{
+ Q_CREATE_INSTANCE( HandwritingImpl )
+}
+#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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+#ifndef HANDWRITINGIMPL_H
+#define HANDWRITINGIMPL_H
+
+#include <qpe/inputmethodinterface.h>
+
+class QIMPenInput;
+class QPixmap;
+
+class HandwritingImpl : public InputMethodInterface
+{
+public:
+ HandwritingImpl();
+ virtual ~HandwritingImpl();
+
+#ifndef QT_NO_COMPONENT
+ QRESULT queryInterface( const QUuid&, QUnknownInterface** );
+ Q_REFCOUNT
+#endif
+
+ virtual QWidget *inputMethod( QWidget *parent, Qt::WFlags f );
+ virtual void resetState();
+ virtual QPixmap *icon();
+ virtual QString name();
+ virtual void onKeyPress( QObject *receiver, const char *slot );
+
+private:
+ QIMPenInput *input;
+ QPixmap *icn;
+ ulong ref;
+};
+
+#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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include <qfile.h>
+#include <qtl.h>
+#include <math.h>
+#include <limits.h>
+#include <errno.h>
+#include <qdatastream.h>
+#include "qimpencombining.h"
+#include "qimpenchar.h"
+
+#define QIMPEN_MATCH_THRESHOLD 200000
+
+const QIMPenSpecialKeys qimpen_specialKeys[] = {
+ { Qt::Key_Escape, "[Esc]" },
+ { Qt::Key_Tab, "[Tab]" },
+ { Qt::Key_Backspace, "[BackSpace]" },
+ { Qt::Key_Return, "[Return]" },
+ { QIMPenChar::Caps, "[Uppercase]" },
+ { QIMPenChar::CapsLock, "[Caps Lock]" },
+ { QIMPenChar::Shortcut, "[Shortcut]" },
+ { QIMPenChar::Punctuation, "[Punctuation]" },
+ { QIMPenChar::Symbol, "[Symbol]" },
+ { QIMPenChar::Extended, "[Extended]" },
+ { Qt::Key_unknown, 0 } };
+
+
+/*!
+ \class QIMPenChar qimpenchar.h
+
+ Handles a single character. Can calculate closeness of match to
+ another character.
+*/
+
+QIMPenChar::QIMPenChar()
+{
+ flags = 0;
+ strokes.setAutoDelete( TRUE );
+}
+
+QIMPenChar::QIMPenChar( const QIMPenChar &chr )
+{
+ strokes.setAutoDelete( TRUE );
+ ch = chr.ch;
+ flags = chr.flags;
+ d = chr.d;
+ QIMPenStrokeIterator it( chr.strokes );
+ while ( it.current() ) {
+ strokes.append( new QIMPenStroke( *it.current() ) );
+ ++it;
+ }
+}
+
+QIMPenChar &QIMPenChar::operator=( const QIMPenChar &chr )
+{
+ strokes.clear();
+ ch = chr.ch;
+ flags = chr.flags;
+ d = chr.d;
+ QIMPenStrokeIterator it( chr.strokes );
+ while ( it.current() ) {
+ strokes.append( new QIMPenStroke( *it.current() ) );
+ ++it;
+ }
+
+ return *this;
+}
+
+QString QIMPenChar::name() const
+{
+ QString n;
+
+ if ( (ch & 0x0000FFFF) == 0 ) {
+ int code = ch >> 16;
+ for ( int i = 0; qimpen_specialKeys[i].code != Qt::Key_unknown; i++ ) {
+ if ( qimpen_specialKeys[i].code == code ) {
+ n = qimpen_specialKeys[i].name;
+ break;
+ }
+ }
+ } else {
+ n = QChar( ch & 0x0000FFFF );
+ }
+
+ return n;
+}
+
+void QIMPenChar::clear()
+{
+ ch = 0;
+ flags = 0;
+ d = QString::null;
+ strokes.clear();
+}
+
+unsigned int QIMPenChar::strokeLength( int s ) const
+{
+ QIMPenStrokeIterator it( strokes );
+ while ( it.current() && s ) {
+ ++it;
+ --s;
+ }
+
+ if ( it.current() )
+ return it.current()->length();
+
+ return 0;
+}
+
+/*!
+ Add a stroke to the character
+*/
+void QIMPenChar::addStroke( QIMPenStroke *st )
+{
+ QIMPenStroke *stroke = new QIMPenStroke( *st );
+ strokes.append( stroke );
+}
+
+/*!
+ Return an indicator of the closeness of this character to \a pen.
+ Lower value is better.
+*/
+int QIMPenChar::match( QIMPenChar *pen )
+{
+/*
+ if ( strokes.count() > pen->strokes.count() )
+ return INT_MAX;
+*/
+ int err = 0;
+ int maxErr = 0;
+ int diff = 0;
+ QIMPenStrokeIterator it1( strokes );
+ QIMPenStrokeIterator it2( pen->strokes );
+ err = it1.current()->match( it2.current() );
+ if ( err > maxErr )
+ maxErr = err;
+ ++it1;
+ ++it2;
+ while ( err < 400000 && it1.current() && it2.current() ) {
+ QPoint p1 = it1.current()->boundingRect().center() -
+ strokes.getFirst()->boundingRect().center();
+ QPoint p2 = it2.current()->boundingRect().center() -
+ pen->strokes.getFirst()->boundingRect().center();
+ int xdiff = QABS( p1.x() - p2.x() ) - 6;
+ int ydiff = QABS( p1.y() - p2.y() ) - 5;
+ if ( xdiff < 0 )
+ xdiff = 0;
+ if ( ydiff < 0 )
+ ydiff = 0;
+ if ( xdiff > 10 || ydiff > 10 ) { // not a chance
+#ifdef DEBUG_QIMPEN
+ qDebug( "char %c, stroke starting pt diff excessive", pen->ch );
+#endif
+ return INT_MAX;
+ }
+ diff += xdiff*xdiff + ydiff*ydiff;
+ err = it1.current()->match( it2.current() );
+ if ( err > maxErr )
+ maxErr = err;
+ ++it1;
+ ++it2;
+ }
+
+ maxErr += diff * diff * 6; // magic weighting :)
+
+#ifdef DEBUG_QIMPEN
+ qDebug( "char: %c, maxErr %d, diff %d, (%d)", pen->ch, maxErr, diff, strokes.count() );
+#endif
+ return maxErr;
+}
+
+/*!
+ Return the bounding rect of this character. It may have sides with
+ negative coords since its origin is where the user started drawing
+ the character.
+*/
+QRect QIMPenChar::boundingRect()
+{
+ QRect br;
+ QIMPenStroke *st = strokes.first();
+ while ( st ) {
+ br |= st->boundingRect();
+ st = strokes.next();
+ }
+
+ return br;
+}
+
+
+/*!
+ Write the character's data to the stream.
+*/
+QDataStream &operator<< (QDataStream &s, const QIMPenChar &ws)
+{
+ s << ws.ch;
+ s << ws.flags;
+ if ( ws.flags & QIMPenChar::Data )
+ s << ws.d;
+ s << ws.strokes.count();
+ QIMPenStrokeIterator it( ws.strokes );
+ while ( it.current() ) {
+ s << *it.current();
+ ++it;
+ }
+
+ return s;
+}
+
+/*!
+ Read the character's data from the stream.
+*/
+QDataStream &operator>> (QDataStream &s, QIMPenChar &ws)
+{
+ s >> ws.ch;
+ s >> ws.flags;
+ if ( ws.flags & QIMPenChar::Data )
+ s >> ws.d;
+ unsigned size;
+ s >> size;
+ for ( unsigned i = 0; i < size; i++ ) {
+ QIMPenStroke *st = new QIMPenStroke();
+ s >> *st;
+ ws.strokes.append( st );
+ }
+
+ return s;
+}
+
+//===========================================================================
+
+bool QIMPenCharMatch::operator>( const QIMPenCharMatch &m )
+{
+ return error > m.error;
+}
+
+bool QIMPenCharMatch::operator<( const QIMPenCharMatch &m )
+{
+ return error < m.error;
+}
+
+bool QIMPenCharMatch::operator<=( const QIMPenCharMatch &m )
+{
+ return error <= m.error;
+}
+
+//===========================================================================
+
+/*!
+ \class QIMPenCharSet qimpenchar.h
+
+ Maintains a set of related characters.
+*/
+
+QIMPenCharSet::QIMPenCharSet()
+{
+ chars.setAutoDelete( TRUE );
+ desc = "Unnamed";
+ csTitle = "abc";
+ csType = Unknown;
+ maxStrokes = 0;
+}
+
+/*!
+ Construct and load a characters set from file \a fn.
+*/
+QIMPenCharSet::QIMPenCharSet( const QString &fn )
+{
+ chars.setAutoDelete( TRUE );
+ desc = "Unnamed";
+ csTitle = "abc";
+ csType = Unknown;
+ maxStrokes = 0;
+ load( fn, System );
+}
+
+const QString &QIMPenCharSet::filename( Domain d ) const
+{
+ if ( d == System )
+ return sysFilename;
+ else
+ return userFilename;
+}
+
+void QIMPenCharSet::setFilename( const QString &fn, Domain d )
+{
+ if ( d == System )
+ sysFilename = fn;
+ else if ( d == User )
+ userFilename = fn;
+}
+
+/*!
+ Load a character set from file \a fn.
+*/
+bool QIMPenCharSet::load( const QString &fn, Domain d )
+{
+ setFilename( fn, d );
+
+ bool ok = FALSE;
+ QFile file( fn );
+ if ( file.open( IO_ReadOnly ) ) {
+ QDataStream ds( &file );
+ QString version;
+ ds >> version;
+ ds >> csTitle;
+ ds >> desc;
+ int major = version.mid( 4, 1 ).toInt();
+ int minor = version.mid( 6 ).toInt();
+ if ( major >= 1 && minor > 0 ) {
+ ds >> (Q_INT8 &)csType;
+ } else {
+ if ( csTitle == "abc" )
+ csType = Lower;
+ else if ( csTitle == "ABC" )
+ csType = Upper;
+ else if ( csTitle == "123" )
+ csType = Numeric;
+ else if ( fn == "Combining" )
+ csType = Combining;
+ }
+ while ( !ds.atEnd() ) {
+ QIMPenChar *pc = new QIMPenChar;
+ ds >> *pc;
+ if ( d == User )
+ markDeleted( pc->character() ); // override system
+ addChar( pc );
+ }
+ if ( file.status() == IO_Ok )
+ ok = TRUE;
+ }
+
+ return ok;
+}
+
+/*!
+ Save this character set.
+*/
+bool QIMPenCharSet::save( Domain d )
+{
+ if ( filename( d ).isEmpty() )
+ return FALSE;
+
+ bool ok = FALSE;
+
+ QString fn = filename( d );
+ QString tmpFn = fn + ".new";
+ QFile file( tmpFn );
+ if ( file.open( IO_WriteOnly|IO_Raw ) ) {
+ QDataStream ds( &file );
+ ds << QString( "QPT 1.1" );
+ ds << csTitle;
+ ds << desc;
+ ds << (Q_INT8)csType;
+ QIMPenCharIterator ci( chars );
+ for ( ; ci.current(); ++ci ) {
+ QIMPenChar *pc = ci.current();
+ if ( ( (d == System) && pc->testFlag( QIMPenChar::System ) ) ||
+ ( (d == User) && !pc->testFlag( QIMPenChar::System ) ) ) {
+ ds << *pc;
+ }
+ if ( file.status() != IO_Ok )
+ break;
+ }
+ if ( file.status() == IO_Ok )
+ ok = TRUE;
+ }
+
+ if ( ok ) {
+ if ( ::rename( tmpFn.latin1(), fn.latin1() ) < 0 ) {
+ qWarning( "problem renaming file %s to %s, errno: %d",
+ tmpFn.latin1(), fn.latin1(), errno );
+ // remove the tmp file, otherwise, it will just lay around...
+ QFile::remove( tmpFn.latin1() );
+ ok = FALSE;
+ }
+ }
+
+ return ok;
+}
+
+QIMPenChar *QIMPenCharSet::at( int i )
+{
+ return chars.at(i);
+}
+
+void QIMPenCharSet::markDeleted( uint ch )
+{
+ QIMPenCharIterator ci( chars );
+ for ( ; ci.current(); ++ci ) {
+ QIMPenChar *pc = ci.current();
+ if ( pc->character() == ch && pc->testFlag( QIMPenChar::System ) )
+ pc->setFlag( QIMPenChar::Deleted );
+ }
+}
+
+/*!
+ Find the best matches for \a ch in this character set.
+*/
+QIMPenCharMatchList QIMPenCharSet::match( QIMPenChar *ch )
+{
+ QIMPenCharMatchList matches;
+
+ QIMPenCharIterator ci( chars );
+ for ( ; ci.current(); ++ci ) {
+ QIMPenChar *tmplChar = ci.current();
+ if ( tmplChar->testFlag( QIMPenChar::Deleted ) ) {
+ continue;
+ }
+ int err;
+ if ( ch->penStrokes().count() <= tmplChar->penStrokes().count() ) {
+ err = ch->match( tmplChar );
+ if ( err <= QIMPEN_MATCH_THRESHOLD ) {
+ if (tmplChar->penStrokes().count() != ch->penStrokes().count())
+ err = QIMPEN_MATCH_THRESHOLD;
+ QIMPenCharMatchList::Iterator it;
+ for ( it = matches.begin(); it != matches.end(); ++it ) {
+ if ( (*it).penChar->character() == tmplChar->character() &&
+ (*it).penChar->penStrokes().count() == tmplChar->penStrokes().count() ) {
+ if ( (*it).error > err )
+ (*it).error = err;
+ break;
+ }
+ }
+ if ( it == matches.end() ) {
+ QIMPenCharMatch m;
+ m.error = err;
+ m.penChar = tmplChar;
+ matches.append( m );
+ }
+ }
+ }
+ }
+ qHeapSort( matches );
+/*
+ QIMPenCharMatchList::Iterator it;
+ for ( it = matches.begin(); it != matches.end(); ++it ) {
+ qDebug( "Match: \'%c\', error %d, strokes %d", (*it).penChar->character(),
+ (*it).error, (*it).penChar->penStrokes().count() );
+ }
+*/
+ return matches;
+}
+
+/*!
+ Add a character \a ch to this set.
+ QIMPenCharSet will delete this character when it is no longer needed.
+*/
+void QIMPenCharSet::addChar( QIMPenChar *ch )
+{
+ if ( ch->penStrokes().count() > maxStrokes )
+ maxStrokes = ch->penStrokes().count();
+ chars.append( ch );
+}
+
+/*!
+ Remove a character by reference \a ch from this set.
+ QIMPenCharSet will delete this character.
+*/
+void QIMPenCharSet::removeChar( QIMPenChar *ch )
+{
+ chars.remove( ch );
+}
+
+/*!
+ Move the character up the list of characters.
+*/
+void QIMPenCharSet::up( QIMPenChar *ch )
+{
+ int idx = chars.findRef( ch );
+ if ( idx > 0 ) {
+ chars.take();
+ chars.insert( idx - 1, ch );
+ }
+}
+
+/*!
+ Move the character down the list of characters.
+*/
+void QIMPenCharSet::down( QIMPenChar *ch )
+{
+ int idx = chars.findRef( ch );
+ if ( idx >= 0 && idx < (int)chars.count() - 1 ) {
+ chars.take();
+ chars.insert( idx + 1, ch );
+ }
+}
+
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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QIMPENCHAR_H_
+#define QIMPENCHAR_H_
+
+#include <qlist.h>
+#include <qvaluelist.h>
+#include <qcstring.h>
+#include "qimpenstroke.h"
+
+struct QIMPenSpecialKeys {
+ int code;
+ char *name;
+};
+
+extern const QIMPenSpecialKeys qimpen_specialKeys[];
+
+
+class QIMPenChar
+{
+public:
+ QIMPenChar();
+ QIMPenChar( const QIMPenChar & );
+
+ unsigned int character() const { return ch; }
+ void setCharacter( unsigned int c ) { ch = c; }
+
+ const QString &data() const { return d; }
+ void setData( const QString &ba ) { d = ba; }
+
+ QString name() const;
+ bool isEmpty() const { return strokes.isEmpty(); }
+ unsigned int strokeCount() const { return strokes.count(); }
+ unsigned int strokeLength( int s ) const;
+ void clear();
+ int match( QIMPenChar *ch );
+ const QIMPenStrokeList &penStrokes() { return strokes; }
+ QPoint startingPoint() const { return strokes.getFirst()->startingPoint(); }
+ QRect boundingRect();
+
+ void setFlag( int f ) { flags |= f; }
+ void clearFlag( int f ) { flags &= ~f; }
+ bool testFlag( int f ) { return flags & f; }
+
+ enum Flags { System=0x01, Deleted=0x02, CombineRight=0x04, Data=0x08 };
+ // Correspond to codes in template files. Do not change values.
+ enum Mode { ModeBase=0x4000, Caps=0x4001, Shortcut=0x4002, CapsLock=0x4003,
+ Punctuation=0x4004, Symbol=0x4005, Extended=0x4006 };
+
+ QIMPenChar &operator=( const QIMPenChar &s );
+
+ void addStroke( QIMPenStroke * );
+
+protected:
+ unsigned int ch;
+ QString d;
+ Q_UINT8 flags;
+ QIMPenStrokeList strokes;
+
+ friend QDataStream &operator<< (QDataStream &, const QIMPenChar &);
+ friend QDataStream &operator>> (QDataStream &, QIMPenChar &);
+};
+
+typedef QList<QIMPenChar> QIMPenCharList;
+typedef QListIterator<QIMPenChar> QIMPenCharIterator;
+
+QDataStream & operator<< (QDataStream & s, const QIMPenChar &ws);
+QDataStream & operator>> (QDataStream & s, QIMPenChar &ws);
+
+struct QIMPenCharMatch
+{
+ int error;
+ QIMPenChar *penChar;
+
+ bool operator>( const QIMPenCharMatch &m );
+ bool operator<( const QIMPenCharMatch &m );
+ bool operator<=( const QIMPenCharMatch &m );
+};
+
+typedef QValueList<QIMPenCharMatch> QIMPenCharMatchList;
+
+
+class QIMPenCharSet
+{
+public:
+ QIMPenCharSet();
+ QIMPenCharSet( const QString &fn );
+
+ bool isEmpty() const { return chars.isEmpty(); }
+ unsigned int count() const { return chars.count(); }
+ void clear() { chars.clear(); }
+
+ void setDescription( const QString &d ) { desc = d; }
+ QString description() const { return desc; }
+ void setTitle( const QString &t ) { csTitle = t; }
+ QString title() const { return csTitle; }
+
+ QIMPenCharMatchList match( QIMPenChar *ch );
+ void addChar( QIMPenChar *ch );
+ void removeChar( QIMPenChar *ch );
+ QIMPenChar *at( int i );
+
+ unsigned maximumStrokes() const { return maxStrokes; }
+
+ void up( QIMPenChar *ch );
+ void down( QIMPenChar *ch );
+
+ enum Domain { System, User };
+ enum Type { Unknown=0x00, Lower=0x01, Upper=0x02, Combining=0x04,
+ Numeric=0x08, Punctuation=0x10, Symbol=0x20, Shortcut=0x40 };
+
+ const QIMPenCharList &characters() const { return chars; }
+
+ void setType( Type t ) { csType = t; }
+ Type type() const { return csType; }
+
+ const QString &filename( Domain d ) const;
+ void setFilename( const QString &fn, Domain d=System );
+ bool load( const QString &fn, Domain d=System );
+ bool save( Domain d=System );
+
+protected:
+ void markDeleted( uint ch );
+
+protected:
+ QString csTitle;
+ QString desc;
+ QString sysFilename;
+ QString userFilename;
+ Type csType;
+ unsigned maxStrokes;
+ QIMPenCharList chars;
+ QIMPenCharMatchList matches;
+};
+
+typedef QList<QIMPenCharSet> QIMPenCharSetList;
+typedef QListIterator<QIMPenCharSet> QIMPenCharSetIterator;
+
+#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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include <qfile.h>
+#include <qtl.h>
+#include <math.h>
+#include <limits.h>
+#include <qdatastream.h>
+#include "qimpencombining.h"
+
+static unsigned int combiningSymbols[] = { '\\', '/', '^', '~', '\"', 'o' };
+static unsigned int combiningChars[][7] = {
+ // \ / ^ ~ "
+ { 'A', 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5 },
+ { 'O', 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0000 },
+ { 'U', 0x00D9, 0x00DA, 0x00DB, 0x0000, 0x00DC, 0x0000 },
+ { 'E', 0x00C8, 0x00C9, 0x00CA, 0x0000, 0x00CB, 0x0000 },
+ { 'I', 0x00CC, 0x00CD, 0x00CE, 0x0000, 0x00CF, 0x0000 },
+ { 'a', 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5 },
+ { 'e', 0x00E8, 0x00E9, 0x00EA, 0x0000, 0x00EB, 0x0000 },
+ { 'i', 0x00EC, 0x00ED, 0x00EE, 0x0000, 0x00EF, 0x0000 },
+ { 'n', 0x0000, 0x0000, 0x0000, 0x00F1, 0x0000, 0x0000 },
+ { 'o', 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0000 },
+ { 'u', 0x00F9, 0x00FA, 0x00FB, 0x0000, 0x00FC, 0x0000 },
+ { 'y', 0x0000, 0x00FD, 0x0000, 0x0000, 0x00FF, 0x0000 },
+ { 0, 0, 0, 0, 0, 0, 0 }
+};
+
+
+QIMPenCombining::QIMPenCombining()
+{
+}
+
+QIMPenCombining::QIMPenCombining( const QString &fn )
+ : QIMPenCharSet( fn )
+{
+}
+
+void QIMPenCombining::addCombined( QIMPenCharSet *cs )
+{
+ unsigned int count = cs->count();
+ QIMPenCharIterator it( cs->characters() );
+ for ( ; it.current() && count; ++it, --count ) {
+ QIMPenChar *pc = it.current();
+ if ( pc->testFlag( QIMPenChar::Deleted ) )
+ continue;
+ int charIdx = findCombining( pc->character() );
+ if ( charIdx < 0 )
+ continue;
+ for ( int i = 0; i < 6; i++ ) {
+ if ( combiningChars[charIdx][i+1] ) {
+ QIMPenCharIterator cit( chars );
+ for ( ; cit.current(); ++cit ) {
+ QIMPenChar *accentPc = cit.current();
+ if ( accentPc->character() == combiningSymbols[i] ) {
+ QIMPenChar *combined = combine( pc, accentPc );
+ combined->setCharacter( combiningChars[charIdx][i+1] );
+ cs->addChar( combined );
+ }
+ }
+ }
+ }
+ }
+}
+
+int QIMPenCombining::findCombining( unsigned int ch ) const
+{
+ int i = 0;
+ while ( combiningChars[i][0] ) {
+ if ( combiningChars[i][0] == ch )
+ return i;
+ i++;
+ }
+
+ return -1;
+}
+
+QIMPenChar *QIMPenCombining::combine( QIMPenChar *base, QIMPenChar *accent )
+{
+ QRect brect = base->boundingRect();
+ QRect arect = accent->boundingRect();
+ int offset;
+ if ( accent->testFlag( QIMPenChar::CombineRight ) )
+ offset = brect.left() - arect.left() + brect.width() + 2;
+ else
+ offset = brect.left() - arect.left() + (brect.width() - arect.width())/2;
+ QIMPenChar *combined = 0;
+ if ( base->character() == 'i' ) {
+ // Hack to remove the dot from i's when combining.
+ if ( base->penStrokes().count() > 1 ) {
+ combined = new QIMPenChar;
+ QIMPenStrokeIterator it( base->penStrokes() );
+ for ( unsigned int i = 0; i < base->penStrokes().count()-1; ++it, i++ ) {
+ QIMPenStroke *st = new QIMPenStroke( *(it.current()) );
+ combined->addStroke( st );
+ }
+ combined->setFlag( QIMPenChar::System );
+ }
+ }
+ if ( !combined )
+ combined = new QIMPenChar( *base );
+ QIMPenStrokeIterator it( accent->penStrokes() );
+ for ( ; it.current(); ++it ) {
+ QIMPenStroke *st = new QIMPenStroke( *(it.current()) );
+ st->setStartingPoint( st->startingPoint() + QPoint(offset, 0 ));
+ combined->addStroke( st );
+ delete st;
+ }
+
+ return combined;
+}
+
+QIMPenChar *QIMPenCombining::penChar( int type )
+{
+ QIMPenCharIterator it( chars );
+ for ( ; it.current(); ++it ) {
+ QIMPenChar *pc = it.current();
+ if ( pc->character() == combiningSymbols[type] )
+ return pc;
+ }
+
+ return 0;
+}
+
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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QIMPENCOMBINING_H_
+#define QIMPENCOMBINING_H_
+
+#include <qlist.h>
+#include "qimpenchar.h"
+
+class QIMPenCombining : public QIMPenCharSet
+{
+public:
+ QIMPenCombining();
+ QIMPenCombining( const QString &fn );
+
+ void addCombined( QIMPenCharSet * );
+
+protected:
+ int findCombining( unsigned int ch ) const;
+ QIMPenChar *combine( QIMPenChar *base, QIMPenChar *accent );
+ QIMPenChar *penChar( int type );
+};
+
+#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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "qimpenwidget.h"
+#include "qimpencombining.h"
+#include "qimpenmatch.h"
+#include "qimpenhelp.h"
+
+#include <qpe/qpeapplication.h>
+#include <qpe/global.h>
+#include <qpe/config.h>
+#include <qpe/stringutil.h>
+
+#include <qtextview.h>
+#include <qlabel.h>
+#include <qlistbox.h>
+#include <qcombobox.h>
+#include <qpushbutton.h>
+#include <qlayout.h>
+#include <qtimer.h>
+#include <qtextstream.h>
+
+/* XPM */
+static const char * const left_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ",
+" . ",
+" .. ",
+" ... ",
+" .... ",
+" ..... ",
+" ...... ",
+" ..... ",
+" .... ",
+" ... ",
+" .. ",
+" . ",
+" ",
+" "};
+
+
+/* XPM */
+static const char * const right_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ",
+" . ",
+" .. ",
+" ... ",
+" .... ",
+" ..... ",
+" ...... ",
+" ..... ",
+" .... ",
+" ... ",
+" .. ",
+" . ",
+" ",
+" "};
+
+class CharListItem : public QListBoxText
+{
+public:
+ CharListItem( const QString &text, uint c )
+ : QListBoxText( text )
+ {
+ _code = c;
+ }
+
+ uint code() const { return _code; }
+
+protected:
+ uint _code;
+};
+
+HandwritingHelp::HandwritingHelp( QIMPenProfile *p, QWidget *parent, const char *name, WFlags f )
+ : QTabWidget( parent, name, f )
+{
+ setCaption( tr("Handwriting Help") );
+ QTextView *help = new QTextView( this );
+ help->setFrameStyle( QFrame::NoFrame );
+ help->setText(
+ tr( "<ul><li>When you start to use the handwriting recogniser "
+ "write slowly, accurately and firmly."
+ "<li>Use the guide lines when drawing your characters."
+ "<li>When drawing a character with multiple strokes, each "
+ "successive stroke must be drawn before the grayed strokes are erased."
+ "<li>Practice your handwriting using the handwriting trainer."
+ "<li>When adding your own character templates make sure they "
+ "are sufficiently different from other characters' templates."
+ "</ul>") );
+
+ addTab( help, tr("Tips") );
+
+ HandwritingTrainer *trainer = new HandwritingTrainer( p, this );
+ addTab( trainer, tr("Trainer") );
+}
+
+void HandwritingHelp::showEvent( QShowEvent * )
+{
+ Global::hideInputMethod();
+}
+
+void HandwritingHelp::hideEvent( QHideEvent * )
+{
+ Global::showInputMethod();
+}
+
+//---------------------------------------------------------------------------
+
+HandwritingTrainer::HandwritingTrainer( QIMPenProfile *p, QWidget *parent, const char *name )
+ : QWidget( parent, name ), profile(p)
+{
+ QGridLayout *gl = new QGridLayout( this, 5, 2, 0, 4 );
+ gl->setRowStretch( 1, 1 );
+ gl->setRowStretch( 2, 1 );
+ gl->setColStretch( 1, 1 );
+
+ charSetCombo = new QComboBox( this );
+ gl->addMultiCellWidget( charSetCombo, 0, 0, 0, 1 );
+ connect( charSetCombo, SIGNAL(activated(int)), SLOT(selectCharSet(int)));
+ QIMPenCharSetIterator it( profile->charSets() );
+ for ( ; it.current(); ++it ) {
+ charSetCombo->insertItem( it.current()->description() );
+ }
+
+ charList = new QListBox( this );
+ charList->setHScrollBarMode( QListBox::AlwaysOff );
+ charList->setFixedWidth( 80 );
+ connect( charList, SIGNAL(highlighted(int)), this, SLOT(selectChar(int)) );
+ gl->addMultiCellWidget( charList, 1, 2, 0, 0 );
+
+ QLabel *help = new QLabel( this );
+ help->setAlignment( AlignLeft | AlignVCenter | WordBreak );
+ gl->addWidget( help, 1, 1 );
+ help->setText(
+ tr( "Select a character from the list. The writing area on the left "
+ "shows the reference character. Practice writing in the area on "
+ "the right.") );
+
+ result = new QLabel( this );
+ gl->addMultiCellWidget( result, 2, 3, 1, 1 );
+
+ matcher = new QIMPenMatch( this );
+ matcher->setCharSet( currentSet );
+ connect( matcher, SIGNAL(noMatch()), this, SLOT(noMatch()) );
+ connect( matcher, SIGNAL(matchedCharacters(const QIMPenCharMatchList &)),
+ this, SLOT(matched(const QIMPenCharMatchList &)) );
+
+ QHBoxLayout *hb = new QHBoxLayout();
+ gl->addLayout( hb, 3, 0 );
+ prevBtn = new QPushButton( this );
+ prevBtn->setPixmap( QPixmap( (const char **)left_xpm ) );
+ connect( prevBtn, SIGNAL(clicked()), SLOT(prevChar()));
+ hb->addWidget( prevBtn );
+
+ nextBtn = new QPushButton( this );
+ nextBtn->setPixmap( QPixmap( (const char **)right_xpm ) );
+ connect( nextBtn, SIGNAL(clicked()), SLOT(nextChar()));
+ hb->addWidget( nextBtn );
+
+ refPw = new QIMPenWidget( this );
+ refPw->setReadOnly( TRUE );
+ gl->addWidget( refPw, 4, 0 );
+
+ pracPw = new QIMPenWidget( this );
+ connect( matcher, SIGNAL(removeStroke()), pracPw, SLOT(removeStroke()) );
+ connect( pracPw, SIGNAL(beginStroke()),
+ this, SLOT(beginStroke()) );
+ connect( pracPw, SIGNAL(stroke( QIMPenStroke * )),
+ this, SLOT(strokeEntered( QIMPenStroke * )) );
+ connect( pracPw, SIGNAL(beginStroke()),
+ matcher, SLOT(beginStroke()) );
+ connect( pracPw, SIGNAL(stroke( QIMPenStroke * )),
+ matcher, SLOT(strokeEntered( QIMPenStroke * )) );
+ gl->addWidget( pracPw, 4, 1 );
+
+ redrawTimer = new QTimer( this );
+ connect( redrawTimer, SIGNAL(timeout()), this, SLOT(redrawChar()) );
+ redrawTimer->start( 5000 );
+
+ currentSet = 0;
+ charSetCombo->setCurrentItem( 1 );
+ selectCharSet( 1 );
+}
+
+HandwritingTrainer::~HandwritingTrainer()
+{
+}
+
+void HandwritingTrainer::showEvent( QShowEvent * )
+{
+ redrawChar();
+ redrawTimer->start( 5000 );
+}
+
+void HandwritingTrainer::setCurrentChar( QIMPenChar *c )
+{
+ currentChar = c;
+ refPw->showCharacter( currentChar );
+ pracPw->clear();
+ if ( currentChar ) {
+ prevBtn->setEnabled( findPrev() != 0 );
+ nextBtn->setEnabled( findNext() != 0 );
+ }
+ result->setText( "" );
+ redrawTimer->start( 5000 );
+}
+
+void HandwritingTrainer::selectChar( int i )
+{
+ currentChar = 0;
+ currentCode = ((CharListItem *)charList->item(i))->code();
+ QIMPenCharIterator it(currentSet->characters() );
+ for ( ; it.current(); ++it ) {
+ if ( it.current()->character() == currentCode &&
+ !it.current()->testFlag( QIMPenChar::Deleted ) ) {
+ setCurrentChar( it.current() );
+ break;
+ }
+ }
+ if ( !it.current() )
+ setCurrentChar( 0 );
+}
+
+void HandwritingTrainer::selectCharSet( int i )
+{
+ if ( currentSet ) {
+ refPw->removeCharSet( 0 );
+ pracPw->removeCharSet( 0 );
+ }
+ currentSet = profile->charSets().at( i );
+ fillCharList();
+ refPw->insertCharSet( currentSet );
+ pracPw->insertCharSet( currentSet );
+ matcher->setCharSet( currentSet );
+ if ( charList->count() ) {
+ charList->setSelected( 0, TRUE );
+ selectChar(0);
+ }
+}
+
+void HandwritingTrainer::noMatch()
+{
+ result->setText( "No match" );
+}
+
+void HandwritingTrainer::matched( const QIMPenCharMatchList &ml )
+{
+ int maxErr = 20000 + (*ml.begin()).penChar->strokeLength(0) * 1000;
+ int baseErr = (*ml.begin()).penChar->strokeLength(0) * 250;
+ unsigned int numStrokes = (*ml.begin()).penChar->strokeCount();
+ QIMPenCharMatchList::ConstIterator it;
+ /*
+ for ( it = ml.begin(); it != ml.end(); ++it ) {
+ if ( (*it).penChar->strokeCount() == numStrokes ) {
+ if ( (*it).error > maxErr )
+ maxErr = (*it).error;
+ }
+ }
+ */
+ int i;
+ QString res;
+ QTextStream ts(&res, IO_WriteOnly);
+ ts << "<qt>" << tr("Matched: ");
+ for ( i = 0, it = ml.begin(); it != ml.end() && i < 4; ++it, i++ ) {
+ if ( (*it).penChar->strokeCount() == numStrokes ) {
+ int rate = 100 - ( ((*it).error - baseErr) * 100 ) / maxErr;
+ if ( it != ml.begin() ) {
+ if ( rate < -10 )
+ continue;
+ ts << "<br>";
+ ts << tr("Similar to: ");
+ }
+ ts << "<big>";
+ if ( (*it).penChar->character() == currentChar->character() )
+ ts << "<b>";
+ ts << Qtopia::escapeString((*it).penChar->name());
+ ts << " (" << rateString(rate) << ")";
+ if ( (*it).penChar->character() == currentChar->character() )
+ ts << "</b>";
+ ts << "</big>";
+ }
+ }
+ ts << "</qt>";
+ result->setText( res );
+}
+
+QString HandwritingTrainer::rateString( int rate ) const
+{
+ if ( rate < 1 )
+ rate = 1;
+ if ( rate > 100 )
+ rate = 100;
+ return tr("%1%").arg(rate);
+}
+
+void HandwritingTrainer::prevChar()
+{
+ QIMPenChar *pc = findPrev();
+ if ( pc )
+ setCurrentChar( pc );
+}
+
+void HandwritingTrainer::nextChar()
+{
+ QIMPenChar *pc = findNext();
+ if ( pc )
+ setCurrentChar( pc );
+}
+
+void HandwritingTrainer::redrawChar()
+{
+ if ( currentChar )
+ refPw->showCharacter( currentChar );
+}
+
+void HandwritingTrainer::beginStroke()
+{
+ redrawTimer->start( 5000 );
+}
+
+void HandwritingTrainer::strokeEntered( QIMPenStroke * )
+{
+ pracPw->greyStroke();
+}
+
+QIMPenChar *HandwritingTrainer::findPrev()
+{
+ if ( !currentChar )
+ return 0;
+ QIMPenCharIterator it( currentSet->characters() );
+ bool found = FALSE;
+ for ( it.toLast(); it.current(); --it ) {
+ if ( !found && it.current() == currentChar )
+ found = TRUE;
+ else if ( found && it.current()->character() == currentCode &&
+ !it.current()->testFlag( QIMPenChar::Deleted ) ) {
+ return it.current();
+ }
+ }
+
+ return 0;
+}
+
+QIMPenChar *HandwritingTrainer::findNext()
+{
+ if ( !currentChar )
+ return 0;
+ QIMPenCharIterator it( currentSet->characters() );
+ bool found = FALSE;
+ for ( ; it.current(); ++it ) {
+ if ( !found && it.current() == currentChar )
+ found = TRUE;
+ else if ( found && it.current()->character() == currentCode &&
+ !it.current()->testFlag( QIMPenChar::Deleted ) ) {
+ return it.current();
+ }
+ }
+
+ return 0;
+}
+
+void HandwritingTrainer::fillCharList()
+{
+ charList->clear();
+ QIMPenCharIterator it( currentSet->characters() );
+ CharListItem *li = 0;
+ for ( ; it.current(); ++it ) {
+ uint ch = it.current()->character();
+ QString n = it.current()->name();
+ if ( !n.isEmpty() )
+ li = new CharListItem( n, ch );
+ if ( li ) {
+ CharListItem *i = (CharListItem *)charList->findItem( li->text() );
+ if ( !i || i->code() != ch ) {
+ charList->insertItem( li );
+ } else {
+ delete li;
+ li = 0;
+ }
+ }
+ }
+ currentChar = 0;
+}
+
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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+#include <qtabwidget.h>
+#include <qlist.h>
+#include "qimpenchar.h"
+#include "qimpenprofile.h"
+
+class QListBox;
+class QLabel;
+class QComboBox;
+class QPushButton;
+class QTimer;
+class QIMPenWidget;
+class QIMPenMatch;
+
+class HandwritingHelp : public QTabWidget
+{
+ Q_OBJECT
+public:
+ HandwritingHelp( QIMPenProfile *p, QWidget *parent=0, const char *name=0, WFlags f=0 );
+
+protected:
+ virtual void showEvent( QShowEvent * );
+ virtual void hideEvent( QHideEvent * );
+};
+
+class HandwritingTrainer : public QWidget
+{
+ Q_OBJECT
+public:
+ HandwritingTrainer( QIMPenProfile *p, QWidget *parent=0, const char *name=0 );
+ ~HandwritingTrainer();
+
+private slots:
+ void selectChar( int );
+ void selectCharSet( int );
+ void noMatch();
+ void matched( const QIMPenCharMatchList &ml );
+ void prevChar();
+ void nextChar();
+ void redrawChar();
+ void beginStroke();
+ void strokeEntered( QIMPenStroke * );
+
+private:
+ virtual void showEvent( QShowEvent * );
+ QString rateString( int rate ) const;
+ void setCurrentChar( QIMPenChar *c );
+ void fillCharList();
+ QIMPenChar *findPrev();
+ QIMPenChar *findNext();
+
+private:
+ QIMPenMatch *matcher;
+ QIMPenCharSet *currentSet;
+ QIMPenChar *currentChar;
+ QIMPenProfile *profile;
+ uint currentCode;
+ QIMPenWidget *refPw;
+ QIMPenWidget *pracPw;
+ QComboBox *charSetCombo;
+ QListBox *charList;
+ QLabel *result;
+ QPushButton *prevBtn;
+ QPushButton *nextBtn;
+ QTimer *redrawTimer;
+};
+
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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "qimpenwidget.h"
+#include "qimpensetup.h"
+#include "qimpeninput.h"
+#include "qimpencombining.h"
+#include "qimpenwordpick.h"
+#include "qimpenmatch.h"
+#include "qimpenhelp.h"
+
+#include <qpe/qpeapplication.h>
+#include <qpe/qdawg.h>
+#include <qpe/config.h>
+#include <qpe/global.h>
+
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qlabel.h>
+#include <qtimer.h>
+#include <qdir.h>
+
+#include <limits.h>
+
+// We'll use little pixmaps for the buttons to save screen space.
+
+/* XPM */
+static const char * const pen_xpm[] = {
+"12 12 4 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"@ c #808080",
+" . ",
+" .+. ",
+" ..@@.",
+" .+@.. ",
+" .+@@. ",
+" .+@@. ",
+" .+@@. ",
+" .@.@. ",
+" .@@. ",
+" .... ",
+" .. ",
+" "};
+
+
+/* XPM */
+static char * bs_xpm[] = {
+"12 12 5 1",
+" c None",
+". c #333333",
+"+ c #000000",
+"@ c #FFFFFF",
+"# c #666666",
+" ",
+" ",
+" ",
+" . ",
+" ++ ",
+" +@#+++++. ",
+" +@@@@@@@@+ ",
+" +@#+++++. ",
+" ++ ",
+" . ",
+" ",
+" "};
+
+
+/* XPM */
+static char * enter_xpm[] = {
+"12 12 5 1",
+" c None",
+". c #333333",
+"+ c #000000",
+"@ c #FFFFFF",
+"# c #666666",
+" ",
+" .+. ",
+" +@+ ",
+" . +@+ ",
+" ++ +@+ ",
+" +@#++++@+ ",
+" +@@@@@@@@+ ",
+" +@#+++++. ",
+" ++ ",
+" . ",
+" ",
+" "};
+
+
+
+/* XPM */
+static char * help_xpm[] = {
+"12 12 5 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"@ c #666666",
+"# c #333333",
+" ",
+" ... ",
+" .+++. ",
+" .+..@+. ",
+" #.# .+. ",
+" .+. ",
+" .+. ",
+" .+. ",
+" .+. ",
+" #.# ",
+" .+. ",
+" #.# "};
+
+
+/*!
+ \class QIMPenInput qimpeninput.h
+
+ Pen input widget.
+*/
+QIMPenInput::QIMPenInput( QWidget *parent, const char *name, WFlags f )
+ : QFrame( parent, name, f ), helpDlg(0), profile(0)
+{
+ setFrameStyle( Box | Plain );
+
+ profileList.setAutoDelete( true );
+
+ matcher = new QIMPenMatch( this );
+ connect( matcher, SIGNAL(keypress(uint)), this, SLOT(keypress(uint)) );
+ connect( matcher, SIGNAL(erase()), this, SLOT(erase()) );
+
+ QGridLayout *gl = new QGridLayout( this, 5, 2, 1, 0 );
+ gl->setColStretch( 0, 1 );
+
+ wordPicker = new QIMPenWordPick( this );
+ connect( wordPicker, SIGNAL(wordClicked(const QString &)),
+ this, SLOT(wordPicked(const QString &)) );
+ connect( matcher, SIGNAL(matchedCharacters(const QIMPenCharMatchList &)),
+ this, SLOT(matchedCharacters(const QIMPenCharMatchList &)) );
+ connect( matcher, SIGNAL(matchedWords(const QIMPenMatch::MatchWordList&)),
+ wordPicker, SLOT(setWords(const QIMPenMatch::MatchWordList&)) );
+ QFont f("smallsmooth",9);
+ QFontInfo fi( f );
+ wordPicker->setFont( f );
+ wordPicker->setBackgroundColor( white );
+ gl->addMultiCellWidget( wordPicker, 0, 0, 0, 1 );
+ if ( !Global::fixedDawg().root() || !matcher->isWordMatchingEnabled() )
+ wordPicker->hide();
+
+ pw = new QIMPenWidget( this );
+ gl->addMultiCellWidget( pw, 1, 4, 0, 0 );
+
+ int bh = pw->sizeHint().height()/4;
+
+ QPushButton *b = new QPushButton( this );
+ b->setFocusPolicy( NoFocus );
+ b->setPixmap( QPixmap( (const char **)bs_xpm ) );
+ b->setFixedHeight(pw->sizeHint().height()-3*bh); // left-over space goes here
+ b->setAutoRepeat( TRUE );
+ gl->addWidget( b, 1, 1 );
+ connect( b, SIGNAL(clicked()), SLOT(backspace()));
+
+ b = new QPushButton( this );
+ b->setFocusPolicy( NoFocus );
+ b->setPixmap( QPixmap( (const char **)enter_xpm ) );
+ b->setFixedHeight(bh);
+ b->setAutoRepeat( TRUE );
+ gl->addWidget( b, 2, 1 );
+ connect( b, SIGNAL(clicked()), SLOT(enter()));
+
+ helpBtn = new QPushButton( this );
+ helpBtn->setFocusPolicy( NoFocus );
+ helpBtn->setPixmap( QPixmap( (const char **)help_xpm ) );
+ helpBtn->setFixedHeight(bh);
+ gl->addWidget( helpBtn, 3, 1 );
+ connect( helpBtn, SIGNAL(clicked()), SLOT(help()));
+
+ QPixmap pm( (const char **)pen_xpm );
+ setupBtn = new QPushButton( this );
+ setupBtn->setFocusPolicy( NoFocus );
+ setupBtn->setPixmap( pm );
+ setupBtn->setFixedHeight(bh);
+ gl->addWidget( setupBtn, 4, 1 );
+ connect( setupBtn, SIGNAL(clicked()), SLOT(setup()));
+
+ connect( matcher, SIGNAL(removeStroke()), pw, SLOT(removeStroke()) );
+ connect( pw, SIGNAL(changeCharSet( QIMPenCharSet * )),
+ matcher, SLOT(setCharSet( QIMPenCharSet * )) );
+ connect( pw, SIGNAL(changeCharSet( int )),
+ this, SLOT(selectCharSet( int )) );
+ connect( pw, SIGNAL(beginStroke()),
+ matcher, SLOT(beginStroke()) );
+ connect( pw, SIGNAL(stroke( QIMPenStroke * )),
+ this, SLOT(strokeEntered( QIMPenStroke * )) );
+ connect( pw, SIGNAL(stroke( QIMPenStroke * )),
+ matcher, SLOT(strokeEntered( QIMPenStroke * )) );
+
+ shortcutCharSet = 0;
+ currCharSet = 0;
+ setupDlg = 0;
+ profile = 0;
+ mode = Normal;
+
+ loadProfiles();
+}
+
+QIMPenInput::~QIMPenInput()
+{
+ delete (HandwritingHelp*) helpDlg;
+}
+
+QSize QIMPenInput::sizeHint() const
+{
+ int fw = frameWidth();
+ int ps = wordPicker->isHidden() ? 0 : wordPicker->sizeHint().height();
+ return pw->sizeHint() + QSize( fw*2, fw*2+ps );
+}
+
+void QIMPenInput::loadProfiles()
+{
+ profileList.clear();
+ profile = 0;
+ delete shortcutCharSet;
+ shortcutCharSet = new QIMPenCharSet();
+ shortcutCharSet->setTitle( "Shortcut" );
+ QString path = QPEApplication::qpeDir() + "etc/qimpen";
+ QDir dir( path, "*.conf" );
+ QStringList list = dir.entryList();
+ QStringList::Iterator it;
+ for ( it = list.begin(); it != list.end(); ++it ) {
+ QIMPenProfile *p = new QIMPenProfile( path + "/" + *it );
+ profileList.append( p );
+ if ( p->shortcut() ) {
+ QIMPenCharIterator it( p->shortcut()->characters() );
+ for ( ; it.current(); ++it ) {
+ shortcutCharSet->addChar( new QIMPenChar(*it.current()) );
+ }
+ }
+ }
+
+ Config config( "handwriting" );
+ config.setGroup( "Settings" );
+ QString prof = config.readEntry( "Profile", "Default" );
+ selectProfile( prof );
+}
+
+void QIMPenInput::selectProfile( const QString &name )
+{
+ QListIterator<QIMPenProfile> it( profileList );
+ for ( ; it.current(); ++it ) {
+ if ( it.current()->name() == name ) {
+ profile = it.current();
+ break;
+ }
+ }
+
+ if ( !it.current() )
+ return;
+
+ pw->clearCharSets();
+ baseSets.clear();
+
+ matcher->setMultiStrokeTimeout( profile->multiStrokeTimeout() );
+ matcher->setWordMatchingEnabled( profile->matchWords() );
+
+ if ( !Global::fixedDawg().root() || !matcher->isWordMatchingEnabled() )
+ wordPicker->hide();
+ else
+ wordPicker->show();
+
+ if ( profile->uppercase() && profile->style() == QIMPenProfile::BothCases ) {
+ baseSets.append( profile->uppercase() );
+ pw->insertCharSet( profile->uppercase() );
+ }
+
+ if ( profile->lowercase() ) {
+ baseSets.append( profile->lowercase() );
+ pw->insertCharSet( profile->lowercase(), profile->style() == QIMPenProfile::BothCases ? 1 : 2 );
+ }
+
+ if ( profile->numeric() ) {
+ baseSets.append( profile->numeric() );
+ pw->insertCharSet( profile->numeric() );
+ }
+
+ if ( helpDlg )
+ delete (HandwritingHelp*) helpDlg;
+}
+
+void QIMPenInput::wordPicked( const QString &w )
+{
+ int bs = matcher->word().length();
+ for ( int i = 0; i < bs; i++ )
+ keypress( Qt::Key_Backspace << 16 );
+
+ for ( unsigned int i = 0; i < w.length(); i++ )
+ keypress( w[i].unicode() );
+
+ matcher->resetState();
+ wordPicker->clear();
+}
+
+void QIMPenInput::selectCharSet( int idx )
+{
+ if ( mode == Switch ) {
+ //qDebug( "Switch back to normal" );
+ pw->changeCharSet( baseSets.at(currCharSet), currCharSet );
+ mode = Normal;
+ }
+ currCharSet = idx;
+}
+
+void QIMPenInput::beginStroke()
+{
+}
+
+void QIMPenInput::strokeEntered( QIMPenStroke * )
+{
+ pw->greyStroke();
+}
+
+void QIMPenInput::erase()
+{
+ keypress( Qt::Key_Backspace << 16 );
+}
+
+void QIMPenInput::matchedCharacters( const QIMPenCharMatchList &cl )
+{
+ const QIMPenChar *ch = cl.first().penChar;
+ int scan = ch->character() >> 16;
+
+ if ( scan < QIMPenChar::ModeBase )
+ return;
+
+ // We matched a special character...
+
+ switch ( scan ) {
+ case QIMPenChar::Caps:
+ if ( profile->style() == QIMPenProfile::ToggleCases ) {
+// qDebug( "Caps" );
+ if ( mode == SwitchLock ) {
+// qDebug( "Switch to normal" );
+ pw->changeCharSet( profile->lowercase(), currCharSet );
+ mode = Switch;
+ } else {
+// qDebug( "Switch to upper" );
+ pw->changeCharSet( profile->uppercase(), currCharSet );
+ mode = Switch;
+ }
+ }
+ break;
+ case QIMPenChar::CapsLock:
+ if ( profile->style() == QIMPenProfile::ToggleCases ) {
+// qDebug( "CapsLock" );
+ if ( mode == Switch &&
+ baseSets.at(currCharSet) == profile->uppercase() ) {
+// qDebug( "Switch to normal" );
+ pw->changeCharSet( profile->lowercase(), currCharSet );
+ // change our base set back to lower.
+ baseSets.remove( currCharSet );
+ baseSets.insert( currCharSet, profile->lowercase() );
+ mode = Normal;
+ } else {
+// qDebug( "Switch to caps lock" );
+ pw->changeCharSet( profile->uppercase(), currCharSet );
+ // change our base set to upper.
+ baseSets.remove( currCharSet );
+ baseSets.insert( currCharSet, profile->uppercase() );
+ mode = SwitchLock;
+ }
+ }
+ break;
+ case QIMPenChar::Punctuation:
+ if ( profile->punctuation() ) {
+ //qDebug( "Switch to punctuation" );
+ pw->changeCharSet( profile->punctuation(), currCharSet );
+ mode = Switch;
+ }
+ break;
+ case QIMPenChar::Symbol:
+ if ( profile->symbol() ) {
+ //qDebug( "Switch to symbol" );
+ pw->changeCharSet( profile->symbol(), currCharSet );
+ mode = Switch;
+ }
+ break;
+ case QIMPenChar::Shortcut:
+ if ( shortcutCharSet ) {
+ pw->changeCharSet( shortcutCharSet, currCharSet );
+ mode = Switch;
+ }
+ break;
+ case QIMPenChar::Extended:
+ handleExtended( ch->data() );
+ break;
+ }
+}
+
+void QIMPenInput::keypress( uint scan_uni )
+{
+ int scan = scan_uni >> 16;
+ if ( !scan ) {
+ if ( scan_uni >= 'a' && scan_uni <= 'z' ) {
+ scan = Qt::Key_A + scan_uni - 'a';
+ } else if ( scan_uni >= 'A' && scan_uni <= 'Z' ) {
+ scan = Qt::Key_A + scan_uni - 'A';
+ } else if ( scan_uni == ' ' ) {
+ scan = Qt::Key_Space;
+ }
+ }
+
+ switch ( scan ) {
+ case Key_Tab:
+ scan_uni = 9;
+ break;
+ case Key_Return:
+ scan_uni = 13;
+ break;
+ case Key_Backspace:
+ scan_uni = 8;
+ break;
+ case Key_Escape:
+ scan_uni = 27;
+ break;
+ default:
+ break;
+ }
+
+ if ( mode == Switch ) {
+// qDebug( "Switch back to normal" );
+ pw->changeCharSet( baseSets.at(currCharSet), currCharSet );
+ if ( baseSets.at(currCharSet) == profile->uppercase() )
+ mode = SwitchLock;
+ else
+ mode = Normal;
+ }
+
+ emit key( scan_uni&0xffff, scan, 0, true, false );
+ emit key( scan_uni&0xffff, scan, 0, false, false );
+}
+
+void QIMPenInput::handleExtended( const QString &ex )
+{
+ if ( ex.find( "Select" ) == 0 ) {
+ QString set = ex.mid( 7 );
+ qDebug( "Select new profile: %s", set.latin1() );
+ selectProfile( set );
+ }
+}
+
+void QIMPenInput::help()
+{
+ if ( helpDlg )
+ delete (HandwritingHelp*) helpDlg;
+ helpDlg = new HandwritingHelp( profile, 0, 0, WDestructiveClose );
+ helpDlg->showMaximized();
+ helpDlg->show();
+ helpDlg->raise();
+}
+
+/*!
+ Open the setup dialog
+*/
+void QIMPenInput::setup()
+{
+ if ( !setupDlg ) {
+ // We are working with our copy of the char sets here.
+ setupDlg = new QIMPenSetup( profile, 0, 0, TRUE );
+ setupDlg->editor()->selectCharSet( profile->charSets().at(1) ); // lower case? This is crap.
+ if ( qApp->desktop()->width() < 640 )
+ setupDlg->showMaximized();
+ Global::hideInputMethod();
+ setupDlg->exec();
+ loadProfiles();
+ delete setupDlg;
+ setupDlg = 0;
+ Global::showInputMethod();
+ } else {
+ setupDlg->raise();
+ }
+}
+
+void QIMPenInput::backspace()
+{
+ keypress( Qt::Key_Backspace << 16 );
+ matcher->backspace();
+}
+
+void QIMPenInput::enter()
+{
+ keypress( Qt::Key_Return << 16 );
+ matcher->resetState();
+}
+
+
+void QIMPenInput::resetState()
+{
+ matcher->resetState();
+}
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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef _QIMPENINPUT_H_
+#define _QIMPENINPUT_H_
+
+#include "qimpenprofile.h"
+
+#include <qpe/qdawg.h>
+
+#include <qframe.h>
+#include <qlist.h>
+#include <qguardedptr.h>
+
+class QPushButton;
+class QTimer;
+class QIMPenWidget;
+class QIMPenSetup;
+class QIMPenWordPick;
+class QIMPenMatch;
+class HandwritingHelp;
+
+class QIMPenInput : public QFrame
+{
+ Q_OBJECT
+public:
+ QIMPenInput( QWidget *parent = 0, const char *name = 0, WFlags f = 0 );
+ virtual ~QIMPenInput();
+
+ void resetState();
+
+ QSize sizeHint() const;
+
+signals:
+ void key( ushort, ushort, ushort, bool, bool );
+
+private slots:
+ void wordPicked( const QString & );
+ void selectCharSet( int );
+ void beginStroke();
+ void strokeEntered( QIMPenStroke *st );
+ void matchedCharacters( const QIMPenCharMatchList &cl );
+ void keypress( uint scan_uni );
+ void erase();
+ void help();
+ void setup();
+ void backspace();
+ void enter();
+
+private:
+ void loadProfiles();
+ void selectProfile( const QString &name );
+ void handleExtended( const QString & );
+ void updateWordMatch( QIMPenCharMatchList &ml );
+ void matchWords();
+ void scanDict( const QDawg::Node* n, int ipos, const QString& str, int error );
+
+ enum Mode { Normal, Switch, SwitchLock };
+
+private:
+ Mode mode;
+ QRect prefRect;
+ QIMPenWidget *pw;
+ QPushButton *helpBtn;
+ QPushButton *setupBtn;
+ QIMPenSetup *setupDlg;
+ QIMPenMatch *matcher;
+ QGuardedPtr<HandwritingHelp> helpDlg;
+ QIMPenProfile *profile;
+ QList<QIMPenProfile> profileList;
+ QIMPenCharSet *shortcutCharSet;
+ QIMPenCharSetList baseSets;
+ int currCharSet;
+ QIMPenWordPick *wordPicker;
+};
+
+#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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "qimpenmatch.h"
+
+#include <qpe/qdawg.h>
+#include <qpe/global.h>
+
+#include <qapplication.h>
+#include <qtimer.h>
+
+#include <limits.h>
+
+#define ERROR_THRESHOLD 200000
+#define LOOKAHEAD_ERROR 2500
+//#define DEBUG_QIMPEN
+
+QIMPenMatch::QIMPenMatch( QObject *parent, const char *name )
+ : QObject( parent, name )
+{
+ strokes.setAutoDelete( TRUE );
+ wordChars.setAutoDelete( TRUE );
+ wordMatches.setAutoDelete( TRUE );
+
+ multiTimer = new QTimer( this );
+ connect( multiTimer, SIGNAL(timeout()), this, SLOT(endMulti()) );
+
+ prevMatchChar = 0;
+ prevMatchError = INT_MAX;
+ charSet = 0;
+ multiCharSet = 0;
+ multiTimeout = 500;
+ canErase = FALSE;
+ doWordMatching = true;
+}
+
+QIMPenMatch::~QIMPenMatch()
+{
+}
+
+void QIMPenMatch::setCharSet( QIMPenCharSet *cs )
+{
+ charSet = cs;
+}
+
+void QIMPenMatch::beginStroke()
+{
+ multiTimer->stop();
+}
+
+void QIMPenMatch::strokeEntered( QIMPenStroke *st )
+{
+#ifdef DEBUG_QIMPEN
+ qDebug( "---------- new stroke -------------" );
+#endif
+ strokes.append( new QIMPenStroke( *st ) );
+
+ QIMPenChar testChar;
+ QIMPenStrokeIterator it(strokes);
+ for ( ; it.current(); ++it ) {
+ testChar.addStroke( it.current() );
+ }
+
+ QIMPenCharMatchList ml;
+ if ( strokes.count() > 1 && multiCharSet ) {
+#ifdef DEBUG_QIMPEN
+ qDebug( "Matching against multi set" );
+#endif
+ ml = multiCharSet->match( &testChar );
+ } else {
+#ifdef DEBUG_QIMPEN
+ qDebug( "Matching against single set" );
+#endif
+ ml = charSet->match( &testChar );
+ }
+
+ processMatches( ml );
+}
+
+void QIMPenMatch::processMatches( QIMPenCharMatchList &ml )
+{
+#ifdef DEBUG_QIMPEN
+ qDebug( "Entering strokes.count() = %d", strokes.count() );
+#endif
+ QIMPenCharMatch candidate1 = { INT_MAX, 0 };
+ QIMPenCharMatch candidate2 = { INT_MAX, 0 };
+ QIMPenCharMatchList ml2;
+
+ if ( ml.count() ) {//&&
+// ml.first().penChar->penStrokes().count() == strokes.count() ) {
+ candidate1 = ml.first();
+#ifdef DEBUG_QIMPEN
+ qDebug( QString("Candidate1 = %1").arg(QChar(candidate1.penChar->character())) );
+#endif
+ }
+
+ if ( strokes.count() > 1 ) {
+ // See if the last stroke can match a new character
+ QIMPenChar testChar;
+ QIMPenStroke *st = strokes.at(strokes.count()-1);
+ testChar.addStroke( st );
+ ml2 = charSet->match( &testChar );
+ if ( ml2.count() ) {
+ candidate2 = ml2.first();
+#ifdef DEBUG_QIMPEN
+ qDebug( QString("Candidate2 = %1").arg(QChar(candidate2.penChar->character())) );
+#endif
+ }
+ }
+
+ bool eraseLast = FALSE;
+ bool output = TRUE;
+
+ if ( candidate1.penChar && candidate2.penChar ) {
+ // Hmmm, a multi-stroke or a new character are both possible.
+ // Bias the multi-stroke case.
+ if ( QMAX(candidate2.error, prevMatchError)*3 < candidate1.error ) {
+ int i = strokes.count()-1;
+ while ( i-- ) {
+ strokes.removeFirst();
+ emit removeStroke();
+ }
+ prevMatchChar = candidate2.penChar;
+ prevMatchError = candidate2.error;
+ multiCharSet = charSet;
+ ml = ml2;
+#ifdef DEBUG_QIMPEN
+ qDebug( "** Using Candidate2" );
+#endif
+ } else {
+ if ( (prevMatchChar->character() >> 16) != Qt::Key_Backspace &&
+ (prevMatchChar->character() >> 16) < QIMPenChar::ModeBase )
+ eraseLast = TRUE;
+ prevMatchChar = candidate1.penChar;
+ prevMatchError = candidate1.error;
+#ifdef DEBUG_QIMPEN
+ qDebug( "** Using Candidate1, with erase" );
+#endif
+ }
+ } else if ( candidate1.penChar ) {
+ if ( strokes.count() != 1 )
+ eraseLast = TRUE;
+ else
+ multiCharSet = charSet;
+ prevMatchChar = candidate1.penChar;
+ prevMatchError = candidate1.error;
+#ifdef DEBUG_QIMPEN
+ qDebug( "** Using Candidate1" );
+#endif
+ } else if ( candidate2.penChar ) {
+ int i = strokes.count()-1;
+ while ( i-- ) {
+ strokes.removeFirst();
+ emit removeStroke();
+ }
+ prevMatchChar = candidate2.penChar;
+ prevMatchError = candidate2.error;
+ multiCharSet = charSet;
+ ml = ml2;
+#ifdef DEBUG_QIMPEN
+ qDebug( "** Using Candidate2" );
+#endif
+ } else {
+ if ( !ml.count() ) {
+#ifdef DEBUG_QIMPEN
+ qDebug( "** Failed" );
+#endif
+ canErase = FALSE;
+ } else {
+#ifdef DEBUG_QIMPEN
+ qDebug( "Need more strokes" );
+#endif
+ if ( strokes.count() == 1 )
+ canErase = FALSE;
+ multiCharSet = charSet;
+ }
+ output = FALSE;
+ emit noMatch();
+ }
+
+ if ( eraseLast && canErase ) {
+#ifdef DEBUG_QIMPEN
+ qDebug( "deleting last" );
+#endif
+ emit erase();
+ wordChars.removeLast();
+ wordEntered.truncate( wordEntered.length() - 1 );
+ }
+
+ if ( output ) {
+ emit matchedCharacters( ml );
+ uint code = prevMatchChar->character() >> 16;
+ if ( code < QIMPenChar::ModeBase ) {
+ updateWordMatch( ml );
+ emit keypress( prevMatchChar->character() );
+ }
+ canErase = TRUE;
+ }
+
+ if ( strokes.count() )
+ multiTimer->start( multiTimeout, TRUE );
+}
+
+void QIMPenMatch::updateWordMatch( QIMPenCharMatchList &ml )
+{
+ if ( !ml.count() || !doWordMatching )
+ return;
+ int ch = ml.first().penChar->character();
+ QChar qch( ch );
+ int code = ch >> 16;
+ if ( qch.isPunct() || qch.isSpace() ||
+ code == Qt::Key_Enter || code == Qt::Key_Return ||
+ code == Qt::Key_Tab || code == Qt::Key_Escape ) {
+// qDebug( "Word Matching: Clearing word" );
+ wordChars.clear();
+ wordMatches.clear();
+ wordEntered = QString();
+ } else if ( code == Qt::Key_Backspace ) {
+ //qDebug( "Word Matching: Handle backspace" );
+ wordChars.removeLast();
+ wordEntered.truncate( wordEntered.length() - 1 );
+ matchWords();
+ } else {
+ QIMPenChar *matchCh;
+
+ wordChars.append( new QIMPenCharMatchList() );
+ wordEntered += ml.first().penChar->character();
+
+ QIMPenCharMatchList::Iterator it;
+ for ( it = ml.begin(); it != ml.end(); ++it ) {
+ matchCh = (*it).penChar;
+
+ if ( matchCh->penStrokes().count() == strokes.count() ) {
+ QChar ch(matchCh->character());
+ if ( !ch.isPunct() && !ch.isSpace() ) {
+ wordChars.last()->append( QIMPenCharMatch( (*it) ) );
+ }
+ }
+ }
+ matchWords();
+ }
+ if ( !wordMatches.count() || wordMatches.getFirst()->word != wordEntered )
+ wordMatches.prepend( new MatchWord( wordEntered, 0 ) );
+ emit matchedWords( wordMatches );
+}
+
+void QIMPenMatch::matchWords()
+{
+ if ( wordEntered.length() > 0 ) {
+ // more leaniency if we don't have many matches
+ if ( badMatches < 200 )
+ errorThreshold += (200 - badMatches) * 100;
+ } else
+ errorThreshold = ERROR_THRESHOLD;
+ wordMatches.clear();
+ goodMatches = 0;
+ badMatches = 0;
+ if ( wordChars.count() > 0 ) {
+ maxGuess = (int)wordChars.count() * 2;
+ if ( maxGuess < 3 )
+ maxGuess = 3;
+ QString str;
+ scanDict( Global::fixedDawg().root(), 0, str, 0 );
+/*
+ QListIterator<MatchWord> it( wordMatches);
+ for ( ; it.current(); ++it ) {
+ qDebug( QString("Match word: %1").arg(it.current()->word) );
+ }
+*/
+ }
+ //qDebug( "Possibles: Good %d, total %d", goodMatches, wordMatches.count() );
+ wordMatches.sort();
+}
+
+void QIMPenMatch::scanDict( const QDawg::Node* n, int ipos, const QString& str, int error )
+{
+ if ( !n )
+ return;
+ if ( error / (ipos+1) > errorThreshold )
+ return;
+
+ while (n) {
+ if ( goodMatches > 20 )
+ break;
+ if ( ipos < (int)wordChars.count() ) {
+ int i;
+ QChar testCh = QChar(n->letter());
+ QIMPenCharMatchList::Iterator it;
+ for ( i = 0, it = wordChars.at(ipos)->begin();
+ it != wordChars.at(ipos)->end() && i < 8; ++it, i++ ) {
+ QChar ch( (*it).penChar->character() );
+ if ( ch == testCh || ( !ipos && ch.lower() == testCh.lower() ) ) {
+ int newerr = error + (*it).error;
+ if ( testCh.category() == QChar::Letter_Uppercase )
+ ch = testCh;
+ QString newstr( str + ch );
+ if ( n->isWord() && ipos == (int)wordChars.count() - 1 ) {
+ wordMatches.append( new MatchWord( newstr, newerr ) );
+ goodMatches++;
+ }
+ scanDict( n->jump(), ipos+1, newstr, newerr );
+ }
+ }
+ } else if ( badMatches < 200 && ipos < maxGuess ) {
+ int d = ipos - wordChars.count();
+ int newerr = error + ERROR_THRESHOLD + LOOKAHEAD_ERROR*d;
+ QString newstr( str + n->letter() );
+ if ( n->isWord() ) {
+ wordMatches.append( new MatchWord( newstr, newerr ) );
+ badMatches++;
+ }
+ scanDict( n->jump(), ipos+1, newstr, newerr );
+ }
+ n = n->next();
+ }
+}
+
+void QIMPenMatch::backspace()
+{
+ wordChars.removeLast();
+ wordEntered.truncate( wordEntered.length() - 1 );
+ matchWords();
+ if ( !wordMatches.count() || wordMatches.getFirst()->word != wordEntered )
+ wordMatches.prepend( new MatchWord( wordEntered, 0 ) );
+ emit matchedWords( wordMatches );
+ if ( wordEntered.length() )
+ canErase = TRUE;
+}
+
+void QIMPenMatch::endMulti()
+{
+ int i = strokes.count();
+ while ( i-- )
+ emit removeStroke();
+ strokes.clear();
+ multiCharSet = 0;
+}
+
+void QIMPenMatch::resetState()
+{
+ if ( !wordEntered.isEmpty() ) {
+ wordChars.clear();
+ wordMatches.clear();
+ wordEntered = QString();
+ emit matchedWords( wordMatches );
+ canErase = FALSE;
+ }
+}
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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef _QIMPENMATCH_H_
+#define _QIMPENMATCH_H_
+
+#include "qimpenchar.h"
+
+#include <qpe/qdawg.h>
+
+#include <qlist.h>
+
+class QTimer;
+class QIMPenWidget;
+class QIMPenSetup;
+class QIMPenWordPick;
+
+class QIMPenMatch : public QObject
+{
+ Q_OBJECT
+public:
+ QIMPenMatch( QObject *parent=0, const char *name=0 );
+ virtual ~QIMPenMatch();
+
+ void resetState();
+ void backspace();
+ void setMultiStrokeTimeout( int t ) { multiTimeout = t; }
+
+ const QString &word() const { return wordEntered; }
+
+ void setWordMatchingEnabled( bool e ) { doWordMatching = e; }
+ bool isWordMatchingEnabled() const { return doWordMatching; }
+
+ struct MatchWord {
+ MatchWord( const QString &w, int e ) { word = w; error = e; }
+ QString word;
+ int error;
+ };
+
+ class MatchWordList : public QList<MatchWord>
+ {
+ public:
+ int compareItems( QCollection::Item item1, QCollection::Item item2 ) {
+ MatchWord *m1 = (MatchWord *)item1;
+ MatchWord *m2 = (MatchWord *)item2;
+ return m1->error - m2->error;
+ }
+ };
+
+public slots:
+ void setCharSet( QIMPenCharSet * );
+ void beginStroke();
+ void strokeEntered( QIMPenStroke *st );
+
+signals:
+ void erase();
+ void noMatch();
+ void removeStroke();
+ void keypress( uint ch );
+ void matchedCharacters( const QIMPenCharMatchList & );
+ void matchedWords( const QIMPenMatch::MatchWordList & );
+
+protected slots:
+ void processMatches( QIMPenCharMatchList &ml );
+ void endMulti();
+
+protected:
+ void updateWordMatch( QIMPenCharMatchList &ml );
+ void matchWords();
+ void scanDict( const QDawg::Node* n, int ipos, const QString& str, int error );
+
+ QList<QIMPenStroke> strokes;
+ QIMPenChar *prevMatchChar;
+ int prevMatchError;
+ QIMPenCharSet *charSet;
+ QIMPenCharSet *multiCharSet;
+ QList<QIMPenCharMatchList> wordChars;
+ MatchWordList wordMatches;
+ QString wordEntered;
+ bool doWordMatching;
+ bool canErase;
+ int errorThreshold;
+ int goodMatches;
+ int badMatches;
+ int maxGuess;
+ QTimer *multiTimer;
+ int multiTimeout;
+};
+
+#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 @@
+<!DOCTYPE UI><UI>
+<class>QIMPenPrefBase</class>
+<widget>
+ <class>QWidget</class>
+ <property stdset="1">
+ <name>name</name>
+ <cstring>QIMPenPrefBase</cstring>
+ </property>
+ <property stdset="1">
+ <name>geometry</name>
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>247</width>
+ <height>280</height>
+ </rect>
+ </property>
+ <property stdset="1">
+ <name>caption</name>
+ <string>Form1</string>
+ </property>
+ <vbox>
+ <property stdset="1">
+ <name>margin</name>
+ <number>11</number>
+ </property>
+ <property stdset="1">
+ <name>spacing</name>
+ <number>6</number>
+ </property>
+ <widget>
+ <class>QLabel</class>
+ <property stdset="1">
+ <name>name</name>
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property stdset="1">
+ <name>text</name>
+ <string>Multi-stroke character timeout:</string>
+ </property>
+ </widget>
+ <widget>
+ <class>QLayoutWidget</class>
+ <property stdset="1">
+ <name>name</name>
+ <cstring>Layout2</cstring>
+ </property>
+ <hbox>
+ <property stdset="1">
+ <name>margin</name>
+ <number>0</number>
+ </property>
+ <property stdset="1">
+ <name>spacing</name>
+ <number>6</number>
+ </property>
+ <widget>
+ <class>QSlider</class>
+ <property stdset="1">
+ <name>name</name>
+ <cstring>multiStrokeSlider</cstring>
+ </property>
+ <property stdset="1">
+ <name>minValue</name>
+ <number>250</number>
+ </property>
+ <property stdset="1">
+ <name>maxValue</name>
+ <number>1000</number>
+ </property>
+ <property stdset="1">
+ <name>lineStep</name>
+ <number>10</number>
+ </property>
+ <property stdset="1">
+ <name>pageStep</name>
+ <number>50</number>
+ </property>
+ <property stdset="1">
+ <name>value</name>
+ <number>500</number>
+ </property>
+ <property stdset="1">
+ <name>orientation</name>
+ <enum>Horizontal</enum>
+ </property>
+ <property stdset="1">
+ <name>tickmarks</name>
+ <enum>Right</enum>
+ </property>
+ </widget>
+ <widget>
+ <class>QLabel</class>
+ <property stdset="1">
+ <name>name</name>
+ <cstring>multiStrokeLabel</cstring>
+ </property>
+ <property stdset="1">
+ <name>minimumSize</name>
+ <size>
+ <width>45</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property stdset="1">
+ <name>text</name>
+ <string>ms</string>
+ </property>
+ <property stdset="1">
+ <name>alignment</name>
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property>
+ <name>hAlign</name>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget>
+ <class>QButtonGroup</class>
+ <property stdset="1">
+ <name>name</name>
+ <cstring>inputStyle</cstring>
+ </property>
+ <property stdset="1">
+ <name>title</name>
+ <string>Input areas displayed</string>
+ </property>
+ <vbox>
+ <property stdset="1">
+ <name>margin</name>
+ <number>11</number>
+ </property>
+ <property stdset="1">
+ <name>spacing</name>
+ <number>6</number>
+ </property>
+ <widget>
+ <class>QRadioButton</class>
+ <property stdset="1">
+ <name>name</name>
+ <cstring>bothCasesRadio</cstring>
+ </property>
+ <property stdset="1">
+ <name>text</name>
+ <string>Upper and lower case areas</string>
+ </property>
+ </widget>
+ <widget>
+ <class>QRadioButton</class>
+ <property stdset="1">
+ <name>name</name>
+ <cstring>toggleCaseRadio</cstring>
+ </property>
+ <property stdset="1">
+ <name>text</name>
+ <string>Lower case (toggle Upper case)</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property>
+ <name>name</name>
+ <cstring>Spacer2</cstring>
+ </property>
+ <property stdset="1">
+ <name>orientation</name>
+ <enum>Vertical</enum>
+ </property>
+ <property stdset="1">
+ <name>sizeType</name>
+ <enum>Expanding</enum>
+ </property>
+ <property>
+ <name>sizeHint</name>
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+</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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "qimpencombining.h"
+#include "qimpenprofile.h"
+
+#include <qpe/qpeapplication.h>
+#include <qpe/config.h>
+#include <qpe/global.h>
+
+
+QIMPenProfile::QIMPenProfile( const QString &fn )
+ : filename( fn )
+{
+ sets.setAutoDelete( true );
+
+ Config config( filename, Config::File );
+ config.setGroup( "Handwriting" );
+
+ pname = config.readEntry( "Name" );
+ pdesc = config.readEntry( "Description" );
+
+ tstyle = config.readBoolEntry( "CanSelectStyle", false );
+
+ wordMatch = config.readBoolEntry( "MatchWords", true );
+
+ config.setGroup( "Settings" );
+
+ pstyle = BothCases;
+ QString s = config.readEntry( "Style", "BothCases" );
+ if ( s == "ToggleCases" )
+ pstyle = ToggleCases;
+
+ msTimeout = config.readNumEntry( "MultiTimeout", 500 );
+
+ // Read user configuration
+ Config usrConfig( userConfig() );
+ usrConfig.setGroup( "Settings" );
+ msTimeout = usrConfig.readNumEntry( "MultiTimeout", msTimeout );
+
+ if ( tstyle && usrConfig.hasKey( "Style" ) ) {
+ pstyle = BothCases;
+ QString s = usrConfig.readEntry( "Style", "BothCases" );
+ if ( s == "ToggleCases" )
+ pstyle = ToggleCases;
+ }
+}
+
+void QIMPenProfile::setStyle( Style s )
+{
+ if ( tstyle && s != pstyle ) {
+ pstyle = s;
+ Config config( userConfig() );
+ config.setGroup( "Settings" );
+ QString s = pstyle == ToggleCases ? "ToggleCases" : "BothCases";
+ config.writeEntry( "Style", s );
+ }
+}
+
+void QIMPenProfile::setMultiStrokeTimeout( int t )
+{
+ if ( t != msTimeout ) {
+ msTimeout = t;
+ Config config( userConfig() );
+ config.setGroup( "Settings" );
+ config.writeEntry( "MultiTimeout", msTimeout );
+ }
+}
+
+QString QIMPenProfile::userConfig()
+{
+ QString un = filename;
+ int pos = un.findRev( '/' );
+ if ( pos >= 0 )
+ un = un.mid( pos + 1 );
+ pos = un.find( '.' );
+ if ( pos > 0 )
+ un.truncate( pos );
+
+ un = "handwriting-" + un;
+
+ return un;
+}
+
+void QIMPenProfile::loadData()
+{
+ Config config( filename, Config::File );
+ config.setGroup( "CharSets" );
+
+ QString baseDir = QPEApplication::qpeDir();
+ baseDir += "/etc/";
+ // accents
+ QIMPenCombining *combining = 0;
+ QString s = config.readEntry( "Combining" );
+ if ( !s.isEmpty() ) {
+ combining = new QIMPenCombining( baseDir + "qimpen/" + s );
+ if ( combining->isEmpty() ) {
+ delete combining;
+ combining = 0;
+ }
+ }
+ // uppercase latin1
+ QIMPenCharSet *cs = 0;
+ s = config.readEntry( "Uppercase" );
+ if ( !s.isEmpty() ) {
+ cs = new QIMPenCharSet( baseDir + "qimpen/" + s );
+ cs->load( Global::applicationFileName("qimpen",s), QIMPenCharSet::User );
+ if ( !cs->isEmpty() ) {
+ if ( combining )
+ combining->addCombined( cs );
+ sets.append( cs );
+ } else {
+ delete cs;
+ }
+ }
+ // lowercase latin1
+ s = config.readEntry( "Lowercase" );
+ if ( !s.isEmpty() ) {
+ cs = new QIMPenCharSet( baseDir + "qimpen/" + s );
+ cs->load( Global::applicationFileName("qimpen",s), QIMPenCharSet::User );
+ if ( !cs->isEmpty() ) {
+ if ( combining )
+ combining->addCombined( cs );
+ sets.append( cs );
+ } else {
+ delete cs;
+ }
+ }
+ // numeric (may comtain punctuation and symbols)
+ s = config.readEntry( "Numeric" );
+ if ( !s.isEmpty() ) {
+ cs = new QIMPenCharSet( baseDir + "qimpen/" + s );
+ cs->load( Global::applicationFileName("qimpen",s), QIMPenCharSet::User );
+ if ( !cs->isEmpty() ) {
+ sets.append( cs );
+ } else {
+ delete cs;
+ }
+ }
+ // punctuation
+ s = config.readEntry( "Punctuation" );
+ if ( !s.isEmpty() ) {
+ cs = new QIMPenCharSet( baseDir + "qimpen/" + s );
+ cs->load( Global::applicationFileName("qimpen",s), QIMPenCharSet::User );
+ if ( !cs->isEmpty() ) {
+ sets.append( cs );
+ } else {
+ delete cs;
+ }
+ }
+ // symbol
+ s = config.readEntry( "Symbol" );
+ if ( !s.isEmpty() ) {
+ cs = new QIMPenCharSet( baseDir + "qimpen/" + s );
+ cs->load( Global::applicationFileName("qimpen",s), QIMPenCharSet::User );
+ if ( !cs->isEmpty() ) {
+ sets.append( cs );
+ } else {
+ delete cs;
+ }
+ }
+ // shortcut
+ s = config.readEntry( "Shortcut" );
+ if ( !s.isEmpty() ) {
+ cs = new QIMPenCharSet( baseDir + "qimpen/" + s );
+ cs->load( Global::applicationFileName("qimpen",s), QIMPenCharSet::User );
+ if ( !cs->isEmpty() ) {
+ sets.append( cs );
+ } else {
+ delete cs;
+ }
+ }
+
+ if ( combining )
+ delete combining;
+}
+
+QIMPenCharSet *QIMPenProfile::uppercase()
+{
+ return find( QIMPenCharSet::Upper );
+}
+
+QIMPenCharSet *QIMPenProfile::lowercase()
+{
+ return find( QIMPenCharSet::Lower );
+}
+
+QIMPenCharSet *QIMPenProfile::numeric()
+{
+ return find( QIMPenCharSet::Numeric );
+}
+
+QIMPenCharSet *QIMPenProfile::punctuation()
+{
+ return find( QIMPenCharSet::Punctuation );
+}
+
+QIMPenCharSet *QIMPenProfile::symbol()
+{
+ return find( QIMPenCharSet::Symbol );
+}
+
+QIMPenCharSet *QIMPenProfile::shortcut()
+{
+ return find( QIMPenCharSet::Shortcut );
+}
+
+QIMPenCharSetList &QIMPenProfile::charSets()
+{
+ if ( sets.isEmpty() )
+ loadData();
+ return sets;
+}
+
+QIMPenCharSet *QIMPenProfile::find( QIMPenCharSet::Type t )
+{
+ if ( sets.isEmpty() )
+ loadData();
+ QIMPenCharSetIterator it( sets );
+ for ( ; it.current(); ++it ) {
+ if ( it.current()->type() == t )
+ return it.current();
+ }
+
+ return 0;
+}
+
+
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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QIMPENPROFILE_H_
+#define QIMPENPROFILE_H_
+
+#include "qimpenchar.h"
+
+class QIMPenProfile
+{
+public:
+ QIMPenProfile( const QString &fn );
+
+ const QString &name() const { return pname; }
+ const QString &description() const { return pdesc; }
+
+ enum Style { ToggleCases, BothCases };
+ Style style() const { return pstyle; }
+ void setStyle( Style s );
+
+ bool canSelectStyle() const { return tstyle; }
+
+ int multiStrokeTimeout() const { return msTimeout; }
+ void setMultiStrokeTimeout( int t );
+
+ bool matchWords() const { return wordMatch; }
+
+ QIMPenCharSet *uppercase();
+ QIMPenCharSet *lowercase();
+ QIMPenCharSet *numeric();
+ QIMPenCharSet *punctuation();
+ QIMPenCharSet *symbol();
+ QIMPenCharSet *shortcut();
+ QIMPenCharSet *find( QIMPenCharSet::Type t );
+
+ QIMPenCharSetList &charSets();
+
+private:
+ QString userConfig();
+ void loadData();
+
+private:
+ QIMPenCharSetList sets;
+ QString filename;
+ QString pname;
+ QString pdesc;
+ Style pstyle;
+ bool tstyle;
+ int msTimeout;
+ bool wordMatch;
+};
+
+#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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "qimpenwidget.h"
+#include "qimpenprefbase.h"
+#include "qimpensetup.h"
+
+#include <qpe/qpeapplication.h>
+#include <qpe/config.h>
+
+#include <qcombobox.h>
+#include <qlistbox.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+#include <qbuttongroup.h>
+#include <qslider.h>
+#include <qtabwidget.h>
+#include <qdir.h>
+#include <qmessagebox.h>
+
+
+/* XPM */
+static const char * const left_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ",
+" . ",
+" .. ",
+" ... ",
+" .... ",
+" ..... ",
+" ...... ",
+" ..... ",
+" .... ",
+" ... ",
+" .. ",
+" . ",
+" ",
+" "};
+
+
+/* XPM */
+static const char * const right_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ",
+" . ",
+" .. ",
+" ... ",
+" .... ",
+" ..... ",
+" ...... ",
+" ..... ",
+" .... ",
+" ... ",
+" .. ",
+" . ",
+" ",
+" "};
+
+
+
+QIMPenSetup::QIMPenSetup( QIMPenProfile *p, QWidget *parent,
+ const char *name, bool modal, int WFlags )
+ : QDialog( parent, name, modal, WFlags ), profileCombo(0), profile(p)
+{
+ setCaption( tr("Setup Handwriting Input") );
+
+ QVBoxLayout *vb = new QVBoxLayout( this );
+
+#if 0
+ profileList.setAutoDelete( true );
+ QHBoxLayout *hb = new QHBoxLayout( vb );
+ hb->setMargin( 6 );
+ QLabel *l = new QLabel( tr("Character Profile:"), this );
+ hb->addWidget( l );
+ profileCombo = new QComboBox( this );
+ connect( profileCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(selectProfile(const QString &)) );
+ hb->addWidget( profileCombo );
+ loadProfiles();
+#else
+ profileList.append( profile );
+#endif
+
+ QTabWidget *tw = new QTabWidget( this );
+ vb->addWidget( tw );
+
+ pref = new QIMPenPrefBase( this );
+ tw->addTab( pref, tr("Preferences") );
+
+ pref->inputStyle->setExclusive( TRUE );
+
+ style = profile->style() == QIMPenProfile::ToggleCases ? 1 : 0;
+ pref->inputStyle->setButton( style );
+ connect( pref->inputStyle, SIGNAL(clicked(int)),
+ this, SLOT(styleClicked(int)) );
+ pref->inputStyle->setEnabled( profile->canSelectStyle() );
+
+ multiTimeout = profile->multiStrokeTimeout();
+ pref->multiStrokeSlider->setValue( multiTimeout );
+ multiTimeoutChanged( multiTimeout );
+ connect( pref->multiStrokeSlider, SIGNAL(valueChanged(int)),
+ this, SLOT(multiTimeoutChanged(int)) );
+
+ edit = new QIMPenEdit( p, tw );
+ tw->addTab( edit, tr("Customize") );
+}
+
+void QIMPenSetup::loadProfiles()
+{
+ QString path = QPEApplication::qpeDir() + "etc/qimpen";
+ QDir dir( path, "*.conf" );
+ QStringList list = dir.entryList();
+ QStringList::Iterator it;
+ for ( it = list.begin(); it != list.end(); ++it ) {
+ QIMPenProfile *p = new QIMPenProfile( path + "/" + *it );
+ profileList.append( p );
+ profileCombo->insertItem( p->name() );
+ if ( p->name() == profile->name() ) {
+ profileCombo->setCurrentItem( profileCombo->count()-1 );
+ profile = p;
+ }
+ }
+}
+
+void QIMPenSetup::styleClicked( int id )
+{
+ style = id;
+}
+
+void QIMPenSetup::multiTimeoutChanged( int v )
+{
+ multiTimeout = v;
+ pref->multiStrokeLabel->setText( tr("%1 ms").arg(v) );
+}
+
+void QIMPenSetup::selectProfile( const QString &p )
+{
+ if ( p == profile->name() )
+ return;
+
+ profile->setStyle( style ? QIMPenProfile::ToggleCases : QIMPenProfile::BothCases );
+ profile->setMultiStrokeTimeout( multiTimeout );
+
+ for ( int i = 0; i < (int)profileList.count(); i++ ) {
+ if ( profileList.at(i)->name() == p ) {
+ profile = profileList.at(i);
+ style = profile->style() == QIMPenProfile::ToggleCases ? 1 : 0;
+ pref->inputStyle->setButton( style );
+ pref->inputStyle->setEnabled( profile->canSelectStyle() );
+ multiTimeout = profile->multiStrokeTimeout();
+ pref->multiStrokeSlider->setValue( multiTimeout );
+ multiTimeoutChanged( multiTimeout );
+ edit->setProfile( profile );
+ break;
+ }
+ }
+}
+
+void QIMPenSetup::accept()
+{
+ profile->setStyle( style ? QIMPenProfile::ToggleCases : QIMPenProfile::BothCases );
+ profile->setMultiStrokeTimeout( multiTimeout );
+ // Save current profile
+ if ( profileCombo ) {
+ Config config( "handwriting" );
+ config.setGroup( "Settings" );
+ config.writeEntry( "Profile", profileCombo->currentText() );
+ }
+ // Save charsets
+ bool ok = TRUE;
+ for ( int i = 0; i < (int)profileList.count(); i++ ) {
+ QIMPenProfile *prof = profileList.at(i);
+ QIMPenCharSetIterator it(prof->charSets());
+ for ( ; it.current(); ++it ) {
+ if ( !(it.current()->save( QIMPenCharSet::User )) ) {
+ ok = FALSE;
+ break;
+ }
+ }
+ }
+ if ( !ok ) {
+ if ( QMessageBox::critical( 0, tr( "Out of space" ),
+ tr("Unable to save information.\n"
+ "Free up some space\n"
+ "and try again.\n"
+ "\nQuit anyway?"),
+ QMessageBox::Yes|QMessageBox::Escape,
+ QMessageBox::No|QMessageBox::Default )
+ != QMessageBox::No ) {
+ QDialog::accept();
+ }
+ } else {
+ QDialog::accept();
+ }
+}
+
+//---------------------------------------------------------------------------
+
+QIMPenInputCharDlg::QIMPenInputCharDlg( QWidget *parent, const char *name,
+ bool modal, int WFlags)
+ : QDialog( parent, name, modal, WFlags )
+{
+ setCaption( tr("Enter new character") );
+ uni = 0;
+
+ QVBoxLayout *vb = new QVBoxLayout( this, 10 );
+
+ QHBoxLayout *hb = new QHBoxLayout();
+ vb->addLayout( hb );
+
+ QLabel *label = new QLabel( "Character:", this );
+ hb->addWidget( label );
+
+ QComboBox *cb = new QComboBox( TRUE, this );
+ connect( cb, SIGNAL(activated(int)), SLOT(setSpecial(int)) );
+ connect( cb, SIGNAL(textChanged(const QString &)),
+ SLOT(setCharacter(const QString &)) );
+ addSpecial( cb );
+ cb->setEditText( "" );
+ hb->addWidget( cb );
+
+ hb = new QHBoxLayout();
+ vb->addLayout( hb );
+
+ QPushButton *pb = new QPushButton( "OK", this );
+ connect( pb, SIGNAL(clicked()), SLOT(accept()));
+ hb->addWidget( pb );
+ pb = new QPushButton( "Cancel", this );
+ connect( pb, SIGNAL(clicked()), SLOT(reject()));
+ hb->addWidget( pb );
+
+ cb->setFocus();
+}
+
+void QIMPenInputCharDlg::addSpecial( QComboBox *cb )
+{
+ int i = 0;
+ while ( qimpen_specialKeys[i].code != Key_unknown ) {
+ cb->insertItem( qimpen_specialKeys[i].name );
+ i++;
+ }
+}
+
+void QIMPenInputCharDlg::setSpecial( int sp )
+{
+ uni = qimpen_specialKeys[sp].code << 16;
+}
+
+void QIMPenInputCharDlg::setCharacter( const QString &string )
+{
+ uni = string[0].unicode();
+}
+
+//---------------------------------------------------------------------------
+
+class CharListItem : public QListBoxText
+{
+public:
+ CharListItem( const QString &text, uint c )
+ : QListBoxText( text )
+ {
+ _code = c;
+ }
+
+ uint code() const { return _code; }
+
+protected:
+ uint _code;
+};
+
+/*!
+ \class QIMPenEdit qimpensetup.h
+
+ Class to allow users to input totally useless character definitions
+ which could match any number of the default set.
+*/
+
+QIMPenEdit::QIMPenEdit( QIMPenProfile *p, QWidget *parent,
+ const char *name )
+ : QWidget( parent, name ), profile(p)
+{
+ currentChar = 0;
+ currentCode = 0;
+ inputChar = new QIMPenChar();
+
+ QVBoxLayout *tvb = new QVBoxLayout( this, 5 );
+
+ QGridLayout *gl = new QGridLayout( tvb, 4, 2 );
+ gl->setRowStretch( 1, 1 );
+ gl->addRowSpacing( 2, 35 );
+ gl->addRowSpacing( 3, 35 );
+
+ charSetCombo = new QComboBox( this );
+ gl->addMultiCellWidget( charSetCombo, 0, 0, 0, 1 );
+ connect( charSetCombo, SIGNAL(activated(int)), SLOT(selectCharSet(int)));
+ QIMPenCharSetIterator it( profile->charSets() );
+ for ( ; it.current(); ++it ) {
+ charSetCombo->insertItem( it.current()->description() );
+ }
+
+ charList = new QListBox( this );
+ charList->setMinimumHeight( charList->sizeHint().height() );
+ connect( charList, SIGNAL(highlighted(int)), SLOT(selectChar(int)) );
+ gl->addWidget( charList, 1, 0 );
+
+ pw = new QIMPenWidget( this );
+ pw->setFixedHeight( 75 );
+ gl->addMultiCellWidget( pw, 2, 3, 0, 0 );
+ connect( pw, SIGNAL(stroke(QIMPenStroke *)),
+ SLOT(newStroke(QIMPenStroke *)) );
+
+ QVBoxLayout *vb = new QVBoxLayout();
+ gl->addLayout( vb, 1, 1 );
+ newBtn = new QPushButton( tr("New..."), this );
+ connect( newBtn, SIGNAL(clicked()), SLOT(addNewChar()) );
+ vb->addWidget( newBtn );
+
+ addBtn = new QPushButton( tr("Add"), this );
+ connect( addBtn, SIGNAL(clicked()), SLOT(addChar()) );
+ vb->addWidget( addBtn );
+
+ removeBtn = new QPushButton( tr("Remove"), this );
+ connect( removeBtn, SIGNAL(clicked()), SLOT(removeChar()) );
+ vb->addWidget( removeBtn );
+
+ QPushButton *pb = new QPushButton( tr("Default"), this );
+ connect( pb, SIGNAL(clicked()), SLOT(defaultChars()) );
+ vb->addWidget( pb );
+
+ QHBoxLayout *hb = new QHBoxLayout();
+ gl->addLayout( hb, 2, 1 );
+ prevBtn = new QPushButton( this );
+ prevBtn->setPixmap( QPixmap( (const char **)left_xpm ) );
+ connect( prevBtn, SIGNAL(clicked()), SLOT(prevChar()));
+ hb->addWidget( prevBtn );
+
+ nextBtn = new QPushButton( this );
+ nextBtn->setPixmap( QPixmap( (const char **)right_xpm ) );
+ connect( nextBtn, SIGNAL(clicked()), SLOT(nextChar()));
+ hb->addWidget( nextBtn );
+
+ pb = new QPushButton( tr("Clear"), this );
+ connect( pb, SIGNAL(clicked()), SLOT(clearChar()) );
+ gl->addWidget( pb, 3, 1 );
+
+ //--
+#if !defined(Q_WS_QWS)
+ hb = new QHBoxLayout( tvb );
+ pb = new QPushButton( "OK", this );
+ connect( pb, SIGNAL(clicked()), SLOT(accept()) );
+ hb->addWidget( pb );
+
+ pb = new QPushButton( "Cancel", this );
+ connect( pb, SIGNAL(clicked()), SLOT(reject()) );
+ hb->addWidget( pb );
+#endif
+ selectCharSet( 0 );
+ charList->setFocus();
+
+ resize( minimumSize() );
+ enableButtons();
+}
+
+void QIMPenEdit::setProfile( QIMPenProfile *p )
+{
+ profile = p;
+ charSetCombo->clear();
+ QIMPenCharSetIterator it( profile->charSets() );
+ for ( ; it.current(); ++it ) {
+ charSetCombo->insertItem( it.current()->description() );
+ }
+ selectCharSet( 0 );
+ charList->setFocus();
+ enableButtons();
+}
+
+void QIMPenEdit::selectCharSet( QIMPenCharSet *c )
+{
+ int i = 0;
+ QIMPenCharSetIterator it( profile->charSets() );
+ for ( ; it.current(); ++it, i++ ) {
+ if ( it.current() == c ) {
+ charSetCombo->setCurrentItem( i );
+ selectCharSet( i );
+ }
+ }
+}
+
+
+/*!
+ Fill the character list box with the characters. Duplicates are not
+ inserted.
+*/
+void QIMPenEdit::fillCharList()
+{
+ charList->clear();
+ QIMPenCharIterator it( currentSet->characters() );
+ CharListItem *li = 0;
+ for ( ; it.current(); ++it ) {
+ uint ch = it.current()->character();
+ QString n = it.current()->name();
+ if ( !n.isEmpty() )
+ li = new CharListItem( n, ch );
+ if ( li ) {
+ CharListItem *i = (CharListItem *)charList->findItem( li->text() );
+ if ( !i || i->code() != ch ) {
+ charList->insertItem( li );
+ } else {
+ delete li;
+ li = 0;
+ }
+ }
+ }
+ currentChar = 0;
+}
+
+void QIMPenEdit::enableButtons()
+{
+ bool add = !inputChar->isEmpty();
+ newBtn->setEnabled( add );
+ addBtn->setEnabled( add );
+ removeBtn->setEnabled( currentChar );
+}
+
+/*!
+ Find the previous character with the same code as the current one.
+ returns 0 if there is no previous character.
+*/
+QIMPenChar *QIMPenEdit::findPrev()
+{
+ if ( !currentChar )
+ return 0;
+ QIMPenCharIterator it( currentSet->characters() );
+ bool found = FALSE;
+ for ( it.toLast(); it.current(); --it ) {
+ if ( !found && it.current() == currentChar )
+ found = TRUE;
+ else if ( found && it.current()->character() == currentCode &&
+ !it.current()->testFlag( QIMPenChar::Deleted ) ) {
+ return it.current();
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ Find the next character with the same code as the current one.
+ returns 0 if there is no next character.
+*/
+QIMPenChar *QIMPenEdit::findNext()
+{
+ if ( !currentChar )
+ return 0;
+ QIMPenCharIterator it( currentSet->characters() );
+ bool found = FALSE;
+ for ( ; it.current(); ++it ) {
+ if ( !found && it.current() == currentChar )
+ found = TRUE;
+ else if ( found && it.current()->character() == currentCode &&
+ !it.current()->testFlag( QIMPenChar::Deleted ) ) {
+ return it.current();
+ }
+ }
+
+ return 0;
+}
+
+void QIMPenEdit::setCurrentChar( QIMPenChar *pc )
+{
+ currentChar = pc;
+ pw->showCharacter( currentChar );
+ if ( currentChar ) {
+ prevBtn->setEnabled( findPrev() != 0 );
+ nextBtn->setEnabled( findNext() != 0 );
+ }
+}
+
+void QIMPenEdit::prevChar()
+{
+ QIMPenChar *pc = findPrev();
+ if ( pc )
+ setCurrentChar( pc );
+}
+
+void QIMPenEdit::nextChar()
+{
+ QIMPenChar *pc = findNext();
+ if ( pc )
+ setCurrentChar( pc );
+}
+
+void QIMPenEdit::clearChar()
+{
+ inputChar->clear();
+ pw->clear();
+ enableButtons();
+}
+
+void QIMPenEdit::selectChar( int i )
+{
+ currentChar = 0;
+ currentCode = ((CharListItem *)charList->item(i))->code();
+ QIMPenCharIterator it(currentSet->characters() );
+ for ( ; it.current(); ++it ) {
+ if ( it.current()->character() == currentCode &&
+ !it.current()->testFlag( QIMPenChar::Deleted ) ) {
+ setCurrentChar( it.current() );
+ break;
+ }
+ }
+ if ( !it.current() )
+ setCurrentChar( 0 );
+ inputChar->clear();
+}
+
+void QIMPenEdit::selectCharSet( int i )
+{
+ if ( currentSet )
+ pw->removeCharSet( 0 );
+ currentSet = profile->charSets().at( i );
+ fillCharList();
+ pw->insertCharSet( currentSet );
+ inputChar->clear();
+ if ( charList->count() ) {
+ charList->setSelected( 0, TRUE );
+ selectChar(0);
+ }
+}
+
+void QIMPenEdit::addChar()
+{
+ if ( !inputChar->isEmpty() ) {
+ QIMPenChar *pc = new QIMPenChar( *inputChar );
+ pc->setCharacter( currentCode );
+
+ // User characters override all matching system characters.
+ // Copy and mark deleted identical system characters.
+ QIMPenCharIterator it(currentSet->characters() );
+ QIMPenChar *sc = 0;
+ while ( (sc = it.current()) != 0 ) {
+ ++it;
+ if ( sc->character() == currentCode &&
+ sc->testFlag( QIMPenChar::System ) &&
+ !sc->testFlag( QIMPenChar::Deleted ) ) {
+ QIMPenChar *cc = new QIMPenChar( *sc );
+ cc->clearFlag( QIMPenChar::System );
+ currentSet->addChar( cc );
+ sc->setFlag( QIMPenChar::Deleted );
+ }
+ }
+
+ currentSet->addChar( pc );
+ setCurrentChar( pc );
+ inputChar->clear();
+ }
+}
+
+void QIMPenEdit::addNewChar()
+{
+ if ( !inputChar->isEmpty() ) {
+ QIMPenInputCharDlg dlg( 0, 0, TRUE );
+ if ( dlg.exec() ) {
+ currentCode = dlg.unicode();
+ addChar();
+ fillCharList();
+ for ( unsigned int i = 0; i < charList->count(); i++ ) {
+ CharListItem *li = (CharListItem *)charList->item(i);
+ if ( li->code() == dlg.unicode() ) {
+ charList->setSelected( i, TRUE );
+ break;
+ }
+ }
+ }
+ }
+}
+
+void QIMPenEdit::removeChar()
+{
+ if ( currentChar ) {
+ QIMPenChar *pc = findPrev();
+ if ( !pc ) pc = findNext();
+ if ( currentChar->testFlag( QIMPenChar::System ) )
+ currentChar->setFlag( QIMPenChar::Deleted );
+ else
+ currentSet->removeChar( currentChar );
+ setCurrentChar( pc );
+ }
+}
+
+void QIMPenEdit::defaultChars()
+{
+ if ( currentCode ) {
+ currentChar = 0;
+ bool haveSystem = FALSE;
+ QIMPenCharIterator it(currentSet->characters() );
+ for ( ; it.current(); ++it ) {
+ if ( it.current()->character() == currentCode &&
+ it.current()->testFlag( QIMPenChar::System ) ) {
+ haveSystem = TRUE;
+ break;
+ }
+ }
+ if ( haveSystem ) {
+ it.toFirst();
+ while ( it.current() ) {
+ QIMPenChar *pc = it.current();
+ ++it;
+ if ( pc->character() == currentCode ) {
+ if ( pc->testFlag( QIMPenChar::System ) ) {
+ pc->clearFlag( QIMPenChar::Deleted );
+ if ( !currentChar )
+ currentChar = pc;
+ } else {
+ currentSet->removeChar( pc );
+ }
+ }
+ }
+ setCurrentChar( currentChar );
+ }
+ }
+}
+
+void QIMPenEdit::newStroke( QIMPenStroke *st )
+{
+ inputChar->addStroke( st );
+ enableButtons();
+}
+
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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include <qdialog.h>
+#include <qlist.h>
+#include "qimpenprofile.h"
+
+class QListBox;
+class QPushButton;
+class QComboBox;
+class QIMPenWidget;
+class QIMPenEdit;
+class QIMPenPrefBase;
+
+class QIMPenSetup : public QDialog
+{
+ Q_OBJECT
+public:
+ QIMPenSetup( QIMPenProfile *p, QWidget *parent=0,
+ const char *name=0, bool modal=FALSE, int WFlags=0 );
+
+ QIMPenEdit *editor() { return edit; }
+
+protected:
+ void loadProfiles();
+ virtual void accept();
+
+private slots:
+ void styleClicked( int );
+ void multiTimeoutChanged( int );
+ void selectProfile( const QString &p );
+
+private:
+ QComboBox *profileCombo;
+ QIMPenEdit *edit;
+ QIMPenPrefBase *pref;
+ int style;
+ int multiTimeout;
+ QIMPenProfile *profile;
+ QList<QIMPenProfile> profileList;
+};
+
+class QIMPenInputCharDlg : public QDialog
+{
+ Q_OBJECT
+public:
+ QIMPenInputCharDlg( QWidget *parent = 0, const char *name = 0,
+ bool modal = FALSE, int WFlags = 0 );
+
+ unsigned int unicode() const { return uni; }
+
+protected:
+ void addSpecial( QComboBox *cb );
+
+protected slots:
+ void setSpecial( int sp );
+ void setCharacter( const QString &string );
+
+protected:
+ uint uni;
+};
+
+class QIMPenEdit : public QWidget
+{
+ Q_OBJECT
+public:
+ QIMPenEdit( QIMPenProfile *p, QWidget *parent=0,
+ const char *name=0 );
+
+ void setProfile( QIMPenProfile *p );
+ void selectCharSet( QIMPenCharSet *c );
+
+protected:
+ void fillCharList();
+ void enableButtons();
+ QIMPenChar *findPrev();
+ QIMPenChar *findNext();
+ void setCurrentChar( QIMPenChar * );
+
+protected slots:
+ void prevChar();
+ void nextChar();
+ void clearChar();
+ void selectChar( int );
+ void selectCharSet( int );
+ void addChar();
+ void addNewChar();
+ void removeChar();
+ void defaultChars();
+ void newStroke( QIMPenStroke * );
+
+protected:
+ QIMPenWidget *pw;
+ QComboBox *charSetCombo;
+ QListBox *charList;
+ QPushButton *newBtn;
+ QPushButton *addBtn;
+ QPushButton *removeBtn;
+ QPushButton *prevBtn;
+ QPushButton *nextBtn;
+ uint currentCode;
+ QIMPenChar *currentChar;
+ QIMPenChar *inputChar;
+ QIMPenCharSet *currentSet;
+ QIMPenProfile *profile;
+};
+
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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include <qfile.h>
+#include <qtl.h>
+#include <math.h>
+#include <limits.h>
+#include <qdatastream.h>
+#include "qimpenstroke.h"
+
+#define QIMPEN_CORRELATION_POINTS 25
+//#define DEBUG_QIMPEN
+
+/*!
+ \class QIMPenStroke qimpenstroke.h
+
+ Handles a single stroke. Can calculate closeness of match to
+ another stroke.
+*/
+
+QIMPenStroke::QIMPenStroke()
+{
+}
+
+QIMPenStroke::QIMPenStroke( const QIMPenStroke &st )
+{
+ startPoint = st.startPoint;
+ lastPoint = st.lastPoint;
+ links = st.links.copy();
+}
+
+QIMPenStroke &QIMPenStroke::operator=( const QIMPenStroke &s )
+{
+ clear();
+ //qDebug( "copy strokes %d", s.links.count() );
+ startPoint = s.startPoint;
+ lastPoint = s.lastPoint;
+ links = s.links.copy();
+
+ return *this;
+}
+
+void QIMPenStroke::clear()
+{
+ startPoint = QPoint(0,0);
+ lastPoint = QPoint( 0, 0 );
+ links.resize( 0 );
+ tsig.resize( 0 );
+ dsig.resize( 0 );
+ asig.resize( 0 );
+}
+
+/*!
+ Begin inputting a new stroke.
+*/
+void QIMPenStroke::beginInput( QPoint p )
+{
+ clear();
+ startPoint = p;
+ bounding = QRect();
+ internalAddPoint( p );
+}
+
+/*!
+ Add a point to the stroke's shape.
+ Returns TRUE if the point was successfully added.
+*/
+bool QIMPenStroke::addPoint( QPoint p )
+{
+ if ( links.count() > 500 ) // sanity check (that the user is sane).
+ return FALSE;
+
+ int dx = p.x() - lastPoint.x();
+ int dy = p.y() - lastPoint.y();
+ if ( QABS( dx ) > 1 || QABS( dy ) > 1 ) {
+ // The point is not adjacent to the previous point, so we fill
+ // in with a straight line. Some kind of non-linear
+ // interpolation might be better.
+ int x = lastPoint.x();
+ int y = lastPoint.y();
+ int ix = 1;
+ int iy = 1;
+ if ( dx < 0 ) {
+ ix = -1;
+ dx = -dx;
+ }
+ if ( dy < 0 ) {
+ iy = -1;
+ dy = -dy;
+ }
+ int d = 0;
+ if ( dx < dy ) {
+ d = dx;
+ do {
+ y += iy;
+ d += dx;
+ if ( d > dy ) {
+ x += ix;
+ d -= dy;
+ }
+ internalAddPoint( QPoint( x, y ) );
+ } while ( y != p.y() );
+ } else {
+ d = dy;
+ do {
+ x += ix;
+ d += dy;
+ if ( d > dx ) {
+ y += iy;
+ d -= dx;
+ }
+ internalAddPoint( QPoint( x, y ) );
+ } while ( x != p.x() );
+ }
+ } else {
+ internalAddPoint( p );
+ }
+
+ return TRUE;
+}
+
+/*!
+ Finish inputting a stroke.
+*/
+void QIMPenStroke::endInput()
+{
+ if ( links.count() < 3 ) {
+ QIMPenGlyphLink gl;
+ links.resize(1);
+ gl.dx = 1;
+ gl.dy = 0;
+ links[0] = gl;
+ }
+
+ //qDebug("Points: %d", links.count() );
+}
+
+/*!
+ Return an indicator of the closeness of this stroke to \a pen.
+ Lower value is better.
+*/
+unsigned int QIMPenStroke::match( QIMPenStroke *pen )
+{
+ double lratio;
+
+ if ( links.count() > pen->links.count() )
+ lratio = (links.count()+2) / (pen->links.count()+2);
+ else
+ lratio = (pen->links.count()+2) / (links.count()+2);
+
+ lratio -= 1.0;
+
+ if ( lratio > 2.0 ) {
+#ifdef DEBUG_QIMPEN
+ qDebug( "stroke length too different" );
+#endif
+ return 400000;
+ }
+
+ createSignatures();
+ pen->createSignatures();
+
+ // Starting point offset
+ int vdiff = QABS(startPoint.y() - pen->startPoint.y());
+
+ // Insanely offset?
+ if ( vdiff > 18 ) {
+ return 400000;
+ }
+
+ vdiff -= 4;
+ if ( vdiff < 0 )
+ vdiff = 0;
+
+ // Ending point offset
+ int evdiff = QABS(lastPoint.y() - pen->lastPoint.y());
+ // Insanely offset?
+ if ( evdiff > 20 ) {
+ return 400000;
+ }
+
+ evdiff -= 5;
+ if ( evdiff < 0 )
+ evdiff = 0;
+
+ // do a correlation with the three available signatures.
+ int err1 = INT_MAX;
+ int err2 = INT_MAX;
+ int err3 = INT_MAX;
+
+ // base has extra points at the start and end to enable
+ // correlation of a sliding window with the pen supplied.
+ QArray<int> base = createBase( tsig, 2 );
+ for ( int i = 0; i < 4; i++ ) {
+ int e = calcError( base, pen->tsig, i, TRUE );
+ if ( e < err1 )
+ err1 = e;
+ }
+ if ( err1 > 40 ) { // no need for more matching
+#ifdef DEBUG_QIMPEN
+ qDebug( "tsig too great: %d", err1 );
+#endif
+ return 400000;
+ }
+
+ // maybe a sliding window is worthwhile for these too.
+ err2 = calcError( dsig, pen->dsig, 0, FALSE );
+ if ( err2 > 100 ) {
+#ifdef DEBUG_QIMPEN
+ qDebug( "dsig too great: %d", err2 );
+#endif
+ return 400000;
+ }
+
+ err3 = calcError( asig, pen->asig, 0, TRUE );
+ if ( err3 > 60 ) {
+#ifdef DEBUG_QIMPEN
+ qDebug( "asig too great: %d", err3 );
+#endif
+ return 400000;
+ }
+
+ // Some magic numbers here - the addition reduces the weighting of
+ // the error and compensates for the different error scales. I
+ // consider the tangent signature to be the best indicator, so it
+ // has the most weight. This ain't rocket science.
+ // Basically, these numbers are the tuning factors.
+ unsigned int err = (err1+1) * ( err2 + 60 ) * ( err3 + 20 ) +
+ vdiff * 1000 + evdiff * 500 +
+ (unsigned int)(lratio * 5000.0);
+
+#ifdef DEBUG_QIMPEN
+ qDebug( "err %d ( %d, %d, %d, %d)", err, err1, err2, err3, vdiff );
+#endif
+
+ return err;
+}
+
+/*!
+ Return the bounding rect of this stroke.
+*/
+QRect QIMPenStroke::boundingRect()
+{
+ if ( !bounding.isValid() ) {
+ int x = startPoint.x();
+ int y = startPoint.y();
+ bounding = QRect( x, y, 1, 1 );
+
+ for ( unsigned i = 0; i < links.count(); i++ ) {
+ x += links[i].dx;
+ y += links[i].dy;
+ if ( x < bounding.left() )
+ bounding.setLeft( x );
+ if ( x > bounding.right() )
+ bounding.setRight( x );
+ if ( y < bounding.top() )
+ bounding.setTop( y );
+ if ( y > bounding.bottom() )
+ bounding.setBottom( y );
+ }
+ }
+
+ return bounding;
+}
+
+
+/*!
+ Perform a correlation of the supplied arrays. \a base should have
+ win.count() + 2 * off points to enable sliding \a win over the
+ \a base data. If \a t is TRUE, the comparison takes into account
+ the circular nature of the angular data.
+ Returns the best (lowest error) match.
+*/
+
+int QIMPenStroke::calcError( const QArray<int> &base,
+ const QArray<int> &win, int off, bool t )
+{
+ int err = 0;
+
+ for ( unsigned i = 0; i < win.count(); i++ ) {
+ int d = QABS( base[i+off] - win[i] );
+ if ( t && d > 128 )
+ d -= 256;
+ err += QABS( d );
+ }
+
+ err /= win.count();
+
+ return err;
+}
+
+/*!
+ Creates signatures used in matching if not already created.
+*/
+void QIMPenStroke::createSignatures()
+{
+ if ( tsig.isEmpty() )
+ createTanSignature();
+ if ( asig.isEmpty() )
+ createAngleSignature();
+ if ( dsig.isEmpty() )
+ createDistSignature();
+}
+
+/*!
+ Create a signature of the tangents to the user's stroke.
+*/
+void QIMPenStroke::createTanSignature()
+{
+ int dist = 5; // number of points to include in calculation
+ if ( (int)links.count() <= dist ) {
+ tsig.resize(1);
+ int dx = 0;
+ int dy = 0;
+ for ( unsigned j = 0; j < links.count(); j++ ) {
+ dx += links[j].dx;
+ dy += links[j].dy;
+ }
+ tsig[0] = arcTan( dy, dx );
+ } else {
+ tsig.resize( (links.count()-dist+1) / 2 );
+ int idx = 0;
+ for ( unsigned i = 0; i < links.count() - dist; i += 2 ) {
+ int dx = 0;
+ int dy = 0;
+ for ( int j = 0; j < dist; j++ ) {
+ dx += links[i+j].dx;
+ dy += links[i+j].dy;
+ }
+ tsig[idx++] = arcTan( dy, dx );
+ }
+ }
+
+ tsig = scale( tsig, QIMPEN_CORRELATION_POINTS, TRUE );
+// smooth(tsig);
+}
+
+/*!
+ Create a signature of the change in angle.
+*/
+void QIMPenStroke::createAngleSignature()
+{
+ QPoint c = calcCenter();
+
+ int dist = 3; // number of points to include in calculation
+ if ( (int)links.count() <= dist ) {
+ asig.resize(1);
+ asig[0] = 1;
+ } else {
+ asig.resize( links.count() );
+ QPoint current(0, 0);
+ int idx = 0;
+ for ( unsigned i = 0; i < links.count(); i++ ) {
+ int dx = c.x() - current.x();
+ int dy = c.y() - current.y();
+ int md = QMAX( QABS(dx), QABS(dy) );
+ if ( md > 5 ) {
+ dx = dx * 5 / md;
+ dy = dy * 5 / md;
+ }
+ asig[idx++] = arcTan( dy, dx );
+ current += QPoint( links[i].dx, links[i].dy );
+ }
+ }
+
+ asig = scale( asig, QIMPEN_CORRELATION_POINTS, TRUE );
+
+/*
+ if ( tsig.isEmpty() )
+ createTanSignature();
+
+ if ( tsig.count() < 5 ) {
+ asig.resize( 1 );
+ asig[0] = 0;
+ } else {
+ asig.resize( tsig.count() - 5 );
+
+ for ( unsigned i = 0; i < asig.count(); i++ ) {
+ asig[i] = QABS(tsig[i] - tsig[i+5]);
+ }
+ }
+*/
+}
+
+/*!
+ Create a signature of the distance from the char's center of gravity
+ to its points.
+*/
+void QIMPenStroke::createDistSignature()
+{
+ dsig.resize( (links.count()+1)/2 );
+ QPoint c = calcCenter();
+ QPoint pt( 0, 0 );
+
+ int minval = INT_MAX;
+ int maxval = 0;
+ int idx = 0;
+ for ( unsigned i = 0; i < links.count(); i += 2 ) {
+ int dx = c.x() - pt.x();
+ int dy = c.y() - pt.y();
+ if ( dx == 0 && dy == 0 )
+ dsig[idx] = 0;
+ else
+ dsig[idx] = dx*dx + dy*dy;
+
+ if ( dsig[idx] > maxval )
+ maxval = dsig[idx];
+ if ( dsig[idx] < minval )
+ minval = dsig[idx];
+ pt.rx() += links[i].dx;
+ pt.ry() += links[i].dy;
+ idx++;
+ }
+
+ // normalise 0-255
+ int div = maxval - minval;
+ if ( div == 0 ) div = 1;
+ for ( unsigned i = 0; i < dsig.count(); i++ ) {
+ dsig[i] = (dsig[i] - minval ) * 255 / div;
+ }
+
+ dsig = scale( dsig, QIMPEN_CORRELATION_POINTS );
+}
+
+
+/*!
+ Scale the points in a array to \a count points.
+ This is braindead at the moment (no smooth scaling) and fixing this is
+ probably one of the simpler ways to improve performance.
+*/
+QArray<int> QIMPenStroke::scale( const QArray<int> &s, unsigned count, bool t )
+{
+ QArray<int> d(count);
+
+ unsigned si = 0;
+ if ( s.count() > count ) {
+ unsigned next = 0;
+ for ( unsigned i = 0; i < count; i++ ) {
+ next = (i+1) * s.count() / count;
+ int maxval = 0;
+ if ( t ) {
+ for ( unsigned j = si; j < next; j++ ) {
+ maxval = s[j] > maxval ? s[j] : maxval;
+ }
+ }
+ int sum = 0;
+ for ( unsigned j = si; j < next; j++ ) {
+ if ( t && maxval - s[j] > 128 )
+ sum += 256;
+ sum += s[j];
+ }
+ d[i] = sum / (next-si);
+ if ( t && d[i] > 256 )
+ d[i] %= 256;
+ si = next;
+ }
+ } else {
+ for ( unsigned i = 0; i < count; i++ ) {
+ si = i * s.count() / count;
+ d[i] = s[si];
+ }
+ }
+
+ return d;
+}
+
+/*!
+ Add another point to the stroke's shape.
+*/
+void QIMPenStroke::internalAddPoint( QPoint p )
+{
+ if ( p == lastPoint )
+ return;
+
+ if ( !lastPoint.isNull() ) {
+ QIMPenGlyphLink gl;
+ gl.dx = p.x() - lastPoint.x();
+ gl.dy = p.y() - lastPoint.y();
+ links.resize( links.size() + 1 ); //### resize by 1 is bad
+ links[links.size() - 1] = gl;
+ }
+
+ lastPoint = p;
+ bounding = QRect();
+}
+
+/*!
+ Calculate the center of gravity of the stroke.
+*/
+QPoint QIMPenStroke::calcCenter()
+{
+ QPoint pt( 0, 0 );
+ int ax = 0;
+ int ay = 0;
+
+ for ( unsigned i = 0; i < links.count(); i++ ) {
+ pt.rx() += links[i].dx;
+ pt.ry() += links[i].dy;
+ ax += pt.x();
+ ay += pt.y();
+ }
+
+ ax /= (int)links.count();
+ ay /= (int)links.count();
+
+ return QPoint( ax, ay );
+}
+
+/*!
+ Calculate the arctan of the lengths supplied.
+ The angle returned is in the range 0-255.
+ \a dy and \a dx MUST be in the range 0-5 - I dont even check :-P
+*/
+int QIMPenStroke::arcTan( int dy, int dx )
+{
+ if ( dx == 0 ) {
+ if ( dy >= 0 )
+ return 64;
+ else
+ return 192;
+ }
+
+ if ( dy == 0 ) {
+ if ( dx >= 0 )
+ return 0;
+ else
+ return 128;
+ }
+
+ static int table[5][5] = {
+ { 32, 19, 13, 10, 8 },
+ { 45, 32, 24, 19, 16 },
+ { 51, 40, 32, 26, 22 },
+ { 54, 45, 37, 32, 27 },
+ { 56, 49, 42, 37, 32 } };
+
+ if ( dy > 0 ) {
+ if ( dx > 0 )
+ return table[dy-1][dx-1];
+ else
+ return 128 - table[dy-1][QABS(dx)-1];
+ } else {
+ if ( dx > 0 )
+ return 256 - table[QABS(dy)-1][dx-1];
+ else
+ return 128 + table[QABS(dy)-1][QABS(dx)-1];
+ }
+
+ return 0;
+}
+
+
+/*!
+ Silly name. Create an array that has \a e points extra at the start and
+ end to enable a sliding correlation to be performed.
+*/
+QArray<int> QIMPenStroke::createBase( const QArray<int> a, int e )
+{
+ QArray<int> ra( a.count() + 2*e );
+
+ for ( int i = 0; i < e; i++ ) {
+ ra[i] = a[e - i - 1];
+ ra[a.count() + i] = a[a.count() - i - 1];
+ }
+ for ( unsigned i = 0; i < a.count(); i++ ) {
+ ra[i+e] = a[i];
+ }
+
+ return ra;
+}
+
+
+/*!
+ Smooth the points in an array. Probably a bad idea.
+*/
+void QIMPenStroke::smooth( QArray<int> &sig)
+{
+ QArray<int> nsig = sig.copy();
+
+ int a;
+ for ( unsigned i = 1; i < sig.count()-2; i++ ) {
+ a = 0;
+ for ( int j = -1; j <= 1; j++ ) {
+ a += sig[ i + j ];
+ }
+ nsig[i] = a / 3;
+ }
+
+ sig = nsig;
+}
+
+/*!
+ Write the character's data to the stream.
+*/
+QDataStream &operator<< (QDataStream &s, const QIMPenStroke &ws)
+{
+ s << ws.startPoint;
+ s << ws.links.count();
+ for ( unsigned i = 0; i < ws.links.count(); i++ ) {
+ s << (Q_INT8)ws.links[i].dx;
+ s << (Q_INT8)ws.links[i].dy;
+ }
+
+ return s;
+}
+
+/*!
+ Read the character's data from the stream.
+*/
+QDataStream &operator>> (QDataStream &s, QIMPenStroke &ws)
+{
+ Q_INT8 i8;
+ s >> ws.startPoint;
+ ws.lastPoint = ws.startPoint;
+ unsigned size;
+ s >> size;
+ ws.links.resize( size );
+ for ( unsigned i = 0; i < size; i++ ) {
+ s >> i8;
+ ws.links[i].dx = i8;
+ s >> i8;
+ ws.links[i].dy = i8;
+ ws.lastPoint += QPoint( ws.links[i].dx, ws.links[i].dy );
+ }
+
+ return s;
+}
+
+
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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QIMPENSTROKE_H_
+#define QIMPENSTROKE_H_
+
+#include <qobject.h>
+#include <qarray.h>
+#include <qlist.h>
+
+struct Q_PACKED QIMPenGlyphLink
+{
+ signed char dx;
+ signed char dy;
+};
+
+class QIMPenStroke
+{
+public:
+ QIMPenStroke();
+ QIMPenStroke( const QIMPenStroke & );
+
+ void clear();
+ bool isEmpty() const { return links.isEmpty(); }
+ unsigned int length() const { return links.count(); }
+ unsigned int match( QIMPenStroke *st );
+ const QArray<QIMPenGlyphLink> &chain() const { return links; }
+ QPoint startingPoint() const { return startPoint; }
+ void setStartingPoint( const QPoint &p ) { startPoint = p; }
+ QRect boundingRect();
+
+ QIMPenStroke &operator=( const QIMPenStroke &s );
+
+ void beginInput( QPoint p );
+ bool addPoint( QPoint p );
+ void endInput();
+
+ QArray<int> sig() { createTanSignature(); return tsig; } // for debugging
+
+protected:
+ void createSignatures();
+ void createTanSignature();
+ void createAngleSignature();
+ void createDistSignature();
+ int calcError( const QArray<int> &base, const QArray<int> &win,
+ int off, bool t );
+ QArray<int> scale( const QArray<int> &s, unsigned count, bool t = FALSE );
+ void internalAddPoint( QPoint p );
+ QPoint calcCenter();
+ int arcTan( int dy, int dx );
+ QArray<int> createBase( const QArray<int> a, int e );
+ void smooth( QArray<int> &);
+
+protected:
+ QPoint startPoint;
+ QPoint lastPoint;
+ QArray<QIMPenGlyphLink> links;
+ QArray<int> tsig;
+ QArray<int> asig;
+ QArray<int> dsig;
+ QRect bounding;
+
+ friend QDataStream &operator<< (QDataStream &, const QIMPenStroke &);
+ friend QDataStream &operator>> (QDataStream &, QIMPenStroke &);
+};
+
+typedef QList<QIMPenStroke> QIMPenStrokeList;
+typedef QListIterator<QIMPenStroke> QIMPenStrokeIterator;
+
+QDataStream & operator<< (QDataStream & s, const QIMPenStroke &ws);
+QDataStream & operator>> (QDataStream & s, const QIMPenStroke &ws);
+
+#endif
+
diff --git a/inputmethods/handwriting/qimpenwidget.cpp b/inputmethods/handwriting/qimpenwidget.cpp
new file mode 100644
index 0000000..8f8f582
--- a/dev/null
+++ b/inputmethods/handwriting/qimpenwidget.cpp
@@ -0,0 +1,446 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include <qapplication.h>
+#include <qinputdialog.h>
+#include <qpainter.h>
+#include <qfile.h>
+#include <qdatastream.h>
+#include <qtimer.h>
+#include "qimpenchar.h"
+#include "qimpenwidget.h"
+
+#define TITLE_WIDTH 30 // ### magic
+
+/*!
+ \class QIMPenWidget qimpenwidget.h
+
+ Draws characters and allows input of characters.
+*/
+
+QIMPenWidget::QIMPenWidget( QWidget *parent )
+ : QWidget( parent )
+{
+ charSets.setAutoDelete( TRUE );
+ inputStroke = 0;
+ outputChar = 0;
+ outputStroke = 0;
+ mode = Waiting;
+ currCharSet = 0;
+ readOnly = FALSE;
+ strokes.setAutoDelete( TRUE );
+
+ timer = new QTimer(this);
+ connect( timer, SIGNAL(timeout()), SLOT(timeout()));
+
+ setBackgroundColor( qApp->palette().color( QPalette::Active,
+ QColorGroup::Base ) );
+ strokeColor = black;
+ setFixedHeight( 75 );
+}
+
+void QIMPenWidget::clear()
+{
+ timer->stop();
+ mode = Waiting;
+ QRect r( dirtyRect );
+ QIMPenStrokeIterator it( strokes );
+ while ( it.current() ) {
+ r |= it.current()->boundingRect();
+ ++it;
+ }
+ outputChar = 0;
+ outputStroke = 0;
+ strokes.clear();
+ if ( !r.isNull() ) {
+ r.moveBy( -2, -2 );
+ r.setSize( r.size() + QSize( 4, 4 ) );
+ repaint( r );
+ } else {
+ repaint();
+ }
+}
+
+void QIMPenWidget::removeStroke()
+{
+ QRect r( dirtyRect );
+ QIMPenStroke *st = strokes.getFirst();
+ QRect strokeRect;
+ if ( st )
+ strokeRect = st->boundingRect();
+ r |= strokeRect;
+ strokes.removeFirst();
+ if ( !r.isNull() ) {
+ r.moveBy( -2, -2 );
+ r.setSize( r.size() + QSize( 4, 4 ) );
+ repaint( r );
+ }
+}
+
+void QIMPenWidget::greyStroke()
+{
+ QRect r( dirtyRect );
+ QIMPenStroke *st = strokes.getLast();
+ QRect strokeRect;
+ if ( st )
+ strokeRect = st->boundingRect();
+ r |= strokeRect;
+ QColor oldCol = strokeColor;
+ strokeColor = gray;
+ if ( !r.isNull() ) {
+ r.moveBy( -2, -2 );
+ r.setSize( r.size() + QSize( 4, 4 ) );
+ repaint( r );
+ }
+ strokeColor = oldCol;
+}
+
+/*!
+ Insert a character set into the list.
+*/
+void QIMPenWidget::insertCharSet( QIMPenCharSet *cs, int stretch, int pos )
+{
+ CharSetEntry *e = new CharSetEntry;
+ e->cs = cs;
+ e->stretch = stretch;
+ if ( pos < 0 )
+ pos = charSets.count();
+ charSets.insert( pos, e );
+ currCharSet = 0;
+ emit changeCharSet( currCharSet );
+ emit changeCharSet( charSets.at(currCharSet)->cs );
+ totalStretch = 0;
+ CharSetEntryIterator it( charSets );
+ for ( ; it.current(); ++it )
+ totalStretch += it.current()->stretch;
+ update();
+}
+
+/*!
+ Remove a character set from the list.
+*/
+void QIMPenWidget::removeCharSet( int pos )
+{
+ if ( pos >= 0 && pos < (int)charSets.count() ) {
+ charSets.remove( pos );
+ currCharSet = 0;
+ if ( charSets.count() ) {
+ emit changeCharSet( currCharSet );
+ emit changeCharSet( charSets.at(currCharSet)->cs );
+ }
+ totalStretch = 0;
+ CharSetEntryIterator it( charSets );
+ for ( ; it.current(); ++it )
+ totalStretch += it.current()->stretch;
+ update();
+ }
+}
+
+void QIMPenWidget::changeCharSet( QIMPenCharSet *cs, int pos )
+{
+ if ( pos >= 0 && pos < (int)charSets.count() ) {
+ CharSetEntry *e = new CharSetEntry;
+ e->cs = cs;
+ e->stretch = charSets.at(pos)->stretch;
+ charSets.remove( pos );
+ charSets.insert( pos, e );
+ if ( pos == currCharSet ) {
+ emit changeCharSet( charSets.at(currCharSet)->cs );
+ }
+ update();
+ }
+}
+
+void QIMPenWidget::clearCharSets()
+{
+ charSets.clear();
+ currCharSet = 0;
+ update();
+}
+
+/*!
+ Display a character. \a speed determines how quickly the character is
+ drawn.
+*/
+void QIMPenWidget::showCharacter( QIMPenChar *ch, int speed )
+{
+ outputChar = 0;
+ outputStroke = 0;
+ strokes.clear();
+ mode = Output;
+ repaint();
+ if ( !ch || ch->isEmpty() ) {
+ mode = Waiting;
+ return;
+ }
+
+ outputChar = ch;
+ outputStroke = outputChar->penStrokes().getFirst();
+ if ( speed < 0 ) speed = 0;
+ if ( speed > 20 ) speed = 20;
+ speed = 50 - speed;
+ pointIndex = 0;
+ strokeIndex = 0;
+ lastPoint = outputStroke->startingPoint();
+ QRect br( outputChar->boundingRect() );
+ lastPoint.setX( (width() - br.width()) / 2 + (lastPoint.x () - br.left()) );
+ QPoint offset = lastPoint - outputStroke->startingPoint();
+ br.moveBy( offset.x(), offset.y() );
+ dirtyRect |= br;
+ timer->start( speed );
+}
+
+/*!
+ Handle drawing/clearing of characters.
+*/
+void QIMPenWidget::timeout()
+{
+ if ( mode == Output ) {
+ const QArray<QIMPenGlyphLink> &chain = outputStroke->chain();
+ if ( pointIndex < chain.count() ) {
+ QPainter paint( this );
+ paint.setBrush( Qt::black );
+ for ( unsigned i = 0; i < 3 && pointIndex < chain.count(); i++ ) {
+ lastPoint.rx() += chain[pointIndex].dx;
+ lastPoint.ry() += chain[pointIndex].dy;
+ pointIndex++;
+ paint.drawRect( lastPoint.x()-1, lastPoint.y()-1, 2, 2 );
+ }
+ }
+ if ( pointIndex >= chain.count() ) {
+ QIMPenStrokeList strokes = outputChar->penStrokes();
+ if ( strokeIndex < (int)strokes.count() - 1 ) {
+ pointIndex = 0;
+ strokeIndex++;
+ outputStroke = strokes.at( strokeIndex );
+ lastPoint = outputChar->startingPoint();
+ QRect br( outputChar->boundingRect() );
+ lastPoint.setX( (width() - br.width()) / 2
+ + (lastPoint.x () - br.left()) );
+ QPoint off = lastPoint - outputChar->startingPoint();
+ lastPoint = outputStroke->startingPoint() + off;
+ } else {
+ timer->stop();
+ mode = Waiting;
+ }
+ }
+ } else if ( mode == Waiting ) {
+ QRect r( dirtyRect );
+ if ( !r.isNull() ) {
+ r.moveBy( -2, -2 );
+ r.setSize( r.size() + QSize( 4, 4 ) );
+ repaint( r );
+ }
+ }
+}
+
+/*!
+ If the point \a p is over one of the character set titles, switch
+ to the set and return TRUE.
+*/
+bool QIMPenWidget::selectSet( QPoint p )
+{
+ if ( charSets.count() ) {
+ CharSetEntryIterator it( charSets );
+ int spos = 0;
+ int idx = 0;
+ for ( ; it.current(); ++it, idx++ ) {
+ int setWidth = width() * it.current()->stretch / totalStretch;
+ spos += setWidth;
+ if ( p.x() < spos ) {
+ if ( idx != currCharSet ) {
+ currCharSet = idx;
+ update( 0, 0, width(), 12 );
+ emit changeCharSet( currCharSet );
+ emit changeCharSet( charSets.at(currCharSet)->cs );
+ }
+ break;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/*!
+ Hopefully returns a sensible size.
+*/
+QSize QIMPenWidget::sizeHint()
+{
+ return QSize( TITLE_WIDTH * charSets.count(), 75 );
+}
+
+void QIMPenWidget::mousePressEvent( QMouseEvent *e )
+{
+ if ( !readOnly && e->button() == LeftButton && mode == Waiting ) {
+ // if selectSet returns false the click was not over the
+ // char set selectors.
+ if ( !selectSet( e->pos() ) ) {
+ // start of character input
+ timer->stop();
+ if ( outputChar ) {
+ outputChar = 0;
+ outputStroke = 0;
+ repaint();
+ }
+ mode = Input;
+ lastPoint = e->pos();
+ emit beginStroke();
+ inputStroke = new QIMPenStroke;
+ strokes.append( inputStroke );
+ inputStroke->beginInput( e->pos() );
+ QPainter paint( this );
+ paint.setBrush( Qt::black );
+ paint.drawRect( lastPoint.x()-1, lastPoint.y()-1, 2, 2 );
+ }
+ }
+}
+
+void QIMPenWidget::mouseReleaseEvent( QMouseEvent *e )
+{
+ if ( !readOnly && e->button() == LeftButton && mode == Input ) {
+ mode = Waiting;
+ inputStroke->endInput();
+ if ( charSets.count() )
+ emit stroke( inputStroke );
+ inputStroke = 0;
+ }
+}
+
+void QIMPenWidget::mouseMoveEvent( QMouseEvent *e )
+{
+ if ( !readOnly && mode == Input ) {
+ int dx = QABS( e->pos().x() - lastPoint.x() );
+ int dy = QABS( e->pos().y() - lastPoint.y() );
+ if ( dx + dy > 1 ) {
+ if ( inputStroke->addPoint( e->pos() ) ) {
+ QPainter paint( this );
+ paint.setPen( Qt::black );
+ paint.setBrush( Qt::black );
+ const QArray<QIMPenGlyphLink> &chain = inputStroke->chain();
+ QPoint p( e->pos() );
+ for ( int i = (int)chain.count()-1; i >= 0; i-- ) {
+ paint.drawRect( p.x()-1, p.y()-1, 2, 2 );
+ p.rx() -= chain[i].dx;
+ p.ry() -= chain[i].dy;
+ if ( p == lastPoint )
+ break;
+ }
+
+ /* ### use this when thick lines work properly on all devices
+ paint.setPen( QPen( Qt::black, 2 ) );
+ paint.drawLine( lastPoint, e->pos() );
+ */
+ }
+ lastPoint = e->pos();
+ }
+ }
+}
+
+void QIMPenWidget::paintEvent( QPaintEvent * )
+{
+ QPainter paint( this );
+
+ // draw guidelines
+ paint.setPen( Qt::gray );
+ paint.drawLine( 0, 0, width(), 0 );
+ int y = height() / 3;
+ paint.drawLine( 0, y, width(), y );
+ y *= 2;
+ paint.setPen( blue );
+ paint.drawLine( 0, y, width(), y );
+ paint.setPen( Qt::gray );
+
+ if ( !charSets.count() )
+ return;
+
+ // draw the character set titles
+ QFont selFont( "helvetica", 8, QFont::Bold );
+ QFont font( "helvetica", 8 );
+ CharSetEntryIterator it( charSets );
+ int spos = 0;
+ for ( ; it.current(); ++it ) {
+ int setWidth = width() * it.current()->stretch / totalStretch;
+ spos += setWidth;
+ if ( it.current() != charSets.getLast() ) {
+ paint.drawLine( spos, 0, spos, 5 );
+ paint.drawLine( spos, height()-1, spos, height()-6 );
+ }
+ paint.setFont( font );
+ int w = paint.fontMetrics().width( it.current()->cs->title() );
+ int tpos = spos - setWidth / 2;
+ paint.drawText( tpos - w/2, 0, w, 12, QPainter::AlignCenter,
+ it.current()->cs->title() );
+ }
+
+ // draw any character that should be displayed when repainted.
+ QPoint off;
+ const QIMPenStrokeList *stk = 0;
+ if ( outputChar && mode == Waiting ) {
+ stk = &outputChar->penStrokes();
+ QPoint p( outputChar->startingPoint() );
+ QRect br( outputChar->boundingRect() );
+ p.setX( (width() - br.width()) / 2 + (p.x () - br.left()) );
+ off = p - outputChar->startingPoint();
+ } else if ( mode == Waiting ) {
+ stk = &strokes;
+ strokeColor = gray;
+ }
+
+ if ( stk && !stk->isEmpty() ) {
+ paint.setPen( strokeColor );
+ paint.setBrush( strokeColor );
+ QIMPenStrokeIterator it( *stk );
+ while ( it.current() ) {
+ QPoint p = it.current()->startingPoint() + off;
+ paint.drawRect( p.x()-1, p.y()-1, 2, 2 );
+ const QArray<QIMPenGlyphLink> &chain = it.current()->chain();
+ for ( unsigned i = 0; i < chain.count(); i++ ) {
+ p.rx() += chain[i].dx;
+ p.ry() += chain[i].dy;
+ paint.drawRect( p.x()-1, p.y()-1, 2, 2 );
+ }
+ ++it;
+ if ( it.atLast() && mode == Waiting )
+ strokeColor = black;
+ }
+ }
+
+ dirtyRect = QRect();
+
+ // debug
+/*
+ if ( input ) {
+ QArray<int> sig = input->sig();
+ for ( unsigned i = 0; i < sig.count(); i++ ) {
+ paint.drawPoint( 200 + i, height()/2 - sig[i] / 8 );
+ }
+ }
+*/
+}
+
+void QIMPenWidget::resizeEvent( QResizeEvent *e )
+{
+ if ( mode == Output )
+ showCharacter( outputChar, 0 );
+
+ QWidget::resizeEvent( e );
+}
+
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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include <qwidget.h>
+#include <qlist.h>
+#include "qimpenchar.h"
+
+class QIMPenWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ QIMPenWidget( QWidget *parent );
+
+ void clear();
+ void greyStroke();
+ void setReadOnly( bool r ) { readOnly = r; }
+
+ void insertCharSet( QIMPenCharSet *cs, int stretch=1, int pos=-1 );
+ void removeCharSet( int );
+ void changeCharSet( QIMPenCharSet *cs, int pos );
+ void clearCharSets();
+ void showCharacter( QIMPenChar *, int speed = 10 );
+ virtual QSize sizeHint();
+
+public slots:
+ void removeStroke();
+
+signals:
+ void changeCharSet( QIMPenCharSet *cs );
+ void changeCharSet( int );
+ void beginStroke();
+ void stroke( QIMPenStroke *ch );
+
+protected slots:
+ void timeout();
+
+protected:
+ enum Mode { Waiting, Input, Output };
+ bool selectSet( QPoint );
+ virtual void mousePressEvent( QMouseEvent *e );
+ virtual void mouseReleaseEvent( QMouseEvent *e );
+ virtual void mouseMoveEvent( QMouseEvent *e );
+ virtual void paintEvent( QPaintEvent *e );
+ virtual void resizeEvent( QResizeEvent *e );
+
+ struct CharSetEntry {
+ QIMPenCharSet *cs;
+ int stretch;
+ };
+ typedef QList<CharSetEntry> CharSetEntryList;
+ typedef QListIterator<CharSetEntry> CharSetEntryIterator;
+
+protected:
+ Mode mode;
+ bool autoHide;
+ bool readOnly;
+ QPoint lastPoint;
+ unsigned pointIndex;
+ int strokeIndex;
+ int currCharSet;
+ QTimer *timer;
+ QColor strokeColor;
+ QRect dirtyRect;
+ QIMPenChar *outputChar;
+ QIMPenStroke *outputStroke;
+ QIMPenStroke *inputStroke;
+ QIMPenStrokeList strokes;
+ CharSetEntryList charSets;
+ int totalStretch;
+};
+
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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include <qpainter.h>
+#include "qimpenwordpick.h"
+
+QIMPenWordPick::QIMPenWordPick( QWidget *parent, const char *name, WFlags f )
+ : QFrame( parent, name, f )
+{
+ clickWord = -1;
+ setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) );
+}
+
+void QIMPenWordPick::clear()
+{
+ words.clear();
+ repaint();
+}
+
+QSize QIMPenWordPick::sizeHint() const
+{
+ return QSize( -1, font().pixelSize()+2 );
+}
+
+void QIMPenWordPick::setWords( const QIMPenMatch::MatchWordList &w )
+{
+ words.clear();
+ QListIterator<QIMPenMatch::MatchWord> it( w );
+ for ( ; it.current(); ++it ) {
+ words.append( it.current()->word );
+ }
+ repaint();
+}
+
+int QIMPenWordPick::onWord( QPoint p )
+{
+ int x = 2;
+ int idx = 0;
+ for ( QStringList::Iterator it = words.begin(); it != words.end(); ++it ) {
+ QString word = *it;
+ int w = fontMetrics().width( word );
+ if ( x + w > width() )
+ break;
+ if ( p.x() > x-2 && p.x() < x + w + 2 )
+ return idx;
+ x += w + 5;
+ if ( !idx )
+ x += 3;
+ idx++;
+ }
+
+ return -1;
+}
+
+void QIMPenWordPick::paintEvent( QPaintEvent * )
+{
+ QPainter p(this);
+ int x = 2;
+ int h = p.fontMetrics().ascent() + 1;
+ int idx = 0;
+ for ( QStringList::Iterator it = words.begin(); it != words.end(); ++it ) {
+ QString word = *it;
+ int w = p.fontMetrics().width( word );
+ if ( x + w > width() )
+ break;
+ if ( idx == clickWord ) {
+ p.fillRect( x, 0, w, height(), black );
+ p.setPen( white );
+ } else {
+ p.setPen( colorGroup().text() );
+ }
+ p.drawText( x, h, word );
+ x += w + 5;
+ if ( !idx )
+ x += 3;
+ idx++;
+ }
+}
+
+void QIMPenWordPick::mousePressEvent( QMouseEvent *e )
+{
+ clickWord = onWord( e->pos() );
+ repaint();
+}
+
+void QIMPenWordPick::mouseReleaseEvent( QMouseEvent *e )
+{
+ int wordIdx = onWord( e->pos() );
+ if ( wordIdx >= 0 && wordIdx == clickWord ) {
+ //qDebug( "Clicked %s", words[wordIdx].latin1() );
+ emit wordClicked( words[wordIdx] );
+ }
+ clickWord = -1;
+ repaint();
+}
+
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 @@
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qtopia Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include <qframe.h>
+#include "qimpenmatch.h"
+
+class QIMPenWordPick : public QFrame
+{
+ Q_OBJECT
+public:
+ QIMPenWordPick( QWidget *parent = 0, const char *name = 0, WFlags f = 0 );
+
+ void clear();
+ virtual QSize sizeHint() const;
+
+public slots:
+ void setWords( const QIMPenMatch::MatchWordList &w );
+
+signals:
+ void wordClicked( const QString & );
+
+protected:
+ int onWord( QPoint p );
+ virtual void paintEvent( QPaintEvent * );
+ virtual void mousePressEvent( QMouseEvent * );
+ virtual void mouseReleaseEvent( QMouseEvent * );
+
+private:
+ QStringList words;
+ int clickWord;
+};
+
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 @@
+Files: plugins/inputmethods/libqhandwriting.so*
+Priority: optional
+Section: qpe/inputmethods
+Maintainer: Martin Jones <mjones@trolltech.com>
+Architecture: arm
+Version: $QPE_VERSION-3
+Depends: qpe-base ($QPE_VERSION)
+Description: Handwriting input method
+ 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 @@
+#!/bin/sh
+/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 @@
+#!/bin/sh
+/opt/QtPalmtop/bin/qcop QPE/TaskBar "reloadInputMethods()"