64 files changed, 9433 insertions, 0 deletions
diff --git a/inputmethods/.cvsignore b/inputmethods/.cvsignore new file mode 100644 index 0000000..f3c7a7c --- a/dev/null +++ b/inputmethods/.cvsignore @@ -0,0 +1 @@ +Makefile 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()" diff --git a/inputmethods/keyboard/.cvsignore b/inputmethods/keyboard/.cvsignore new file mode 100644 index 0000000..edfa921 --- a/dev/null +++ b/inputmethods/keyboard/.cvsignore @@ -0,0 +1,3 @@ +moc_* +*.moc +Makefile diff --git a/inputmethods/keyboard/Makefile.in b/inputmethods/keyboard/Makefile.in new file mode 100644 index 0000000..3631807 --- a/dev/null +++ b/inputmethods/keyboard/Makefile.in @@ -0,0 +1,151 @@ +############################################################################# + +####### 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 = qkeyboard +TARGET1 = lib$(TARGET).so.$(VER_MAJ) + +####### Files + +HEADERS = keyboard.h \ + ../pickboard/pickboardcfg.h \ + ../pickboard/pickboardpicks.h \ + keyboardimpl.h +SOURCES = keyboard.cpp \ + ../pickboard/pickboardcfg.cpp \ + ../pickboard/pickboardpicks.cpp \ + keyboardimpl.cpp +OBJECTS = keyboard.o \ + ../pickboard/pickboardcfg.o \ + ../pickboard/pickboardpicks.o \ + keyboardimpl.o +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = moc_keyboard.cpp \ + ../pickboard/moc_pickboardcfg.cpp \ + ../pickboard/moc_pickboardpicks.cpp +OBJMOC = moc_keyboard.o \ + ../pickboard/moc_pickboardcfg.o \ + ../pickboard/moc_pickboardpicks.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 keyboard.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 + +keyboard.o: keyboard.cpp \ + keyboard.h \ + ../pickboard/pickboardcfg.h \ + ../pickboard/pickboardpicks.h + +../pickboard/pickboardcfg.o: ../pickboard/pickboardcfg.cpp \ + ../pickboard/pickboardcfg.h \ + ../pickboard/pickboardpicks.h + +../pickboard/pickboardpicks.o: ../pickboard/pickboardpicks.cpp \ + ../pickboard/pickboardpicks.h \ + ../pickboard/pickboardcfg.h + +keyboardimpl.o: keyboardimpl.cpp \ + keyboard.h \ + ../pickboard/pickboardcfg.h \ + ../pickboard/pickboardpicks.h \ + keyboardimpl.h + +moc_keyboard.o: moc_keyboard.cpp \ + keyboard.h \ + ../pickboard/pickboardcfg.h \ + ../pickboard/pickboardpicks.h + +../pickboard/moc_pickboardcfg.o: ../pickboard/moc_pickboardcfg.cpp \ + ../pickboard/pickboardcfg.h + +../pickboard/moc_pickboardpicks.o: ../pickboard/moc_pickboardpicks.cpp \ + ../pickboard/pickboardpicks.h + +moc_keyboard.cpp: keyboard.h + $(MOC) keyboard.h -o moc_keyboard.cpp + +../pickboard/moc_pickboardcfg.cpp: ../pickboard/pickboardcfg.h + $(MOC) ../pickboard/pickboardcfg.h -o ../pickboard/moc_pickboardcfg.cpp + +../pickboard/moc_pickboardpicks.cpp: ../pickboard/pickboardpicks.h + $(MOC) ../pickboard/pickboardpicks.h -o ../pickboard/moc_pickboardpicks.cpp + + diff --git a/inputmethods/keyboard/keyboard.cpp b/inputmethods/keyboard/keyboard.cpp new file mode 100644 index 0000000..9dd24e4 --- a/dev/null +++ b/inputmethods/keyboard/keyboard.cpp @@ -0,0 +1,794 @@ +/********************************************************************** +** 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 "keyboard.h" + +#include <qpe/global.h> + +#include <qwindowsystem_qws.h> +#include <qpainter.h> +#include <qfontmetrics.h> +#include <qtimer.h> +#include <ctype.h> + + +#define USE_SMALL_BACKSPACE + +Keyboard::Keyboard(QWidget* parent, const char* name, WFlags f) : + QFrame(parent, name, f), shift(FALSE), lock(FALSE), ctrl(FALSE), + alt(FALSE), useLargeKeys(TRUE), useOptiKeys(0), pressedKey(-1), + unicode(-1), qkeycode(0), modifiers(0) +{ + // setPalette(QPalette(QColor(240,240,230))); // Beige! + // setFont( QFont( "Helvetica", 8 ) ); +// setPalette(QPalette(QColor(200,200,200))); // Gray + setPalette(QPalette(QColor(220,220,220))); // Gray + + picks = new KeyboardPicks( this ); + picks->setFont( QFont( "smallsmooth", 9 ) ); + setFont( QFont( "smallsmooth", 9 ) ); + picks->initialise(); + QObject::connect( picks, SIGNAL(key(ushort,ushort,ushort,bool,bool) ), + this, SIGNAL(key(ushort,ushort,ushort,bool,bool)) ); + + repeatTimer = new QTimer( this ); + connect( repeatTimer, SIGNAL(timeout()), this, SLOT(repeat()) ); +} + +void Keyboard::resizeEvent(QResizeEvent*) +{ + int ph = picks->sizeHint().height(); + picks->setGeometry( 0, 0, width(), ph ); + keyHeight = (height()-ph)/5; + int nk; + if ( useOptiKeys ) { + nk = 15; + } else if ( useLargeKeys ) { + nk = 15; + } else { + nk = 19; + } + defaultKeyWidth = width()/nk; + xoffs = (width()-defaultKeyWidth*nk)/2; +} + +void KeyboardPicks::initialise() +{ + setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed)); + mode = 0; + dc = new KeyboardConfig(this); + configs.append(dc); +} + +QSize KeyboardPicks::sizeHint() const +{ + return QSize(240,fontMetrics().lineSpacing()); +} + + +void KeyboardConfig::generateText(const QString &s) +{ +#if defined(Q_WS_QWS) || defined(_WS_QWS_) + for (int i=0; i<(int)backspaces; i++) { + parent->emitKey( 0, Qt::Key_Backspace, 0, true, false ); + parent->emitKey( 0, Qt::Key_Backspace, 0, false, false ); + } + for (int i=0; i<(int)s.length(); i++) { + parent->emitKey( s[i].unicode(), 0, 0, true, false ); + parent->emitKey( s[i].unicode(), 0, 0, false, false ); + } + parent->emitKey( 0, Qt::Key_Space, 0, true, false ); + parent->emitKey( 0, Qt::Key_Space, 0, false, false ); + backspaces = 0; +#endif +} + + +//PC keyboard layout and scancodes + +/* + Format: length, code, length, code, ..., 0 + + length is measured in half the width of a standard key. + If code < 0x80 we have length/2 consecutive standard keys, + starting with scancode code. + + Special keys are hardcoded, one at a time, with length of key + and code >= 0x80, these are NOT standard PC scancodes, but are looked + up in specialM[]. (The special keys are not keymappable.) + + */ + +static const uchar * const keyboard_opti[5] = { + (const uchar *const) "\001\223\003\240\002\20\002\41\002\26\002\62\002\56\002\45\002\54\003\200\001\223\002\226\002\235\002\234\002\236", + (const uchar *const) "\001\223\003\201\004\207\002\30\002\24\002\43\004\207\003\203\001\223\006\002\002\065", + (const uchar *const) "\001\223\003\202\002\60\002\37\002\23\002\22\002\36\002\21\002\55\003\203\001\223\006\005\002\055", + (const uchar *const) "\001\223\003\205\004\207\002\27\002\61\002\40\004\207\003\204\001\223\006\010\002\014", + (const uchar *const) "\001\223\003\206\002\44\002\31\002\57\002\42\002\46\002\25\002\207\003\204\001\223\002\013\002\064\002\015\002\230" +}; + + +static const uchar * const keyboard_standard[5] = { + +#ifdef USE_SMALL_BACKSPACE + (const uchar *const)"\002\240\002`\0021\0022\0023\0024\0025\0026\0027\0028\0029\0020\002-\002=\002\200\002\223\002\215\002\216\002\217", +#else + (const uchar *const)"\002\051\0021\0022\0023\0024\0025\0026\0027\0028\0029\0020\002-\002=\004\200\002\223\002\215\002\216\002\217", +#endif + //~ + 123...+ BACKSPACE //+ INSERT + HOME + PGUP + + (const uchar *const)"\003\201\002q\002w\002e\002r\002t\002y\002u\002i\002o\002p\002[\002]\002\\\001\224\002\223\002\221\002\220\002\222", + //TAB + qwerty.. + backslash //+ DEL + END + PGDN + + (const uchar *const)"\004\202\002a\002s\002d\002f\002g\002h\002j\002k\002l\002;\002'\004\203", + //CAPS + asdf.. + RETURN + + (const uchar *const)"\005\204\002z\002x\002c\002v\002b\002n\002m\002,\002.\002/\005\204\002\223\002\223\002\211", + //SHIFT + zxcv... //+ UP + + (const uchar *const)"\003\205\003\206\022\207\003\206\003\205\002\223\002\212\002\213\002\214" + //CTRL + ALT + SPACE //+ LEFT + DOWN + RIGHT + +}; + + +struct ShiftMap { + char normal; + char shifted; +}; + + +static const ShiftMap shiftMap[] = { + { '`', '~' }, + { '1', '!' }, + { '2', '@' }, + { '3', '#' }, + { '4', '$' }, + { '5', '%' }, + { '6', '^' }, + { '7', '&' }, + { '8', '*' }, + { '9', '(' }, + { '0', ')' }, + { '-', '_' }, + { '=', '+' }, + { '\\', '|' }, + { '[', '{' }, + { ']', '}' }, + { ';', ':' }, + { '\'', '"' }, + { ',', '<' }, + { '.', '>' }, + { '/', '?' } +}; + + +/* XPM */ +static const char * const uparrow_xpm[]={ +"9 9 2 1", +"a c #000000", +". c None", +".........", +"....a....", +"...aaa...", +"..aaaaa..", +"....a....", +"....a....", +"....a....", +"....a....", +"........."}; +/* XPM */ +static const char * const leftarrow_xpm[]={ +"9 9 2 1", +"a c #000000", +". c None", +".........", +".........", +"...a.....", +"..aa.....", +".aaaaaaa.", +"..aa.....", +"...a.....", +".........", +"........."}; +/* XPM */ +static const char * const downarrow_xpm[]={ +"9 9 2 1", +"a c #000000", +". c None", +".........", +"....a....", +"....a....", +"....a....", +"....a....", +"..aaaaa..", +"...aaa...", +"....a....", +"........."}; +/* XPM */ +static const char * const rightarrow_xpm[]={ +"9 9 2 1", +"a c #000000", +". c None", +".........", +".........", +".....a...", +".....aa..", +".aaaaaaa.", +".....aa..", +".....a...", +".........", +"........."}; +/* XPM */ +static const char * const insert_xpm[]={ +"9 9 2 1", +"a c #000000", +". c None", +".........", +"a........", +"a.aaa.aaa", +"a.a.a.a..", +"a.a.a..a.", +"a.a.a...a", +"a.a.a.aaa", +".........", +"........."}; +/* XPM */ +static const char * const delete_xpm[]={ +"9 9 2 1", +"a c #000000", +". c None", +".........", +"aa......a", +"a.a.aaa.a", +"a.a.a.a.a", +"a.a.aaa.a.", +"a.a.a...a", +"aaa.aaa.a", +".........", +"........."}; +/* XPM */ +static const char * const home_xpm[]={ +"9 9 2 1", +"a c #000000", +". c None", +"....a....", +"...a.a...", +"..a...a..", +".a.....a.", +"aa.aaa.aa", +".a.a.a.a.", +".a.a.a.a.", +".aaaaaaa.", +"........."}; +/* XPM */ +static const char * const end_xpm[]={ +"10 9 2 1", +"a c #000000", +". c None", +"..........", +"aa.......a", +"a..aaa.aaa", +"aa.a.a.a.a", +"a..a.a.a.a", +"a..a.a.a.a", +"aa.a.a.aaa", +"..........", +".........."}; +/* XPM */ +static const char * const pageup_xpm[]={ +"9 9 2 1", +"a c #000000", +". c None", +".aaa.aaa.", +".a.a.a.a.", +".aaa..aa.", +".a...aaa.", +".........", +".a.a.aaa.", +".a.a.a.a.", +".aaa.aaa.", +".....a..."}; +/* XPM */ +static const char * const pagedown_xpm[]={ +"9 9 2 1", +"a c #000000", +". c None", +".aaa.aaa.", +".a.a.a.a.", +".aaa..aa.", +".a...aaa.", +".........", +"...a.....", +".aaa.aaa.", +".a.a.a.a.", +".aaa.a.a."}; +/* XPM */ +static const char * const expand_xpm[]={ +"4 9 2 1", +"a c #408040", +". c None", +"a...", +"aa..", +"aaa.", +"aaaa", +"aaaa", +"aaaa", +"aaa.", +"aa..", +"a..."}; +/* XPM */ +#ifdef USE_SMALL_BACKSPACE +static const char * const backspace_xpm[]={ +"9 9 2 1", +"a c #000000", +". c None", +".........", +".........", +"...a.....", +"..aa.....", +".aaaaaaaa", +"..aa.....", +"...a.....", +".........", +"........."}; +#else +static const char * const backspace_xpm[]={ +"21 9 2 1", +"a c #000000", +". c None", +".....................", +".....................", +".....aaa..a..........", +".a...a..a.a.a.aaa.aaa", +"aaaa.aaa..aa..aa..a.a", +".a...a..a.aaa..aa.a.a", +".....aaaa.a.a.aaa.aa.", +"..................a..", +"....................."}; +#endif +/* XPM */ +static const char * const escape_xpm[]={ +"9 9 2 1", +"a c #000000", +". c None", +".........", +".........", +".aa.aa.aa", +".a..a..a.", +".aa.aa.a.", +".a...a.a.", +".aa.aa.aa", +".........", +"........."}; + + +enum { BSCode = 0x80, TabCode, CapsCode, RetCode, + ShiftCode, CtrlCode, AltCode, SpaceCode, BackSlash, + UpCode, LeftCode, DownCode, RightCode, Blank, Expand, + Opti, ResetDict, + Divide, Multiply, Add, Subtract, Decimal, Equal, + Percent, Sqrt, Inverse, Escape }; + +typedef struct SpecialMap { + int qcode; + ushort unicode; + const char * label; + const char * const * xpm; +}; + + +static const SpecialMap specialM[] = { + { Qt::Key_Backspace, 8, "<", backspace_xpm }, + { Qt::Key_Tab, 9, "Tab", NULL }, + { Qt::Key_CapsLock, 0, "Caps", NULL }, + { Qt::Key_Return, 13, "Ret", NULL }, + { Qt::Key_Shift, 0, "Shift", NULL }, + { Qt::Key_Control, 0, "Ctrl", NULL }, + { Qt::Key_Alt, 0, "Alt", NULL }, + { Qt::Key_Space, ' ', "", NULL }, + { BackSlash, 43, "\\", NULL }, + + // Need images? + { Qt::Key_Up, 0, "^", uparrow_xpm }, + { Qt::Key_Left, 0, "<", leftarrow_xpm }, + { Qt::Key_Down, 0, "v", downarrow_xpm }, + { Qt::Key_Right, 0, ">", rightarrow_xpm }, + { Qt::Key_Insert, 0, "I", insert_xpm }, + { Qt::Key_Home, 0, "H", home_xpm }, + { Qt::Key_PageUp, 0, "U", pageup_xpm }, + { Qt::Key_End, 0, "E", end_xpm }, + { Qt::Key_Delete, 0, "X", delete_xpm }, + { Qt::Key_PageDown, 0, "D", pagedown_xpm }, + { Blank, 0, " ", NULL }, + { Expand, 0, "->", expand_xpm }, + { Opti, 0, "#", NULL }, + { ResetDict, 0, "R", NULL }, + + // number pad stuff + { Divide, 0, "/", NULL }, + { Multiply, 0, "*", NULL }, + { Add, 0, "+", NULL }, + { Subtract, 0, "-", NULL }, + { Decimal, 0, ".", NULL }, + { Equal, 0, "=", NULL }, + { Percent, 0, "%", NULL }, + { Sqrt, 0, "^1/2", NULL }, + { Inverse, 0, "1/x", NULL }, + + { Escape, 27, "ESC", escape_xpm } +}; + + +static int keycode( int i2, int j, const uchar **keyboard ) +{ + if ( j <0 || j >= 5 ) + return 0; + + const uchar *row = keyboard[j]; + + while ( *row && *row <= i2 ) { + i2 -= *row; + row += 2; + } + + if ( !*row ) return 0; + + int k; + if ( row[1] >= 0x80 ) { + k = row[1]; + } else { + k = row[1]+i2/2; + } + + return k; +} + + +/* + return scancode and width of first key in row \a j if \a j >= 0, + or next key on current row if \a j < 0. + +*/ + +int Keyboard::getKey( int &w, int j ) { + static const uchar *row = 0; + static int key_i = 0; + static int scancode = 0; + static int half = 0; + + if ( j >= 0 && j < 5 ) { + if (useOptiKeys) + row = keyboard_opti[j]; + else + row = keyboard_standard[j]; + half=0; + } + + if ( !row || !*row ) { + return 0; + } else if ( row[1] >= 0x80 ) { + scancode = row[1]; + w = (row[0] * w + (half++&1)) / 2; + row += 2; + return scancode; + } else if ( key_i <= 0 ) { + key_i = row[0]/2; + scancode = row[1]; + } + key_i--; + if ( key_i <= 0 ) + row += 2; + return scancode++; +} + + +void Keyboard::paintEvent(QPaintEvent* e) +{ + QPainter painter(this); + painter.setClipRect(e->rect()); + drawKeyboard( painter ); + picks->dc->draw( &painter ); +} + + +/* + Draw the keyboard. + + If key >= 0, only the specified key is drawn. +*/ +void Keyboard::drawKeyboard( QPainter &p, int key ) +{ + const bool threeD = FALSE; + const QColorGroup& cg = colorGroup(); + QColor keycolor = // cg.background(); + QColor(240,240,230); // Beige! + QColor keycolor_pressed = cg.mid(); + QColor keycolor_lo = cg.dark(); + QColor keycolor_hi = cg.light(); + QColor textcolor = QColor(0,0,0); // cg.text(); + + int margin = threeD ? 1 : 0; + +// p.fillRect( 0, , kw-1, keyHeight-2, keycolor_pressed ); + + for ( int j = 0; j < 5; j++ ) { + int y = j * keyHeight + picks->height() + 1; + int x = xoffs; + int kw = defaultKeyWidth; + int k= getKey( kw, j ); + while ( k ) { + if ( key < 0 || k == key ) { + QString s; + bool pressed = (k == pressedKey); + bool blank = (k == 0223); + const char * const * xpm = NULL; + + if ( k >= 0x80 ) { + s = specialM[k - 0x80].label; + + xpm = specialM[k - 0x80].xpm; + + if ( k == ShiftCode ) { + pressed = shift; + } else if ( k == CapsCode ) { + pressed = lock; + } else if ( k == CtrlCode ) { + pressed = ctrl; + } else if ( k == AltCode ) { + pressed = alt; + } + } else { +#if defined(Q_WS_QWS) || defined(_WS_QWS_) +/* + s = QChar( shift^lock ? QWSServer::keyMap()[k].shift_unicode : + QWSServer::keyMap()[k].unicode); +*/ + // ### Fixme, bad code, needs improving, whole thing needs to + // be re-coded to get rid of the way it did things with scancodes etc + char shifted = k; + if ( !isalpha( k ) ) { + for ( unsigned i = 0; i < sizeof(shiftMap)/sizeof(ShiftMap); i++ ) + if ( shiftMap[i].normal == k ) + shifted = shiftMap[i].shifted; + } else { + shifted = toupper( k ); + } + s = QChar( shift^lock ? shifted : k ); +#endif + } + + if (!blank) { + if ( pressed ) + p.fillRect( x+margin, y+margin, kw-margin, keyHeight-margin-1, keycolor_pressed ); + else + p.fillRect( x+margin, y+margin, kw-margin, keyHeight-margin-1, keycolor ); + + if ( threeD ) { + p.setPen(pressed ? keycolor_lo : keycolor_hi); + p.drawLine( x, y+1, x, y+keyHeight-2 ); + p.drawLine( x+1, y+1, x+1, y+keyHeight-3 ); + p.drawLine( x+1, y+1, x+1+kw-2, y+1 ); + } else if ( j == 0 ) { + p.setPen(pressed ? keycolor_hi : keycolor_lo); + p.drawLine( x, y, x+kw, y ); + } + + // right + p.setPen(pressed ? keycolor_hi : keycolor_lo); + p.drawLine( x+kw-1, y, x+kw-1, y+keyHeight-2 ); + + if ( threeD ) { + p.setPen(keycolor_lo.light()); + p.drawLine( x+kw-2, y+keyHeight-2, x+kw-2, y+1 ); + p.drawLine( x+kw-2, y+keyHeight-2, x+1, y+keyHeight-2 ); + } + + if (xpm) { + p.drawPixmap( x + 1, y + 2, QPixmap((const char**)xpm) ); + } else { + p.setPen(textcolor); + p.drawText( x - 1, y, kw, keyHeight-2, AlignCenter, s ); + } + + if ( threeD ) { + p.setPen(keycolor_hi); + p.drawLine( x, y, x+kw-1, y ); + } + + // bottom + p.setPen(keycolor_lo); + p.drawLine( x, y+keyHeight-1, x+kw-1, y+keyHeight-1 ); + + } else { + p.fillRect( x, y, kw, keyHeight, cg.background() ); + } + } + + x += kw; + kw = defaultKeyWidth; + k = getKey( kw ); + } + } +} + + +void Keyboard::mousePressEvent(QMouseEvent *e) +{ + clearHighlight(); // typing fast? + + int i2 = ((e->x() - xoffs) * 2) / defaultKeyWidth; + int j = (e->y() - picks->height()) / keyHeight; + + int k = keycode( i2, j, (const uchar **)((useOptiKeys) ? keyboard_opti : keyboard_standard) ); + bool need_repaint = FALSE; + unicode = -1; + qkeycode = 0; + if ( k >= 0x80 ) { + if ( k == ShiftCode ) { + shift = !shift; + need_repaint = TRUE; + } else if ( k == AltCode ){ + alt = !alt; + need_repaint = TRUE; + } else if ( k == CapsCode ) { + lock = !lock; + need_repaint = TRUE; + } else if ( k == CtrlCode ) { + ctrl = !ctrl; + need_repaint = TRUE; + } else if ( k == 0224 /* Expand */ ) { + useLargeKeys = !useLargeKeys; + resizeEvent(0); + repaint( TRUE ); // need it to clear first + } else if ( k == 0225 /* Opti/Toggle */ ) { + useOptiKeys = !useOptiKeys; + resizeEvent(0); + repaint( TRUE ); // need it to clear first + } else { + qkeycode = specialM[ k - 0x80 ].qcode; + unicode = specialM[ k - 0x80 ].unicode; + } + } else { +#if defined(Q_WS_QWS) || defined(_WS_QWS_) +/* + qk = QWSServer::keyMap()[k].key_code; + if ( qk != Key_unknown ) { + if ( ctrl ) + u = QWSServer::keyMap()[k].ctrl_unicode; + else if ( shift^lock ) + u = QWSServer::keyMap()[k].shift_unicode; + else + u = QWSServer::keyMap()[k].unicode; + } +*/ + char shifted = k; + if ( !isalpha( k ) ) { + // ### Fixme, bad code, needs improving, whole thing needs to + // be re-coded to get rid of the way it did things with scancodes etc + for ( unsigned i = 0; i < sizeof(shiftMap)/sizeof(ShiftMap); i++ ) + if ( shiftMap[i].normal == k ) + shifted = shiftMap[i].shifted; + } else { + shifted = toupper( k ); + } + QChar tempChar( shift^lock ? shifted : k ); + unicode = tempChar.unicode(); +#endif + } + if ( unicode != -1 ) { + modifiers = (shift ? Qt::ShiftButton : 0) | (ctrl ? Qt::ControlButton : 0) | + (alt ? Qt::AltButton : 0); +#if defined(Q_WS_QWS) || defined(_WS_QWS_) + emit key( unicode, qkeycode, modifiers, true, false ); + repeatTimer->start( 500 ); +#endif + need_repaint = shift || alt || ctrl; + shift = alt = ctrl = FALSE; + //qDebug( "pressed %d -> %04x ('%c')", k, u, u&0xffff < 256 ? u&0xff : 0 ); + + KeyboardConfig *dc = picks->dc; + + if (dc) { + if (qkeycode == Qt::Key_Backspace) { + dc->input.remove(dc->input.last()); // remove last input + dc->decBackspaces(); + } else if ( k == 0226 || qkeycode == Qt::Key_Return || + qkeycode == Qt::Key_Space || + QChar(unicode).isPunct() ) { + dc->input.clear(); + dc->resetBackspaces(); + } else { + dc->add(QString(QChar(unicode))); + dc->incBackspaces(); + } + } + + picks->repaint(); + + } + pressedKey = k; + if ( need_repaint ) { + repaint( FALSE ); + } else { + QPainter p(this); + drawKeyboard( p, pressedKey ); + } + pressTid = startTimer(80); + pressed = TRUE; +} + + +void Keyboard::mouseReleaseEvent(QMouseEvent*) +{ + if ( pressTid == 0 ) + clearHighlight(); +#if defined(Q_WS_QWS) || defined(_WS_QWS_) + if ( unicode != -1 ) { + emit key( unicode, qkeycode, modifiers, false, false ); + repeatTimer->stop(); + } +#endif + pressed = FALSE; +} + +void Keyboard::timerEvent(QTimerEvent* e) +{ + if ( e->timerId() == pressTid ) { + killTimer(pressTid); + pressTid = 0; + if ( !pressed ) + clearHighlight(); + } +} + +void Keyboard::repeat() +{ + repeatTimer->start( 150 ); + emit key( unicode, qkeycode, modifiers, true, true ); +} + +void Keyboard::clearHighlight() +{ + if ( pressedKey >= 0 ) { + int tmp = pressedKey; + pressedKey = -1; + QPainter p(this); + drawKeyboard( p, tmp ); + } +} + + +QSize Keyboard::sizeHint() const +{ + QFontMetrics fm=fontMetrics(); + int keyHeight = fm.lineSpacing()+2; + + if (useOptiKeys) + keyHeight += 1; + + return QSize( 320, keyHeight * 5 + picks->sizeHint().height() + 1 ); +} + + +void Keyboard::resetState() +{ + picks->resetState(); +} diff --git a/inputmethods/keyboard/keyboard.h b/inputmethods/keyboard/keyboard.h new file mode 100644 index 0000000..38ae338 --- a/dev/null +++ b/inputmethods/keyboard/keyboard.h @@ -0,0 +1,103 @@ +/********************************************************************** +** 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 "../pickboard/pickboardcfg.h" +#include "../pickboard/pickboardpicks.h" + +class QTimer; + +class KeyboardConfig : public DictFilterConfig +{ +public: + KeyboardConfig(PickboardPicks* p) : DictFilterConfig(p), backspaces(0) { nrows = 1; } + virtual void generateText(const QString &s); + void decBackspaces() { if (backspaces) backspaces--; } + void incBackspaces() { backspaces++; } + void resetBackspaces() { backspaces = 0; } +private: + int backspaces; +}; + + +class KeyboardPicks : public PickboardPicks +{ + Q_OBJECT +public: + KeyboardPicks(QWidget* parent=0, const char* name=0, WFlags f=0) + : PickboardPicks(parent, name, f) { } + void initialise(); + virtual QSize sizeHint() const; + KeyboardConfig *dc; +}; + +class Keyboard : public QFrame +{ + Q_OBJECT +public: + Keyboard( QWidget* parent=0, const char* name=0, WFlags f=0 ); + + void resetState(); + + void mousePressEvent(QMouseEvent*); + void mouseReleaseEvent(QMouseEvent*); + void resizeEvent(QResizeEvent*); + void paintEvent(QPaintEvent* e); + void timerEvent(QTimerEvent* e); + void drawKeyboard( QPainter &p, int key = -1 ); + + void setMode(int mode) { useOptiKeys = mode; } + + QSize sizeHint() const; + +signals: + void key( ushort scancode, ushort unicode, ushort modifiers, bool, bool ); + +private slots: + void repeat(); + +private: + int getKey( int &w, int j = -1 ); + void clearHighlight(); + + uint shift:1; + uint lock:1; + uint ctrl:1; + uint alt:1; + uint useLargeKeys:1; + uint useOptiKeys:1; + + int pressedKey; + + KeyboardPicks *picks; + + int keyHeight; + int defaultKeyWidth; + int xoffs; + + int unicode; + int qkeycode; + int modifiers; + + int pressTid; + bool pressed; + + QTimer *repeatTimer; +}; + diff --git a/inputmethods/keyboard/keyboard.pro b/inputmethods/keyboard/keyboard.pro new file mode 100644 index 0000000..87c838e --- a/dev/null +++ b/inputmethods/keyboard/keyboard.pro @@ -0,0 +1,18 @@ +TEMPLATE = lib +CONFIG += qt warn_on release +HEADERS = keyboard.h \ + ../pickboard/pickboardcfg.h \ + ../pickboard/pickboardpicks.h \ + keyboardimpl.h +SOURCES = keyboard.cpp \ + ../pickboard/pickboardcfg.cpp \ + ../pickboard/pickboardpicks.cpp \ + keyboardimpl.cpp +TARGET = qkeyboard +DESTDIR = ../../plugins/inputmethods +INCLUDEPATH += $(QPEDIR)/include +DEPENDPATH += ../$(QPEDIR)/include ../../taskbar +LIBS += -lqpe +VERSION = 1.0.0 + +TRANSLATIONS += ../../i18n/de/libqkeyboard.ts diff --git a/inputmethods/keyboard/keyboardimpl.cpp b/inputmethods/keyboard/keyboardimpl.cpp new file mode 100644 index 0000000..ea61272 --- a/dev/null +++ b/inputmethods/keyboard/keyboardimpl.cpp @@ -0,0 +1,131 @@ +/********************************************************************** +** 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 <qpixmap.h> +#include "keyboard.h" +#include "keyboardimpl.h" + +/* XPM */ +static const char * kb_xpm[] = { +"28 13 4 1", +" c None", +". c #4C4C4C", +"+ c #FFF7DD", +"@ c #D6CFBA", +" .......................... ", +" .+++.+++.+++.+++.+++.++++. ", +" .+@@.+@@.+@@.+@@.+@@.+@@@. ", +" .......................... ", +" .+++++.+++.+++.+++.++++++. ", +" .+@@@@.+@@.+@@.+@@.+@@@@@. ", +" .......................... ", +" .++++++.+++.+++.+++.+++++. ", +" .+@@@@@.+@@.+@@.+@@.+@@@@. ", +" .......................... ", +" .++++.++++++++++++++.++++. ", +" .+@@@.+@@@@@@@@@@@@@.+@@@. ", +" .......................... "}; + + +/* XPM */ +static char * opti_xpm[] = { +"28 13 4 1", +" c None", +". c #4C4C4C", +"+ c #FFF7DD", +"@ c #D6CFBA", +" ......................... ", +" .+++.+++.+++.+++.+++.+++. ", +" .+@@.+@@.+@@.+@@.+@@.+@@. ", +" ......................... ", +" .+++.+++.+++.+++.+++.+++. ", +" .+@@.+@@.+@@.+@@.+@@.+@@. ", +" ......................... ", +" .+++.+++.+++.+++.+++.+++. ", +" .+@@.+@@.+@@.+@@.+@@.+@@. ", +" ......................... ", +" .+++.+++.+++.+++.+++.+++. ", +" .+@@.+@@.+@@.+@@.+@@.+@@. ", +" ......................... "}; + + + +KeyboardImpl::KeyboardImpl() + : input(0), icn(0), ref(0) +{ +} + +KeyboardImpl::~KeyboardImpl() +{ + delete input; + delete icn; +} + +QWidget *KeyboardImpl::inputMethod( QWidget *parent, Qt::WFlags f ) +{ + if ( !input ) + input = new Keyboard( parent, "Keyboard", f ); + return input; +} + +void KeyboardImpl::resetState() +{ + if ( input ) + input->resetState(); +} + +QPixmap *KeyboardImpl::icon() +{ + if ( !icn ) + icn = new QPixmap( (const char **)kb_xpm ); + return icn; +} + +QString KeyboardImpl::name() +{ + return qApp->translate( "InputMethods", "Keyboard" ); +// return qApp->translate( "InputMethods", "Opti" ); +} + +void KeyboardImpl::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 KeyboardImpl::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( KeyboardImpl ) +} +#endif diff --git a/inputmethods/keyboard/keyboardimpl.h b/inputmethods/keyboard/keyboardimpl.h new file mode 100644 index 0000000..e756364 --- a/dev/null +++ b/inputmethods/keyboard/keyboardimpl.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 KEYBOARDIMPL_H +#define KEYBOARDIMPL_H + +#include <qpe/inputmethodinterface.h> + +class Keyboard; +class QPixmap; + +class KeyboardImpl : public InputMethodInterface +{ +public: + KeyboardImpl(); + virtual ~KeyboardImpl(); + +#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: + Keyboard *input; + QPixmap *icn; + ulong ref; +}; + +#endif diff --git a/inputmethods/keyboard/qpe-keyboard.control b/inputmethods/keyboard/qpe-keyboard.control new file mode 100644 index 0000000..dae67f9 --- a/dev/null +++ b/inputmethods/keyboard/qpe-keyboard.control @@ -0,0 +1,9 @@ +Files: plugins/inputmethods/libqkeyboard.so* +Priority: optional +Section: qpe/inputmethods +Maintainer: Martin Jones <mjones@trolltech.com> +Architecture: arm +Version: $QPE_VERSION-3 +Depends: qpe-base ($QPE_VERSION) +Description: Keyboard input method + Keyboard input method for the Qtopia environment. diff --git a/inputmethods/keyboard/qpe-keyboard.postinst b/inputmethods/keyboard/qpe-keyboard.postinst new file mode 100755 index 0000000..c254b01 --- a/dev/null +++ b/inputmethods/keyboard/qpe-keyboard.postinst @@ -0,0 +1,2 @@ +#!/bin/sh +/opt/QtPalmtop/bin/qcop QPE/TaskBar "reloadInputMethods()" diff --git a/inputmethods/keyboard/qpe-keyboard.postrm b/inputmethods/keyboard/qpe-keyboard.postrm new file mode 100755 index 0000000..c254b01 --- a/dev/null +++ b/inputmethods/keyboard/qpe-keyboard.postrm @@ -0,0 +1,2 @@ +#!/bin/sh +/opt/QtPalmtop/bin/qcop QPE/TaskBar "reloadInputMethods()" diff --git a/inputmethods/pickboard/.cvsignore b/inputmethods/pickboard/.cvsignore new file mode 100644 index 0000000..edfa921 --- a/dev/null +++ b/inputmethods/pickboard/.cvsignore @@ -0,0 +1,3 @@ +moc_* +*.moc +Makefile diff --git a/inputmethods/pickboard/Makefile.in b/inputmethods/pickboard/Makefile.in new file mode 100644 index 0000000..d72e82d --- a/dev/null +++ b/inputmethods/pickboard/Makefile.in @@ -0,0 +1,147 @@ +############################################################################# + +####### 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 = qpickboard +TARGET1 = lib$(TARGET).so.$(VER_MAJ) + +####### Files + +HEADERS = pickboard.h \ + pickboardcfg.h \ + pickboardimpl.h \ + pickboardpicks.h +SOURCES = pickboard.cpp \ + pickboardcfg.cpp \ + pickboardimpl.cpp \ + pickboardpicks.cpp +OBJECTS = pickboard.o \ + pickboardcfg.o \ + pickboardimpl.o \ + pickboardpicks.o +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = moc_pickboard.cpp \ + moc_pickboardcfg.cpp \ + moc_pickboardpicks.cpp +OBJMOC = moc_pickboard.o \ + moc_pickboardcfg.o \ + moc_pickboardpicks.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 pickboard.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 + +pickboard.o: pickboard.cpp \ + pickboard.h \ + pickboardpicks.h \ + pickboardcfg.h + +pickboardcfg.o: pickboardcfg.cpp \ + pickboardcfg.h \ + pickboardpicks.h + +pickboardimpl.o: pickboardimpl.cpp \ + pickboard.h \ + pickboardimpl.h + +pickboardpicks.o: pickboardpicks.cpp \ + pickboardpicks.h \ + pickboardcfg.h + +moc_pickboard.o: moc_pickboard.cpp \ + pickboard.h + +moc_pickboardcfg.o: moc_pickboardcfg.cpp \ + pickboardcfg.h + +moc_pickboardpicks.o: moc_pickboardpicks.cpp \ + pickboardpicks.h + +moc_pickboard.cpp: pickboard.h + $(MOC) pickboard.h -o moc_pickboard.cpp + +moc_pickboardcfg.cpp: pickboardcfg.h + $(MOC) pickboardcfg.h -o moc_pickboardcfg.cpp + +moc_pickboardpicks.cpp: pickboardpicks.h + $(MOC) pickboardpicks.h -o moc_pickboardpicks.cpp + + diff --git a/inputmethods/pickboard/pickboard.cpp b/inputmethods/pickboard/pickboard.cpp new file mode 100644 index 0000000..087144e --- a/dev/null +++ b/inputmethods/pickboard/pickboard.cpp @@ -0,0 +1,89 @@ +/********************************************************************** +** 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 "pickboard.h" +#include "pickboardpicks.h" +#include "pickboardcfg.h" + +#include <qpe/global.h> + +#include <qpainter.h> +#include <qlist.h> +#include <qbitmap.h> +#include <qlayout.h> +#include <qvbox.h> +#include <qdialog.h> +#include <qscrollview.h> +#include <qpopupmenu.h> +#include <qhbuttongroup.h> +#include <qpushbutton.h> +#include <qmessagebox.h> +#include <qwindowsystem_qws.h> + +/* XPM */ +static const char * const menu_xpm[]={ +"9 9 2 1", +"a c #000000", +". c None", +".........", +".........", +".........", +"....a....", +"...aaa...", +"..aaaaa..", +".aaaaaaa.", +".........", +"........."}; + +class PickboardPrivate { +public: + PickboardPrivate(Pickboard* parent) + { + picks = new PickboardPicks(parent); + picks->initialise(); + menu = new QPushButton(parent); + menu->setSizePolicy(QSizePolicy(QSizePolicy::Minimum,QSizePolicy::Expanding)); + menu->setPixmap(QPixmap((const char **)menu_xpm)); + QObject::connect(menu,SIGNAL(clicked()),picks,SLOT(doMenu())); + QObject::connect(picks,SIGNAL(key(ushort,ushort,ushort,bool,bool)), + parent,SIGNAL(key(ushort,ushort,ushort,bool,bool))); + } + + PickboardPicks* picks; + QPushButton* menu; +}; + +Pickboard::Pickboard(QWidget* parent, const char* name, WFlags f) : + QFrame(parent,name,f) +{ + (new QHBoxLayout(this))->setAutoAdd(TRUE); + d = new PickboardPrivate(this); + setFont( QFont( "smallsmooth", 9 ) ); +} + +Pickboard::~Pickboard() +{ + delete d; +} + +void Pickboard::resetState() +{ + d->picks->resetState(); +} + diff --git a/inputmethods/pickboard/pickboard.h b/inputmethods/pickboard/pickboard.h new file mode 100644 index 0000000..5fff875 --- a/dev/null +++ b/inputmethods/pickboard/pickboard.h @@ -0,0 +1,43 @@ +/********************************************************************** +** 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 PICKBOARD_H +#define PICKBOARD_H + +#include <qframe.h> + +class PickboardPrivate; + +class Pickboard : public QFrame +{ + Q_OBJECT +public: + Pickboard(QWidget* parent=0, const char* name=0, WFlags f=0); + ~Pickboard(); + + void resetState(); + +signals: + void key( ushort, ushort, ushort, bool, bool ); + +private: + PickboardPrivate* d; +}; + +#endif diff --git a/inputmethods/pickboard/pickboard.pro b/inputmethods/pickboard/pickboard.pro new file mode 100644 index 0000000..50bc342 --- a/dev/null +++ b/inputmethods/pickboard/pickboard.pro @@ -0,0 +1,12 @@ +TEMPLATE = lib +CONFIG += qt warn_on release +HEADERS = pickboard.h pickboardcfg.h pickboardimpl.h pickboardpicks.h +SOURCES = pickboard.cpp pickboardcfg.cpp pickboardimpl.cpp pickboardpicks.cpp +TARGET = qpickboard +DESTDIR = ../../plugins/inputmethods +INCLUDEPATH += $(QPEDIR)/include +DEPENDPATH += ../$(QPEDIR)/include ../../taskbar +LIBS += -lqpe +VERSION = 1.0.0 + +TRANSLATIONS += ../../i18n/de/libqpickboard.ts diff --git a/inputmethods/pickboard/pickboardcfg.cpp b/inputmethods/pickboard/pickboardcfg.cpp new file mode 100644 index 0000000..e8b47cb --- a/dev/null +++ b/inputmethods/pickboard/pickboardcfg.cpp @@ -0,0 +1,731 @@ +/********************************************************************** +** 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 "pickboardcfg.h" +#include "pickboardpicks.h" + +#include <qpe/global.h> + +#include <qpainter.h> +#include <qlist.h> +#include <qbitmap.h> +#include <qlayout.h> +#include <qvbox.h> +#include <qdialog.h> +#include <qscrollview.h> +#include <qpopupmenu.h> +#include <qhbuttongroup.h> +#include <qpushbutton.h> +#include <qmessagebox.h> +#include <qwindowsystem_qws.h> + +static const char * pickboard_help = + "<h1>The Pickboard</h1>" + "<i>The smallest and fastest way to type.</i>" + "<p>" + "Enter a word by tapping letter-groups and picking the word." + "<br>Enter spaces with \"Space\", or other keys through \"KEY\"." + "<br>Use \"Shift\" to capitalize words that are not normally capitalized." + "<br>Press \"Shift\" twice for an all-capitals word." + "<br>Add custom words by picking them, then selecting \"Add...\" from the menu on the right." + ; + +const int intermatchmargin=5; + + +PickboardConfig::~PickboardConfig() { } + +void PickboardConfig::updateRows(int from, int to) +{ + if ( from != to ) { // (all) + parent->update(); + } else { + QFontMetrics fm = parent->fontMetrics(); + parent->update(QRect(0,1+fm.descent() + from * fm.lineSpacing(), parent->width(), + fm.lineSpacing())); + } +} + +void PickboardConfig::updateItem(int r, int) +{ + updateRows(r,r); +} + +void PickboardConfig::changeMode(int m) +{ + parent->setMode(m); +} +void PickboardConfig::generateText(const QString& s) +{ +#if defined(Q_WS_QWS) || defined(_WS_QWS_) + for (int i=0; i<(int)s.length(); i++) { + parent->emitKey(s[i].unicode(), 0, 0, true, false); + parent->emitKey(s[i].unicode(), 0, 0, false, false); + } +#endif +} +void PickboardConfig::generateKey( int k ) +{ +#if defined(Q_WS_QWS) || defined(_WS_QWS_) + parent->emitKey(0, k, 0, true, false); + parent->emitKey(0, k, 0, false, false); +#endif +} + +void PickboardConfig::pickPoint(const QPoint& p, bool press) +{ + if ( press ) { + int ls=parent->height()/nrows; + int y=0; + pressx = -1; + for (int r=0; r<nrows; r++) { + if ( p.y() >= y && p.y() < y+ls ) { + pressrow = r; + pressx = p.x(); + pickInRow( pressrow, pressx, TRUE ); + return; + } + y += ls; + } + } else if ( pressx >= 0 ) { + pickInRow( pressrow, pressx, FALSE ); + pressx = -1; + } +} + +void PickboardConfig::fillMenu(QPopupMenu& menu) +{ + menu.insertItem("Reset",100); + menu.insertSeparator(); + menu.insertItem("Help",1); +} + +void PickboardConfig::doMenu(int i) +{ + switch (i) { + case 100: + if ( parent->currentMode() ) { + changeMode(0); + updateRows(0,1); + } + break; + case 1: { + QMessageBox help("Pickboard Help", pickboard_help, + QMessageBox::NoIcon, 1, 0, 0); + help.showMaximized(); + help.exec(); + } + } +} + +void StringConfig::draw(QPainter* p) +{ + QFontMetrics fm = p->fontMetrics(); + + for (int r=0; r<nrows; r++) { + p->translate(0,fm.lineSpacing()); + p->setPen(rowColor(r)); + + int tw=0; + QString s; + int i=0; + for (; !(s=text(r,i)).isNull(); ++i) { + int w = fm.width(s); + tw += w; + } + bool spread = spreadRow(r);// && parent->width() > tw; + int xw = spread ? (parent->width()-tw)/(i-1) : 3; + int x = spread ? (parent->width()-tw-xw*(i-1))/2 : 2; + + i=0; + for (; !(s=text(r,i)).isNull(); ++i) { + int w = fm.width(s)+xw; + if ( highlight(r,i) ) { + p->fillRect(x-xw/2,1+fm.descent()-fm.lineSpacing(),w,fm.lineSpacing(),Qt::black); + p->setPen(Qt::white); + }else{ + p->setPen(Qt::black); + } + p->drawText(x,-fm.descent()-1,s); + x += w; + } + } +} + +void StringConfig::pickInRow(int r, int xpos, bool press) +{ + QFontMetrics fm = parent->fontMetrics(); + + int tw=0; + QString s; + int i=0; + for (; !(s=text(r,i)).isNull(); ++i) { + int w = fm.width(s); + tw += w; + } + bool spread = spreadRow(r) && parent->width() > tw; + int xw = spread ? (parent->width()-tw)/(i-1) : 3; + int x = spread ? (parent->width()-tw-xw*(i-1))/2 : 2; + + i=0; + for (; !(s=text(r,i)).isNull(); ++i) { + int x2 = x + fm.width(s)+xw; + if ( xpos >= x && xpos < x2 ) { + pick(press, r, i); + return; + } + x = x2; + } +} + +void StringConfig::updateItem(int r, int item) +{ + QFontMetrics fm = parent->fontMetrics(); + + int y = r * fm.lineSpacing(); + + int tw=0; + QString s; + int i=0; + for (; !(s=text(r,i)).isNull(); ++i) { + int w = fm.width(s); + tw += w; + } + bool spread = spreadRow(r) && parent->width() > tw; + int xw = spread ? (parent->width()-tw)/(i-1) : 3; + int x = spread ? (parent->width()-tw-xw*(i-1))/2 : 2; + + i=0; + for (; !(s=text(r,i)).isNull(); ++i) { + int w = fm.width(s)+xw; + if ( i == item ) { + parent->update(QRect(x-xw/2,y+1+fm.descent(),w,fm.lineSpacing())); + return; + } + x += w; + } +} + +bool StringConfig::highlight(int,int) const +{ + return FALSE; +} + +LetterButton::LetterButton(const QChar& letter, QWidget* parent) : + QPushButton(letter,parent) +{ + setToggleButton(TRUE); + setAutoDefault(FALSE); + connect(this,SIGNAL(clicked()),this,SLOT(toggleCase())); + skip=TRUE; +} + +void LetterButton::toggleCase() +{ + if ( skip ) { + // Don't toggle case the first time + skip=FALSE; + return; + } + + QChar ch = text()[0]; + QChar nch = ch.lower(); + if ( ch == nch ) + nch = ch.upper(); + setText(nch); +} + +LetterChoice::LetterChoice(QWidget* parent, const QString& set) : + QButtonGroup(parent) +{ + QHBoxLayout *l = new QHBoxLayout(this); + setFrameStyle(0); + setExclusive(TRUE); + for (int i=0; i<(int)set.length(); i++) { + LetterButton* b = new LetterButton(set[i],this); + l->addWidget(b,1,AlignCenter); + connect(b,SIGNAL(clicked()),this,SLOT(change())); + } +} + +void LetterChoice::change() +{ + LetterButton* b = (LetterButton*)sender(); + ch = b->text()[0]; + emit changed(); +} + + +PickboardAdd::PickboardAdd(QWidget* owner, const QStringList& setlist) : + QDialog( owner, 0, TRUE ) +{ + QVBoxLayout* l = new QVBoxLayout(this); + l->setAutoAdd(TRUE); + + QScrollView *sv = new QScrollView(this); + sv->setResizePolicy(QScrollView::AutoOneFit); + setMaximumHeight(200); // ### QDialog shouldn't allow us to be bigger than the screen + QVBox *letters = new QVBox(sv); + letters->setSpacing(0); + lc = new LetterChoice*[setlist.count()]; + nlc = (int)setlist.count(); + for (int i=0; i<nlc; i++) { + lc[i] = new LetterChoice(letters,setlist[i]); + connect(lc[i],SIGNAL(changed()),this,SLOT(checkAllDone())); + } + sv->addChild(letters); + QHBox* hb = new QHBox(this); + hb->setSpacing(0); + yes = new QPushButton("OK",hb); + yes->setEnabled(FALSE); + QPushButton *no = new QPushButton("Cancel",hb); + connect(yes, SIGNAL(clicked()), this, SLOT(accept())); + connect(no, SIGNAL(clicked()), this, SLOT(reject())); +} + +PickboardAdd::~PickboardAdd() +{ + delete [] lc; +} + +QString PickboardAdd::word() const +{ + QString str; + for (int i=0; i<nlc; i++) { + str += lc[i]->choice(); + } + return str; +} + +bool PickboardAdd::exec() +{ + QPoint pos = parentWidget()->mapToGlobal(QPoint(0,0)); + pos.ry() -= height(); + if ( QDialog::exec() ) { + Global::addWords(QStringList(word())); + return TRUE; + } else { + return FALSE; + } +} + +void PickboardAdd::checkAllDone() +{ + if ( !yes->isEnabled() ) { + for (int i=0; i<nlc; i++) { + if ( lc[i]->choice().isNull() ) + return; + } + yes->setEnabled(TRUE); + } +} + + +void DictFilterConfig::doMenu(int i) +{ + switch (i) { + case 300: + if ( input.count() == 0 ) { + QMessageBox::information(0, "Adding Words", + "To add words, pick the letters,\nthen " + "open the Add dialog. In that\ndialog, tap " + "the correct letters\nfrom the list " + "(tap twice for\ncapitals)."); + } else { + PickboardAdd add(parent,capitalize(input)); + if ( add.exec() ) + generateText(add.word()); + input.clear(); + matches.clear(); + updateRows(0,0); + } + break; + case 100: + if ( !input.isEmpty() ) { + input.clear(); + matches.clear(); + StringConfig::doMenu(i); + updateRows(0,1); + break; + } // else fall through + default: + StringConfig::doMenu(i); + } + shift = 0; + lit0 = -1; +} + +QString DictFilterConfig::text(int r, int i) +{ + QStringList l = r ? sets : input.isEmpty() ? othermodes : matches; + return i < (int)l.count() ? + (input.isEmpty() ? l[i] : capitalize(l[i])) + : QString::null; +} + +bool DictFilterConfig::spreadRow(int r) +{ + return r ? TRUE : input.isEmpty() ? TRUE : FALSE; +} + +QStringList DictFilterConfig::capitalize(const QStringList& l) +{ + switch ( shift ) { + case 1: { + QStringList r; + QStringList::ConstIterator it = l.begin(); + r.append((*it).upper()); + for (++it; it != l.end(); ++it) + r.append(*it); + return r; + } case 2: { + QStringList r; + for (QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) + r.append((*it).upper()); + return r; + } + } + return l; +} + +QString DictFilterConfig::capitalize(const QString& s) +{ + switch ( shift ) { + case 1: { + QString u = s; + u[0] = u[0].upper(); + return u; + break; + } case 2: + return s.upper(); + break; + } + return s; +} + +void DictFilterConfig::pick(bool press, int row, int item) +{ + if ( row == 0 ) { + if ( press ) { + if ( input.isEmpty() ) { + lit0 = item; + if ( othermodes[item] == "Space" ) { + updateItem(row,item); + generateText(" "); + } else if ( othermodes[item] == "Back" ) { + updateItem(row,item); + generateKey(Qt::Key_Backspace); + } else if ( othermodes[item] == "Enter" ) { + updateItem(row,item); + generateKey(Qt::Key_Return); + } else if ( othermodes[item] == "Shift" ) { + updateItem(row,item); + shift = (shift+1)%3; + } + } + } else { + if ( !input.isEmpty() ) { + input.clear(); + if ( item>=0 ) { + generateText(capitalize(matches[item])); + } + shift = 0; + matches.clear(); + updateRows(0,0); + } else if ( item < 3 ) { + lit0 = -1; + changeMode(item+1); // I'm mode 0! #### + updateRows(0,1); + } + if ( lit0 >= 0 ) { + if ( !shift || othermodes[lit0] != "Shift" ) { + updateItem(0,lit0); + lit0 = -1; + } + } + } + } else { + lit0 = -1; + if ( press && item >= 0 ) { + lit1 = item; + add(sets[item]); + updateItem(1,item); + updateRows(0,0); + } else { + updateItem(1,lit1); + lit1 = -1; + } + } +} + +bool DictFilterConfig::scanMatch(const QString& set, const QChar& l) const +{ + return set == "?" || set == "*" || set.contains(l); +} + +//static int visit=0; +//static int lvisit=0; + +void DictFilterConfig::scan(const QDawg::Node* n, int ipos, const QString& str, int length, bool extend) +{ + if ( n ) { + do { +//visit++; + bool pastend = ipos >= (int)input.count(); + if ( pastend && extend || !pastend && scanMatch(input[ipos],n->letter().lower()) ) { + if ( length>1 ) { + if ( !pastend && input[ipos] == "*" ) { + scan(n->jump(),ipos+1,str+n->letter(),length-1,FALSE); + scan(n->jump(),ipos,str+n->letter(),length,FALSE); + } else { + scan(n->jump(),ipos+1,str+n->letter(),length-1,extend); + } + } else { + if ( n->isWord() ) { + matches.append(str+n->letter()); + } + } + } + n = n->next(); + } while (n); + } +} + +void DictFilterConfig::scanLengths(const QDawg::Node* n, int ipos, int& length_bitarray) +{ + if ( n ) { + do { +//lvisit++; + bool pastend = ipos >= (int)input.count(); + if ( pastend || scanMatch(input[ipos],n->letter().lower()) ) { + scanLengths(n->jump(),ipos+1,length_bitarray); + if ( n->isWord() ) + length_bitarray |= (1<<(ipos+1)); + } + n = n->next(); + } while (n); + } +} + +void DictFilterConfig::add(const QString& set) +{ + QFontMetrics fm = parent->fontMetrics(); + input.append(set.lower()); + matches.clear(); +//visit=0; +//lvisit=0; + int length_bitarray = 0; + if ( input.count() > 4 ) { + scanLengths(Global::addedDawg().root(),0,length_bitarray); + scanLengths(Global::fixedDawg().root(),0,length_bitarray); + } else { + length_bitarray = 0xffffffff; + } + for (int len=input.count(); len<22 /* 32 */; ++len) { + if ( length_bitarray & (1<<len) ) { + scan(Global::addedDawg().root(),0,"",len,TRUE); + scan(Global::fixedDawg().root(),0,"",len,TRUE); + int x = 2; + for (QStringList::Iterator it=matches.begin(); it!=matches.end(); ++it) { + x += fm.width(*it)+intermatchmargin; + if ( x >= parent->width() ) { +//qDebug("%d+%d visits",lvisit,visit); + return; // RETURN - No point finding more + } + } + } + if ( len == 1 && input.count() == 1 ) { + // Allow all single-characters to show as "matches" + for ( int i=0; i<(int)set.length(); i++ ) { + QChar ch = set[i].lower(); + matches.append(ch); + } + } + } +//qDebug("%d+%d visits",lvisit,visit); +} + +bool DictFilterConfig::highlight(int r,int c) const +{ + return r == 0 ? c == lit0 : c == lit1; +} + + +void DictFilterConfig::addSet(const QString& s) +{ + sets.append(s); +} + +void DictFilterConfig::addMode(const QString& s) +{ + othermodes.append(s); +} + +void DictFilterConfig::fillMenu(QPopupMenu& menu) +{ + menu.insertItem("Add...",300); + StringConfig::fillMenu(menu); +} + +QValueList<QPixmap> KeycodeConfig::row(int i) +{ + return i ? keypm2 : keypm1; +} + +void KeycodeConfig::pickInRow(int r, int xpos, bool press) +{ + QValueList<QPixmap> pl = row(r); + QValueList<QPixmap>::Iterator it; + int item=0; + int x=xmarg; + for (it=pl.begin(); it!=pl.end(); ++it) { + int x2 = x + (*it).width(); + if ( (*it).height() > 1 ) + x2 += xw; + if ( xpos >= x && xpos < x2 ) { + pick(press, r, item); + return; + } + x = x2; + item++; + } +} + +void KeycodeConfig::pick(bool press, int row, int item) +{ + if ( !press ) { + if ( item >= 0 ) { + int k = row == 0 ? keys1[item] : keys2[item]; + if ( k ) + generateKey(k); + } + changeMode(0); + updateRows(0,1); + } +} + +void KeycodeConfig::draw(QPainter* p) +{ + int y=3; + QValueList<QPixmap>::Iterator it; + for (int r=0; r<nrows; r++) { + QValueList<QPixmap> pl = row(r); + int x = xmarg; + for (it=pl.begin(); it!=pl.end(); ++it) { + if ( (*it).height() == 1 ) { + // just a gap + x += (*it).width(); + } else { + p->drawPixmap(x,y,*it); + x += (*it).width()+xw; + } + } + y += parent->height()/nrows; + } +} + + +void KeycodeConfig::addKey(int r, const QPixmap& pm, int code) +{ + if ( r == 0 ) { + keypm1.append(pm); + keys1.append(code); + } else { + keypm2.append(pm); + keys2.append(code); + } +} +void KeycodeConfig::addGap(int r, int w) +{ + QBitmap pm(w,1); // ick. + addKey(r,pm,0); +} + +QString CharConfig::text(int r, int i) +{ + QStringList l = r ? chars2 : chars1; + return i < (int)l.count() ? l[i] : QString::null; +} +bool CharConfig::spreadRow(int) +{ + return TRUE; +} + +void CharConfig::pick(bool press, int row, int item) +{ + if ( !press ) { + if ( item >= 0 ) { + generateText(row == 0 ? chars1[item] : chars2[item]); + } + changeMode(0); + updateRows(0,1); + } +} + +void CharConfig::addChar(int r, const QString& s) +{ + if ( r ) chars2.append(s); else chars1.append(s); +} + +QString CharStringConfig::text(int r, int i) +{ + QStringList l = r ? chars : QStringList(input); + return i < (int)l.count() ? l[i] : QString::null; +} + +bool CharStringConfig::spreadRow(int i) +{ + return i ? TRUE : FALSE; +} + +void CharStringConfig::pick(bool press, int row, int item) +{ + if ( row == 0 ) { + if ( !press ) { + if ( item>=0 ) { + generateText(input); + } + input = ""; + changeMode(0); + updateRows(0,1); + } + } else { + if ( press && item >= 0 ) { + input.append(chars[item]); + updateRows(0,0); + } + } +} + +void CharStringConfig::addChar(const QString& s) +{ + chars.append(s); +} + +void CharStringConfig::doMenu(int i) +{ + if ( i == 100 ) { + input = ""; + updateRows(0,0); + } + + StringConfig::doMenu(i); +} + diff --git a/inputmethods/pickboard/pickboardcfg.h b/inputmethods/pickboard/pickboardcfg.h new file mode 100644 index 0000000..e0dc0dd --- a/dev/null +++ b/inputmethods/pickboard/pickboardcfg.h @@ -0,0 +1,213 @@ +/********************************************************************** +** 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 PICKBOARDCFG_H +#define PICKBOARDCFG_H + +#include <qpe/qdawg.h> + +#include <qpushbutton.h> +#include <qhbuttongroup.h> +#include <qdialog.h> +#include <qlist.h> + +// Internal stuff... + +class PickboardPicks; + +class LetterButton : public QPushButton { + Q_OBJECT +public: + LetterButton(const QChar& letter, QWidget* parent); +private slots: + void toggleCase(); +private: + bool skip; +}; + +class LetterChoice : public QButtonGroup { + Q_OBJECT +public: + LetterChoice(QWidget* parent, const QString& set); + + QChar choice() { return ch; } + +signals: + void changed(); + +private slots: + void change(); + +private: + QChar ch; +}; + +class PickboardAdd : public QDialog { + Q_OBJECT +public: + PickboardAdd(QWidget* owner, const QStringList& setlist); + ~PickboardAdd(); + + QString word() const; + bool exec(); + +private slots: + void checkAllDone(); + +private: + QPushButton *yes; + LetterChoice **lc; + int nlc; +}; + +class PickboardConfig : QObject { +public: + PickboardConfig(PickboardPicks* p) : parent(p), nrows(2), pressx(-1) { } + virtual ~PickboardConfig(); + virtual void pickPoint(const QPoint& p, bool press); + virtual void draw(QPainter*)=0; + virtual void fillMenu(QPopupMenu&); + virtual void doMenu(int); + +protected: + void updateRows(int from, int to); + virtual void updateItem(int r, int i); + virtual void pickInRow(int r, int xpos, bool press)=0; + + void changeMode(int m); + virtual void generateText(const QString& s); + void generateKey( int k ); + + virtual void pick(bool press, int row, int item)=0; + +protected: + PickboardPicks* parent; + int nrows; +private: + int pressrow, pressx; +}; + +class StringConfig : public PickboardConfig { +public: + StringConfig(PickboardPicks* p) : PickboardConfig(p) { } + + void draw(QPainter* p); + +protected: + virtual QString text(int r, int i)=0; + virtual bool spreadRow(int i)=0; + virtual QColor rowColor(int) { return Qt::black; } + virtual void pickInRow(int r, int xpos, bool press); + virtual void updateItem(int r, int i); + virtual bool highlight(int,int) const; +}; + +class CharStringConfig : public StringConfig { + QString input; + QStringList chars; +public: + CharStringConfig(PickboardPicks* p) : StringConfig(p) { } + + void addChar(const QString& s); + virtual void doMenu(int); + +protected: + QString text(int r, int i); + bool spreadRow(int i); + void pick(bool press, int row, int item); +}; + +class DictFilterConfig : public StringConfig { + QStringList matches; + QStringList sets; + QStringList othermodes; + int lit0; + int lit1; + int shift; + QString capitalize(const QString& s); + QStringList capitalize(const QStringList& s); + +public: + QStringList input; + DictFilterConfig(PickboardPicks* p) : StringConfig(p) + { + shift = 0; + lit0 = -1; + lit1 = -1; + } + + void addSet(const QString& s); + void addMode(const QString& s); + + void fillMenu(QPopupMenu& menu); + void doMenu(int i); + + void add(const QString& set); + +protected: + QString text(int r, int i); + + bool spreadRow(int i); + + void pick(bool press, int row, int item); + + bool scanMatch(const QString& set, const QChar& l) const; + void scan(const QDawg::Node* n, int ipos, const QString& str, int length, bool extend); + void scanLengths(const QDawg::Node* n, int ipos, int& bitarray); + + bool highlight(int r,int c) const; +}; + +class CharConfig : public StringConfig { + QStringList chars1; + QStringList chars2; +public: + CharConfig(PickboardPicks* p) : StringConfig(p) { } + void addChar(int r, const QString& s); + +protected: + QString text(int r, int i); + bool spreadRow(int); + void pick(bool press, int row, int item); +}; + +class KeycodeConfig : public PickboardConfig { + QValueList<int> keys1; + QValueList<int> keys2; + QValueList<QPixmap> keypm1; + QValueList<QPixmap> keypm2; + static const int xw = 8; + static const int xmarg = 8; + +public: + KeycodeConfig(PickboardPicks* p) : PickboardConfig(p) { } + void addKey(int r, const QPixmap& pm, int code); + void addGap(int r, int w); + + void draw(QPainter* p); + +protected: + void pickInRow(int r, int xpos, bool press); + QValueList<QPixmap> row(int i); + + void pick(bool press, int row, int item); +}; + + +#endif diff --git a/inputmethods/pickboard/pickboardimpl.cpp b/inputmethods/pickboard/pickboardimpl.cpp new file mode 100644 index 0000000..a4e8f02 --- a/dev/null +++ b/inputmethods/pickboard/pickboardimpl.cpp @@ -0,0 +1,101 @@ +/********************************************************************** +** 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 <qobject.h> +#include <qpixmap.h> +#include "pickboard.h" +#include "pickboardimpl.h" + +/* XPM */ +static const char * pb_xpm[]={ +"28 7 2 1", +"# c #303030", +" c None", +" ########################## ", +" # # # # # # # ", +" # # # # # # # ", +" ########################## ", +" # # # # # # ", +" # # # # # # ", +" ########################## "}; + + +PickboardImpl::PickboardImpl() + : pickboard(0), icn(0), ref(0) +{ +} + +PickboardImpl::~PickboardImpl() +{ + delete pickboard; + delete icn; +} + +QWidget *PickboardImpl::inputMethod( QWidget *parent, Qt::WFlags f ) +{ + if ( !pickboard ) + pickboard = new Pickboard( parent, "pickboard", f ); + return pickboard; +} + +void PickboardImpl::resetState() +{ + if ( pickboard ) + pickboard->resetState(); +} + +QPixmap *PickboardImpl::icon() +{ + if ( !icn ) + icn = new QPixmap( (const char **)pb_xpm ); + return icn; +} + +QString PickboardImpl::name() +{ + return qApp->translate( "InputMethods", "Pickboard" ); +} + +void PickboardImpl::onKeyPress( QObject *receiver, const char *slot ) +{ + if ( pickboard ) + QObject::connect( pickboard, SIGNAL(key(ushort,ushort,ushort,bool,bool)), receiver, slot ); +} + +#ifndef QT_NO_COMPONENT +QRESULT PickboardImpl::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( PickboardImpl ) +} +#endif + diff --git a/inputmethods/pickboard/pickboardimpl.h b/inputmethods/pickboard/pickboardimpl.h new file mode 100644 index 0000000..4f23665 --- a/dev/null +++ b/inputmethods/pickboard/pickboardimpl.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 PICKBOARDIMPL_H +#define PICKBOARDIMPL_H + +#include <qpe/inputmethodinterface.h> + +class Pickboard; +class QPixmap; + +class PickboardImpl : public InputMethodInterface +{ +public: + PickboardImpl(); + virtual ~PickboardImpl(); + +#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: + Pickboard *pickboard; + QPixmap *icn; + ulong ref; +}; + +#endif diff --git a/inputmethods/pickboard/pickboardpicks.cpp b/inputmethods/pickboard/pickboardpicks.cpp new file mode 100644 index 0000000..a80bbf8 --- a/dev/null +++ b/inputmethods/pickboard/pickboardpicks.cpp @@ -0,0 +1,418 @@ +/********************************************************************** +** 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 "pickboardpicks.h" +#include "pickboardcfg.h" + +#include <qpe/global.h> + +#include <qpainter.h> +#include <qlist.h> +#include <qbitmap.h> +#include <qlayout.h> +#include <qvbox.h> +#include <qdialog.h> +#include <qscrollview.h> +#include <qpopupmenu.h> +#include <qhbuttongroup.h> +#include <qpushbutton.h> +#include <qmessagebox.h> +#include <qwindowsystem_qws.h> + +void PickboardPicks::doMenu() +{ + QWidget* cause = (QWidget*)sender(); // evil + + QPopupMenu popup(this); + config()->fillMenu(popup); + + QPoint pos = cause->mapToGlobal(cause->rect().topRight()); + QSize sz = popup.sizeHint(); + pos.ry() -= sz.height(); + pos.rx() -= sz.width(); + popup.move(pos); + config()->doMenu(popup.exec()); +} + +static const char *BS_xpm[] = { +"5 7 2 1", +"a c #000000", +". c None", +"...aa", +"..aaa", +".aaaa", +"aaaaa", +".aaaa", +"..aaa", +"...aa", +}; +static const char *Del_xpm[] = { +"14 7 2 1", +"a c #000000", +". c None", +"aaa..aaaa.a...", +"a..a.a....a...", +"a..a.a....a...", +"a..a.aaa..a...", +"a..a.a....a...", +"a..a.a....a...", +"aaa..aaaa.aaaa" +}; +static const char *Home_xpm[] = { +"20 7 2 1", +"a c #000000", +". c None", +"a..a..aa..a...a.aaaa", +"a..a.a..a.aa.aa.a...", +"a..a.a..a.a.a.a.a...", +"aaaa.a..a.a.a.a.aaa.", +"a..a.a..a.a...a.a...", +"a..a.a..a.a...a.a...", +"a..a..aa..a...a.aaaa" +}; +static const char *PgUp_xpm[] = { +"20 7 2 1", +"a c #000000", +". c None", +"aaa.......a..a......", +"a..a......a..a......", +"a..a.aa...a..a.aaa..", +"aaa.a.....a..a.a..a.", +"a...a.aa..a..a.aaa..", +"a...a..a..a..a.a....", +"a....aaa...aa..a...." +}; +static const char *PgDn_xpm[] = { +"20 7 2 1", +"a c #000000", +". c None", +"aaa.......aaa.......", +"a..a......a..a......", +"a..a.aa...a..a.a..a.", +"aaa.a.....a..a.aa.a.", +"a...a.aa..a..a.a.aa.", +"a...a..a..a..a.a..a.", +"a....aaa..aaa..a..a." +}; +static const char *End_xpm[] = { +"14 7 2 1", +"a c #000000", +". c None", +"aaaa.a..a.aaa.", +"a....aa.a.a..a", +"a....a.aa.a..a", +"aaa..a..a.a..a", +"a....a..a.a..a", +"a....a..a.a..a", +"aaaa.a..a.aaa." +}; +static const char *Enter_xpm[] = { +"14 7 2 1", +"a c #000000", +". c None", +".............a", +".............a", +"..a..........a", +".aa.........a.", +"aaaaaaaaaaaa..", +".aa...........", +"..a..........." +}; +static const char *Esc_xpm[] = { +"14 7 2 1", +"a c #000000", +". c None", +"aaaa..aa...aa.", +"a....a..a.a..a", +"a....a....a...", +"aaa...aa..a...", +"a.......a.a...", +"a....a..a.a..a", +"aaaa..aa...aa." +}; +static const char *Ins_xpm[] = { +"13 7 2 1", +"a c #000000", +". c None", +"aaa.a..a..aa.", +".a..aa.a.a..a", +".a..a.aa.a...", +".a..a..a..aa.", +".a..a..a....a", +".a..a..a.a..a", +"aaa.a..a..aa." +}; +static const char *Up_xpm[] = { +"7 7 2 1", +"a c #000000", +". c None", +"...a...", +"..aaa..", +".a.a.a.", +"a..a..a", +"...a...", +"...a...", +"...a..." +}; +static const char *Left_xpm[] = { +"7 7 2 1", +"a c #000000", +". c None", +"...a...", +"..a....", +".a.....", +"aaaaaaa", +".a.....", +"..a....", +"...a..." +}; +static const char *Down_xpm[] = { +"7 7 2 1", +"a c #000000", +". c None", +"...a...", +"...a...", +"...a...", +"a..a..a", +".a.a.a.", +"..aaa..", +"...a..." +}; +static const char *Right_xpm[] = { +"7 7 2 1", +"a c #000000", +". c None", +"...a...", +"....a..", +".....a.", +"aaaaaaa", +".....a.", +"....a..", +"...a..." +}; +static const char *BackTab_xpm[] = { +"8 7 2 1", +"a c #000000", +". c None", +"a.......", +"a..a....", +"a.aa....", +"aaaaaaaa", +"a.aa....", +"a..a....", +"a......." +}; +static const char *Tab_xpm[] = { +"8 7 2 1", +"a c #000000", +". c None", +".......a", +"....a..a", +"....aa.a", +"aaaaaaaa", +"....aa.a", +"....a..a", +".......a" +}; +static const char *Space_xpm[] = { +"9 9 2 1", +"a c #000000", +". c None", +"aaaaaaaaa", +"a.......a", +"a.......a", +"a.......a", +"a.......a", +"a.......a", +"a.......a", +"a.......a", +"aaaaaaaaa" +}; + +PickboardPicks::PickboardPicks(QWidget* parent, const char* name, WFlags f ) : + QFrame(parent,name,f) +{ +} + +void PickboardPicks::initialise(void) +{ + setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed)); + mode = 0; + + DictFilterConfig* dc = new DictFilterConfig(this); + dc->addSet("ABC"); + dc->addSet("DEF"); + dc->addSet("GHI"); + dc->addSet("JKL"); + dc->addSet("MNO"); + dc->addSet("PQR"); + dc->addSet("STU"); + dc->addSet("VWX"); + dc->addSet("YZ-'"); + dc->addMode("123"); + dc->addMode("@*!?"); + dc->addMode("KEY"); + dc->addMode("Space"); + dc->addMode("Back"); + dc->addMode("Enter"); + dc->addMode("Shift"); + configs.append(dc); + + CharStringConfig* number = new CharStringConfig(this); + number->addChar("0"); + number->addChar("1"); + number->addChar("2"); + number->addChar("3"); + number->addChar("4"); + number->addChar("5"); + number->addChar("6"); + number->addChar("7"); + number->addChar("8"); + number->addChar("9"); + number->addChar("."); // #### or "," in some locales + configs.append(number); + + CharConfig* punc = new CharConfig(this); + + punc->addChar(0,"\""); + punc->addChar(0,"`"); + punc->addChar(0,"'"); + punc->addChar(0,"\253"); + punc->addChar(0,"\273"); + punc->addChar(0,"\277"); + punc->addChar(1,"("); + punc->addChar(1,")"); + punc->addChar(1,"["); + punc->addChar(1,"]"); + punc->addChar(1,"{"); + punc->addChar(1,"}"); + + punc->addChar(0,"+"); + punc->addChar(0,"-"); + punc->addChar(0,"*"); + punc->addChar(0,"/"); + punc->addChar(0,"="); + punc->addChar(0,"_"); + punc->addChar(0,"$"); + punc->addChar(0,"&"); + punc->addChar(1,"|"); + punc->addChar(1,"@"); + punc->addChar(1,"\\"); + punc->addChar(1,"#"); + punc->addChar(1,"^"); + punc->addChar(1,"~"); + punc->addChar(1,"<"); + punc->addChar(1,">"); + + punc->addChar(0,"."); + punc->addChar(0,"?"); + punc->addChar(0,"!"); + punc->addChar(0,","); + punc->addChar(0,";"); + punc->addChar(1,":"); + punc->addChar(1,"\267"); + punc->addChar(1,"\277"); + punc->addChar(1,"\241"); + punc->addChar(1,"\367"); + + punc->addChar(0,"$"); + punc->addChar(0,"\242"); + punc->addChar(0,"\245"); + punc->addChar(1,"\243"); + punc->addChar(1,"\244"); + punc->addChar(1,"\260"); + + configs.append(punc); + + KeycodeConfig* keys = new KeycodeConfig(this); + keys->addKey(0,QPixmap(Esc_xpm),Key_Escape); + keys->addKey(0,QPixmap(BS_xpm),Key_Backspace); + keys->addGap(0,10); + + keys->addKey(0,QPixmap(Ins_xpm),Key_Insert); + keys->addKey(0,QPixmap(Home_xpm),Key_Home); + keys->addKey(0,QPixmap(PgUp_xpm),Key_PageUp); + + keys->addGap(0,25); + keys->addKey(0,QPixmap(Up_xpm),Key_Up); + keys->addGap(0,15); + + keys->addKey(1,QPixmap(BackTab_xpm),Key_Tab); + keys->addGap(1,3); + keys->addKey(1,QPixmap(Tab_xpm),Key_Tab); + keys->addGap(1,10); + + keys->addKey(1,QPixmap(Del_xpm),Key_Delete); + keys->addGap(1,2); + keys->addKey(1,QPixmap(End_xpm),Key_End); + keys->addGap(1,3); + keys->addKey(1,QPixmap(PgDn_xpm),Key_PageDown); + + keys->addGap(1,10); + keys->addKey(1,QPixmap(Left_xpm),Key_Left); + keys->addKey(1,QPixmap(Down_xpm),Key_Down); + keys->addKey(1,QPixmap(Right_xpm),Key_Right); + + keys->addGap(1,13); + keys->addKey(1,QPixmap(Space_xpm),Key_Space); + + keys->addGap(0,10); + keys->addKey(0,QPixmap(Enter_xpm),Key_Return); + + configs.append(keys); +} + +PickboardPicks::~PickboardPicks() +{ +} + +QSize PickboardPicks::sizeHint() const +{ + return QSize(240,fontMetrics().lineSpacing()*2+3); +} + +void PickboardPicks::drawContents(QPainter* p) +{ + config()->draw(p); +} + +void PickboardPicks::mousePressEvent(QMouseEvent* e) +{ + config()->pickPoint(e->pos(),TRUE); +} + +void PickboardPicks::mouseDoubleClickEvent(QMouseEvent* e) +{ + config()->pickPoint(e->pos(),TRUE); +} + +void PickboardPicks::mouseReleaseEvent(QMouseEvent* e) +{ + config()->pickPoint(e->pos(),FALSE); +} + +void PickboardPicks::setMode(int m) +{ + mode = m; +} + +void PickboardPicks::resetState() +{ + config()->doMenu(100); +} diff --git a/inputmethods/pickboard/pickboardpicks.h b/inputmethods/pickboard/pickboardpicks.h new file mode 100644 index 0000000..1881e92 --- a/dev/null +++ b/inputmethods/pickboard/pickboardpicks.h @@ -0,0 +1,66 @@ +/********************************************************************** +** 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 PICKBOARDPICKS_H +#define PICKBOARDPICKS_H + +#include <qframe.h> +#include <qlist.h> + +// Internal stuff... + +class PickboardConfig; + +class PickboardPicks : public QFrame { + Q_OBJECT +public: + PickboardPicks(QWidget* parent=0, const char* name=0, WFlags f=0); + ~PickboardPicks(); + QSize sizeHint() const; + void initialise(); + void setMode(int); + int currentMode() const { return mode; } + + void mousePressEvent(QMouseEvent*); + + void emitKey( ushort scan, ushort uni, ushort mod, bool press, bool repeat ) + { key(scan,uni,mod,press, repeat); } + void resetState(); + +signals: + void key( ushort scan, ushort uni, ushort mod, bool, bool ); + +public slots: + void doMenu(); + +protected: + void drawContents( QPainter * ); + void mouseDoubleClickEvent(QMouseEvent*); + void mouseReleaseEvent(QMouseEvent*); + +protected: + int mode; + QList<PickboardConfig> configs; + +private: + PickboardConfig* config() { return configs.at(mode); } +}; + + +#endif diff --git a/inputmethods/pickboard/qpe-pickboard.control b/inputmethods/pickboard/qpe-pickboard.control new file mode 100644 index 0000000..8d12181 --- a/dev/null +++ b/inputmethods/pickboard/qpe-pickboard.control @@ -0,0 +1,9 @@ +Files: plugins/inputmethods/libqpickboard.so* +Priority: optional +Section: qpe/inputmethods +Maintainer: Warwick Allison <warwick@trolltech.com> +Architecture: arm +Version: $QPE_VERSION-3 +Depends: qpe-base ($QPE_VERSION) +Description: Pickboard input method + Pickboard dictionary-based input method for the Qtopia environment. diff --git a/inputmethods/pickboard/qpe-pickboard.postinst b/inputmethods/pickboard/qpe-pickboard.postinst new file mode 100755 index 0000000..c254b01 --- a/dev/null +++ b/inputmethods/pickboard/qpe-pickboard.postinst @@ -0,0 +1,2 @@ +#!/bin/sh +/opt/QtPalmtop/bin/qcop QPE/TaskBar "reloadInputMethods()" diff --git a/inputmethods/pickboard/qpe-pickboard.postrm b/inputmethods/pickboard/qpe-pickboard.postrm new file mode 100755 index 0000000..c254b01 --- a/dev/null +++ b/inputmethods/pickboard/qpe-pickboard.postrm @@ -0,0 +1,2 @@ +#!/bin/sh +/opt/QtPalmtop/bin/qcop QPE/TaskBar "reloadInputMethods()" diff --git a/inputmethods/unikeyboard/.cvsignore b/inputmethods/unikeyboard/.cvsignore new file mode 100644 index 0000000..edfa921 --- a/dev/null +++ b/inputmethods/unikeyboard/.cvsignore @@ -0,0 +1,3 @@ +moc_* +*.moc +Makefile diff --git a/inputmethods/unikeyboard/Makefile.in b/inputmethods/unikeyboard/Makefile.in new file mode 100644 index 0000000..ebbab50 --- a/dev/null +++ b/inputmethods/unikeyboard/Makefile.in @@ -0,0 +1,115 @@ +############################################################################# + +####### 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 = qunikeyboard +TARGET1 = lib$(TARGET).so.$(VER_MAJ) + +####### Files + +HEADERS = unikeyboard.h \ + unikeyboardimpl.h +SOURCES = unikeyboard.cpp \ + unikeyboardimpl.cpp +OBJECTS = unikeyboard.o \ + unikeyboardimpl.o +INTERFACES = +UICDECLS = +UICIMPLS = +SRCMOC = moc_unikeyboard.cpp +OBJMOC = moc_unikeyboard.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 unikeyboard.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 + +unikeyboard.o: unikeyboard.cpp \ + unikeyboard.h + +unikeyboardimpl.o: unikeyboardimpl.cpp \ + unikeyboard.h \ + unikeyboardimpl.h + +moc_unikeyboard.o: moc_unikeyboard.cpp \ + unikeyboard.h + +moc_unikeyboard.cpp: unikeyboard.h + $(MOC) unikeyboard.h -o moc_unikeyboard.cpp + + diff --git a/inputmethods/unikeyboard/qpe-unikeyboard.control b/inputmethods/unikeyboard/qpe-unikeyboard.control new file mode 100644 index 0000000..a8c9bd3 --- a/dev/null +++ b/inputmethods/unikeyboard/qpe-unikeyboard.control @@ -0,0 +1,9 @@ +Files: plugins/inputmethods/libqunikeyboard.so* +Priority: optional +Section: qpe/inputmethods +Maintainer: Warwick Allison <warwick@trolltech.com> +Architecture: arm +Version: $QPE_VERSION-3 +Depends: qpe-base ($QPE_VERSION) +Description: Unicode input method + Basic unicode input method for the Qtopia environment. diff --git a/inputmethods/unikeyboard/qpe-unikeyboard.postinst b/inputmethods/unikeyboard/qpe-unikeyboard.postinst new file mode 100755 index 0000000..c254b01 --- a/dev/null +++ b/inputmethods/unikeyboard/qpe-unikeyboard.postinst @@ -0,0 +1,2 @@ +#!/bin/sh +/opt/QtPalmtop/bin/qcop QPE/TaskBar "reloadInputMethods()" diff --git a/inputmethods/unikeyboard/qpe-unikeyboard.postrm b/inputmethods/unikeyboard/qpe-unikeyboard.postrm new file mode 100755 index 0000000..c254b01 --- a/dev/null +++ b/inputmethods/unikeyboard/qpe-unikeyboard.postrm @@ -0,0 +1,2 @@ +#!/bin/sh +/opt/QtPalmtop/bin/qcop QPE/TaskBar "reloadInputMethods()" diff --git a/inputmethods/unikeyboard/unikeyboard.cpp b/inputmethods/unikeyboard/unikeyboard.cpp new file mode 100644 index 0000000..aa74c66 --- a/dev/null +++ b/inputmethods/unikeyboard/unikeyboard.cpp @@ -0,0 +1,278 @@ +/********************************************************************** +** 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 "unikeyboard.h" + +#include <qpe/fontmanager.h> + +#include <qpainter.h> +#include <qfontmetrics.h> +#include <qcombobox.h> +#if defined(Q_WS_QWS) || defined(_WS_QWS_) +#include <qwindowsystem_qws.h> +#endif + +static const int nw = 8; + +typedef struct BlockMap { + ushort start; + ushort stop; + const char *name; +}; + +//# Start Code; Block Name + +static const BlockMap blockMap[] = +{ +{0x0000, 0x007F, "Basic Latin"}, +{0x0080, 0x00FF, "Latin-1 Supplement"}, +{0x0100, 0x017F, "Latin Extended-A"}, +{0x0180, 0x024F, "Latin Extended-B"}, +{0x0250, 0x02AF, "IPA Extensions"}, +{0x02B0, 0x02FF, "Spacing Modifier Letters"}, +{0x0300, 0x036F, "Combining Diacritical Marks"}, +{0x0370, 0x03FF, "Greek"}, +{0x0400, 0x04FF, "Cyrillic"}, +{0x0530, 0x058F, "Armenian"}, +{0x0590, 0x05FF, "Hebrew"}, +{0x0600, 0x06FF, "Arabic"}, +{0x0700, 0x074F, "Syriac "}, +{0x0780, 0x07BF, "Thaana"}, +{0x0900, 0x097F, "Devanagari"}, +{0x0980, 0x09FF, "Bengali"}, +{0x0A00, 0x0A7F, "Gurmukhi"}, +{0x0A80, 0x0AFF, "Gujarati"}, +{0x0B00, 0x0B7F, "Oriya"}, +{0x0B80, 0x0BFF, "Tamil"}, +{0x0C00, 0x0C7F, "Telugu"}, +{0x0C80, 0x0CFF, "Kannada"}, +{0x0D00, 0x0D7F, "Malayalam"}, +{0x0D80, 0x0DFF, "Sinhala"}, +{0x0E00, 0x0E7F, "Thai"}, +{0x0E80, 0x0EFF, "Lao"}, +{0x0F00, 0x0FFF, "Tibetan"}, +{0x1000, 0x109F, "Myanmar "}, +{0x10A0, 0x10FF, "Georgian"}, +{0x1100, 0x11FF, "Hangul Jamo"}, +{0x1200, 0x137F, "Ethiopic"}, +{0x13A0, 0x13FF, "Cherokee"}, +{0x1400, 0x167F, "Unified Canadian Aboriginal Syllabics"}, +{0x1680, 0x169F, "Ogham"}, +{0x16A0, 0x16FF, "Runic"}, +{0x1780, 0x17FF, "Khmer"}, +{0x1800, 0x18AF, "Mongolian"}, +{0x1E00, 0x1EFF, "Latin Extended Additional"}, +{0x1F00, 0x1FFF, "Greek Extended"}, +{0x2000, 0x206F, "General Punctuation"}, +{0x2070, 0x209F, "Superscripts and Subscripts"}, +{0x20A0, 0x20CF, "Currency Symbols"}, +{0x20D0, 0x20FF, "Combining Marks for Symbols"}, +{0x2100, 0x214F, "Letterlike Symbols"}, +{0x2150, 0x218F, "Number Forms"}, +{0x2190, 0x21FF, "Arrows"}, +{0x2200, 0x22FF, "Mathematical Operators"}, +{0x2300, 0x23FF, "Miscellaneous Technical"}, +{0x2400, 0x243F, "Control Pictures"}, +{0x2440, 0x245F, "Optical Character Recognition"}, +{0x2460, 0x24FF, "Enclosed Alphanumerics"}, +{0x2500, 0x257F, "Box Drawing"}, +{0x2580, 0x259F, "Block Elements"}, +{0x25A0, 0x25FF, "Geometric Shapes"}, +{0x2600, 0x26FF, "Miscellaneous Symbols"}, +{0x2700, 0x27BF, "Dingbats"}, +{0x2800, 0x28FF, "Braille Patterns"}, +{0x2E80, 0x2EFF, "CJK Radicals Supplement"}, +{0x2F00, 0x2FDF, "Kangxi Radicals"}, +{0x2FF0, 0x2FFF, "Ideographic Description Characters"}, +{0x3000, 0x303F, "CJK Symbols and Punctuation"}, +{0x3040, 0x309F, "Hiragana"}, +{0x30A0, 0x30FF, "Katakana"}, +{0x3100, 0x312F, "Bopomofo"}, +{0x3130, 0x318F, "Hangul Compatibility Jamo"}, +{0x3190, 0x319F, "Kanbun"}, +{0x31A0, 0x31BF, "Bopomofo Extended"}, +{0x3200, 0x32FF, "Enclosed CJK Letters and Months"}, +{0x3300, 0x33FF, "CJK Compatibility"}, +{0x3400, 0x4DB5, "CJK Unified Ideographs Extension A"}, +{0x4E00, 0x9FFF, "CJK Unified Ideographs"}, +{0xA000, 0xA48F, "Yi Syllables"}, +{0xA490, 0xA4CF, "Yi Radicals"}, +{0xAC00, 0xD7A3, "Hangul Syllables"}, +{0xD800, 0xDB7F, "High Surrogates"}, +{0xDB80, 0xDBFF, "High Private Use Surrogates"}, +{0xDC00, 0xDFFF, "Low Surrogates"}, +{0xE000, 0xF8FF, "Private Use"}, +{0xF900, 0xFAFF, "CJK Compatibility Ideographs"}, +{0xFB00, 0xFB4F, "Alphabetic Presentation Forms"}, +{0xFB50, 0xFDFF, "Arabic Presentation Forms-A"}, +{0xFE20, 0xFE2F, "Combining Half Marks"}, +{0xFE30, 0xFE4F, "CJK Compatibility Forms"}, +{0xFE50, 0xFE6F, "Small Form Variants"}, +{0xFE70, 0xFEFE, "Arabic Presentation Forms-B"}, +{0xFF00, 0xFEFF, "Halfwidth and Fullwidth Forms"}, +{0xFFF0, 0xFFEF, "Specials"}, +{0xFFFF, 0xFFFF, 0} }; + + +UniScrollview::UniScrollview(QWidget* parent, const char* name, int f) : + QScrollView(parent, name, f) +{ + // smallFont.setRawName( "-adobe-courier-medium-r-normal--10-100-75-75-m-60-iso8859-1" ); //###### + smallFont = QFont( "Helvetica", 8 ); + QFontMetrics sfm( smallFont ); + xoff = sfm.width( "AAA" ); + setFont( FontManager::unicodeFont( FontManager::Fixed ) ); + QFontMetrics fm( font() ); + cellsize = fm.lineSpacing() + 2; + resizeContents( cellsize*nw, cellsize*65536/nw ); + verticalScrollBar()->setLineStep(cellsize); + + viewport()->setBackgroundMode( QWidget::PaletteBase ); +} + + + +void UniScrollview::contentsMousePressEvent(QMouseEvent* e) +{ + if ( e->x() < xoff || e->x() > xoff + nw*cellsize ) + return; + int row = e->y()/cellsize; + int col = (e->x()-xoff)/cellsize; + int u = row*nw+col; +#if defined(Q_WS_QWS) || defined(_WS_QWS_) + emit key( u, 0, 0, true, false ); + emit key( u, 0, 0, false, false ); +#endif +} + + +void UniScrollview::contentsMouseReleaseEvent(QMouseEvent*) +{ +} + +void UniScrollview::scrollTo( int unicode ) +{ + int row = unicode / nw; + setContentsPos( 0, row*cellsize ); +} + + +void UniScrollview::drawContents( QPainter *p, int /*cx*/, int cy, int /*cw*/, int ch ) +{ + QFontMetrics fm = fontMetrics(); + int row = cy / cellsize; + int y = row*cellsize; + while ( y < cy+ch ) { + p->drawLine( xoff, y, xoff+nw*cellsize, y ); + if ( row*nw%16 == 0 ) { + p->setFont( smallFont ); + QString s; + s.sprintf( "%03X", row*nw/16 ); + p->drawText( 0, y, xoff, cellsize, AlignLeft, s ); + p->setFont( font() ); + } + for ( int i = 0; i < nw; i++ ) { + p->drawLine( xoff+i*cellsize, y, xoff+i*cellsize, y+cellsize ); + QChar u = row*nw + i; + if ( fm.inFont( u ) ) + p->drawText( xoff+i*cellsize, y, cellsize, cellsize, AlignCenter, + u ); + } + p->drawLine( xoff+nw*cellsize, y, xoff+nw*cellsize, y+cellsize ); + row++; + y += cellsize; + } +} + + + + +UniKeyboard::UniKeyboard(QWidget* parent, const char* name, int f ) + : QFrame( parent, name, f ) +{ + setFrameStyle( NoFrame ); + sv = new UniScrollview( this ); + cb = new QComboBox( FALSE, this ); + currentBlock = 0; + QFontMetrics fm = sv->fontMetrics(); + cbmap = new int[sizeof(blockMap)/sizeof(blockMap[0])]; + for ( int i = 0; blockMap[i].name; i++ ) { + bool any=FALSE; + for ( int c=blockMap[i].start; !any && c<=blockMap[i].stop; c++ ) + any = fm.inFont(QChar(c)); + if ( any ) { + cbmap[cb->count()]=i; + cb->insertItem( blockMap[i].name ); + } + } + connect( cb, SIGNAL( activated(int)), this, SLOT( handleCombo(int)) ); + connect( sv, SIGNAL( contentsMoving(int,int)), this, SLOT( svMove(int,int)) ); + connect( sv, SIGNAL( key(ushort,ushort,ushort,bool,bool)), + this, SIGNAL( key(ushort,ushort,ushort,bool,bool)) ); +} + +UniKeyboard::~UniKeyboard() +{ + delete [] cbmap; +} + +void UniKeyboard::resizeEvent(QResizeEvent *) +{ + int d = frameWidth(); + cb->setGeometry( d, d, width()-2*d, cb->sizeHint().height() ); + sv->setGeometry( d, cb->height()+d, width()-2*d, height()-cb->height()-2*d ); +} + +void UniKeyboard::svMove( int /*x*/, int y ) +{ + int cs = sv->cellSize(); + int u = ((y+cs-1)/cs) * nw; + int i = currentBlock; + while ( i > 0 && blockMap[i].start > u ) { + i--; + } + while ( blockMap[i+1].name && blockMap[i+1].start < u ) { + i++; + } + if ( i != currentBlock ) { + currentBlock = i; + for (int ind=0; ind<cb->count(); ind++) { + if ( cbmap[ind] == i ) { + cb->setCurrentItem( ind ); + break; + } + } + } +} + +void UniKeyboard::handleCombo( int i ) +{ + currentBlock = cbmap[i]; + sv->scrollTo( blockMap[currentBlock].start ); +} + +void UniKeyboard::resetState() +{ +} + +QSize UniKeyboard::sizeHint() const +{ + return QSize( 240, 2+sv->cellSize()*4+cb->sizeHint().height() ); +} diff --git a/inputmethods/unikeyboard/unikeyboard.h b/inputmethods/unikeyboard/unikeyboard.h new file mode 100644 index 0000000..1a716c0 --- a/dev/null +++ b/inputmethods/unikeyboard/unikeyboard.h @@ -0,0 +1,77 @@ +/********************************************************************** +** 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 <qscrollview.h> + +class QComboBox; + +class UniScrollview : public QScrollView { + Q_OBJECT +public: + UniScrollview(QWidget* parent=0, const char* name=0, int f=0); + int cellSize() const { return cellsize; } + +signals: + void key( ushort unicode, ushort scancode, ushort modifiers, bool, bool ); + +public slots: + void scrollTo( int unicode ); + +protected: + void contentsMousePressEvent(QMouseEvent*); + void contentsMouseReleaseEvent(QMouseEvent*); + void drawContents( QPainter *, int cx, int cy, int cw, int ch ) ; + +private: + int cellsize; + QFont smallFont; + int xoff; +}; + + +class UniKeyboard : public QFrame +{ + Q_OBJECT +public: + UniKeyboard(QWidget* parent=0, const char* name=0, int f=0); + ~UniKeyboard(); + + void resetState(); + + QSize sizeHint() const; + +signals: + void key( ushort, ushort, ushort, bool, bool ); + +protected: + void resizeEvent(QResizeEvent *); + +private slots: + void handleCombo( int ); + void svMove( int, int ); + +private: + UniScrollview *sv; + QComboBox *cb; + int currentBlock; + int* cbmap; +}; + + + diff --git a/inputmethods/unikeyboard/unikeyboard.pro b/inputmethods/unikeyboard/unikeyboard.pro new file mode 100644 index 0000000..c0aad42 --- a/dev/null +++ b/inputmethods/unikeyboard/unikeyboard.pro @@ -0,0 +1,12 @@ +TEMPLATE = lib +CONFIG += qt warn_on release +HEADERS = unikeyboard.h unikeyboardimpl.h +SOURCES = unikeyboard.cpp unikeyboardimpl.cpp +TARGET = qunikeyboard +DESTDIR = ../../plugins/inputmethods +INCLUDEPATH += $(QPEDIR)/include +DEPENDPATH += ../$(QPEDIR)/include ../../taskbar +LIBS += -lqpe +VERSION = 1.0.0 + +TRANSLATIONS += ../../i18n/de/libqunikeyboard.ts diff --git a/inputmethods/unikeyboard/unikeyboardimpl.cpp b/inputmethods/unikeyboard/unikeyboardimpl.cpp new file mode 100644 index 0000000..fe601e0 --- a/dev/null +++ b/inputmethods/unikeyboard/unikeyboardimpl.cpp @@ -0,0 +1,105 @@ +/********************************************************************** +** 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 <qobject.h> +#include <qpixmap.h> +#include "unikeyboard.h" +#include "unikeyboardimpl.h" + +/* XPM */ +static const char * uni_xpm[]={ +"28 13 2 1", +"# c #000000", +". c None", +"............................", +"...####....#####.....####...", +"...####....######....####...", +"...####....#######..........", +"...####....########..####...", +"...####....####.####.####...", +"...####....####..########...", +"...####....####...#######...", +"...####....####....######...", +"...#####..#####.....#####...", +"....##########.......####...", +"......######..........###...", +"............................"}; + +UniKeyboardImpl::UniKeyboardImpl() + : input(0), icn(0), ref(0) +{ +} + +UniKeyboardImpl::~UniKeyboardImpl() +{ + delete input; + delete icn; +} + +QWidget *UniKeyboardImpl::inputMethod( QWidget *parent, Qt::WFlags f ) +{ + if ( !input ) + input = new UniKeyboard( parent, "UniKeyboard", f ); + return input; +} + +void UniKeyboardImpl::resetState() +{ + if ( input ) + input->resetState(); +} + +QPixmap *UniKeyboardImpl::icon() +{ + if ( !icn ) + icn = new QPixmap( (const char **)uni_xpm ); + return icn; +} + +QString UniKeyboardImpl::name() +{ + return qApp->translate( "InputMethods", "Unicode" ); +} + +void UniKeyboardImpl::onKeyPress( QObject *receiver, const char *slot ) +{ + if ( input ) + QObject::connect( input, SIGNAL(key(ushort,ushort,ushort,bool,bool)), receiver, slot ); +} + +QRESULT UniKeyboardImpl::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( UniKeyboardImpl ) +} + + diff --git a/inputmethods/unikeyboard/unikeyboardimpl.h b/inputmethods/unikeyboard/unikeyboardimpl.h new file mode 100644 index 0000000..97b1ff6 --- a/dev/null +++ b/inputmethods/unikeyboard/unikeyboardimpl.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. +** +**********************************************************************/ +#ifndef UNIKEYBOARDIMPL_H +#define UNIKEYBOARDIMPL_H + +#include <qpe/inputmethodinterface.h> + +class UniKeyboard; +class QPixmap; + +class UniKeyboardImpl : public InputMethodInterface +{ +public: + UniKeyboardImpl(); + virtual ~UniKeyboardImpl(); + + QRESULT queryInterface( const QUuid&, QUnknownInterface** ); + Q_REFCOUNT + + 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: + UniKeyboard *input; + QPixmap *icn; + ulong ref; +}; + +#endif |