Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

inputmethods.cpp

Go to the documentation of this file.
00001 /**********************************************************************
00002 ** Copyright (C) 2000-2002 Trolltech AS.  All rights reserved.
00003 **
00004 ** This file is part of the Qtopia Environment.
00005 **
00006 ** This file may be distributed and/or modified under the terms of the
00007 ** GNU General Public License version 2 as published by the Free Software
00008 ** Foundation and appearing in the file LICENSE.GPL included in the
00009 ** packaging of this file.
00010 **
00011 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00012 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00013 **
00014 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
00015 **
00016 ** Contact info@trolltech.com if any conditions of this licensing are
00017 ** not clear to you.
00018 **
00019 **********************************************************************/
00020 
00021 #define QTOPIA_INTERNAL_LANGLIST
00022 #include "inputmethods.h"
00023 
00024 /* OPIE */
00025 #include <opie2/odebug.h>
00026 #include <qpe/config.h>
00027 #include <qpe/global.h>
00028 #include <qpe/qpeapplication.h>
00029 using namespace Opie::Core;
00030 
00031 /* QT */
00032 #include <qpopupmenu.h>
00033 #include <qtoolbutton.h>
00034 #include <qwidgetstack.h>
00035 #include <qlayout.h>
00036 #include <qdir.h>
00037 #include <qtl.h>
00038 #ifdef Q_WS_QWS
00039 #include <qwindowsystem_qws.h>
00040 #include <qwsevent_qws.h>
00041 #include <qcopchannel_qws.h>
00042 #endif
00043 
00044 /* STD */
00045 #include <stdlib.h>
00046 
00047 /* XPM */
00048 static const char * tri_xpm[]={
00049 "9 9 2 1",
00050 "a c #000000",
00051 ". c None",
00052 ".........",
00053 ".........",
00054 ".........",
00055 "....a....",
00056 "...aaa...",
00057 "..aaaaa..",
00058 ".aaaaaaa.",
00059 ".........",
00060 "........."};
00061 
00062 int InputMethod::operator <(const InputMethod& o) const
00063 {
00064     return name() < o.name();
00065 }
00066 int InputMethod::operator >(const InputMethod& o) const
00067 {
00068     return name() > o.name();
00069 }
00070 int InputMethod::operator <=(const InputMethod& o) const
00071 {
00072     return name() <= o.name();
00073 }
00074 
00075 
00076 /*
00077   Slightly hacky: We use WStyle_Tool as a flag to say "this widget
00078   belongs to the IM system, so clicking it should not cause a reset".
00079  */
00080 class IMToolButton : public QToolButton
00081 {
00082 public:
00083     IMToolButton::IMToolButton( QWidget *parent ) : QToolButton( parent )
00084     { setWFlags( WStyle_Tool ); 
00085       setBackgroundOrigin( ParentOrigin );
00086       setBackgroundMode( PaletteBackground );
00087     }
00088 };
00089 
00090 
00091 InputMethods::InputMethods( QWidget *parent ) :
00092     QWidget( parent, "InputMethods", WStyle_Tool | WStyle_Customize ),
00093     mkeyboard(0), imethod(0)
00094 {
00095     readConfig();
00096 
00097     setBackgroundOrigin( ParentOrigin );
00098     setBackgroundMode( PaletteBackground );
00099     QHBoxLayout *hbox = new QHBoxLayout( this );
00100 
00101     kbdButton = new IMToolButton( this);
00102     kbdButton->setFocusPolicy(NoFocus);
00103     kbdButton->setToggleButton( TRUE );
00104     if (parent->sizeHint().height() > 0)
00105         kbdButton->setFixedHeight( parent->sizeHint().height() );
00106     kbdButton->setFixedWidth( 32 );
00107     kbdButton->setAutoRaise( TRUE );
00108     kbdButton->setUsesBigPixmap( TRUE );
00109     hbox->addWidget( kbdButton );
00110     connect( kbdButton, SIGNAL(toggled(bool)), this, SLOT(showKbd(bool)) );
00111 
00112     kbdChoice = new IMToolButton( this );
00113     kbdChoice->setFocusPolicy(NoFocus);
00114     kbdChoice->setPixmap( QPixmap( (const char **)tri_xpm ) );
00115     if (parent->sizeHint().height() > 0)
00116         kbdChoice->setFixedHeight( parent->sizeHint().height() );
00117     kbdChoice->setFixedWidth( 13 );
00118     kbdChoice->setAutoRaise( TRUE );
00119     hbox->addWidget( kbdChoice );
00120     connect( kbdChoice, SIGNAL(clicked()), this, SLOT(chooseKbd()) );
00121 
00122     connect( (QPEApplication*)qApp, SIGNAL(clientMoused()),
00123             this, SLOT(resetStates()) );
00124 
00125 
00126     imButton = new QWidgetStack( this ); // later a widget stack
00127     imButton->setFocusPolicy(NoFocus);
00128     if (parent->sizeHint().height() > 0)
00129         imButton->setFixedHeight( parent->sizeHint().height() );
00130     hbox->addWidget(imButton);
00131 
00132     imChoice = new QToolButton( this );
00133     imChoice->setFocusPolicy(NoFocus);
00134     imChoice->setPixmap( QPixmap( (const char **)tri_xpm ) );
00135     if (parent->sizeHint().height() > 0)
00136         imChoice->setFixedHeight( parent->sizeHint().height() );
00137     imChoice->setFixedWidth( 13 );
00138     imChoice->setAutoRaise( TRUE );
00139     hbox->addWidget( imChoice );
00140     connect( imChoice, SIGNAL(clicked()), this, SLOT(chooseIm()) );
00141 
00142     loadInputMethods();
00143 
00144     QCopChannel *channel = new QCopChannel( "QPE/IME", this );
00145     connect( channel, SIGNAL(received(const QCString&,const QByteArray&)),
00146              this, SLOT(qcopReceive(const QCString&,const QByteArray&)) );
00147 }
00148 
00149 InputMethods::~InputMethods()
00150 {
00151     Config cfg("qpe");
00152     cfg.setGroup("InputMethod");
00153     if (imethod)
00154         cfg.writeEntry("im", imethod->name() );
00155     if (mkeyboard)
00156         cfg.writeEntry("current", mkeyboard->name() );
00157 
00158     unloadInputMethods();
00159 }
00160 
00161 void InputMethods::hideInputMethod()
00162 {
00163     kbdButton->setOn( FALSE );
00164 }
00165 
00166 void InputMethods::showInputMethod()
00167 {
00168     kbdButton->setOn( TRUE );
00169 }
00170 
00171 void InputMethods::showInputMethod(const QString& name)
00172 {
00173     int i = 0;
00174     QValueList<InputMethod>::Iterator it;
00175     InputMethod *im = 0;
00176     for ( it = inputMethodList.begin(); it != inputMethodList.end(); ++it, i++ ) {
00177         QString lname = (*it).libName.mid((*it).libName.findRev('/') + 1);
00178         if ( (*it).name() == name || lname == name ) {
00179             im = &(*it);
00180             break;
00181         }
00182     }
00183     if ( im )
00184         chooseKeyboard(im);
00185 }
00186 
00187 void InputMethods::resetStates()
00188 {
00189     if ( mkeyboard && !mkeyboard->newIM )
00190         mkeyboard->interface->resetState();
00191 }
00192 
00193 QRect InputMethods::inputRect() const
00194 {
00195     if ( !mkeyboard || !mkeyboard->widget || !mkeyboard->widget->isVisible() )
00196         return QRect();
00197     else
00198         return mkeyboard->widget->geometry();
00199 }
00200 
00201 void InputMethods::unloadInputMethods()
00202 {
00203     unloadMethod( inputMethodList );
00204     unloadMethod( inputModifierList );
00205     inputMethodList.clear();
00206     inputModifierList.clear();
00207 
00208 }
00209 
00210 void InputMethods::unloadMethod( QValueList<InputMethod>& list ) {
00211     QValueList<InputMethod>::Iterator it;
00212 
00213     for (it = list.begin(); it != list.end(); ++it )
00214         (*it).releaseInterface();
00215 
00216 }
00217 
00218 
00219 QStringList InputMethods::plugins()const {
00220    QString path = QPEApplication::qpeDir() + "plugins/inputmethods";
00221 #ifdef Q_OS_MACX
00222    QDir dir( path, "lib*.dylib" );
00223 #else
00224    QDir dir( path, "lib*.so" );
00225 #endif /* Q_OS_MACX */
00226    return dir.entryList();
00227 }
00228 
00229 void InputMethods::installTranslator( const QString& type ) {
00230     QStringList langs = Global::languageList();
00231     QStringList::ConstIterator lit;
00232     for ( lit= langs.begin(); lit!=langs.end(); ++lit) {
00233         QString lang = *lit;
00234         QTranslator * trans = new QTranslator(qApp);
00235 
00236         QString tfn = QPEApplication::qpeDir()+"i18n/"+lang+"/"+type+".qm";
00237 
00238         if ( trans->load( tfn ))
00239             qApp->installTranslator( trans );
00240         else
00241             delete trans;
00242     }
00243 }
00244 
00245 void InputMethods::setPreferedHandlers() {
00246     Config cfg("qpe");
00247     cfg.setGroup("InputMethod");
00248     QString current = cfg.readEntry("current");
00249     QString im = cfg.readEntry("im");
00250 
00251     QValueList<InputMethod>::Iterator it;
00252     if (!inputModifierList.isEmpty() && !im.isEmpty() ) {
00253         for (it = inputModifierList.begin(); it != inputModifierList.end(); ++it )
00254             if ( (*it).name() == im ) {
00255                 imethod = &(*it); break;
00256             }
00257 
00258     }
00259     if (!inputMethodList.isEmpty() && !current.isEmpty() ) {
00260         for (it = inputMethodList.begin(); it != inputMethodList.end(); ++it )
00261             if ( (*it).name() == current ) {
00262                 owarn << "preferred keyboard is " << current << "" << oendl;
00263                 mkeyboard = &(*it);
00264                 kbdButton->setPixmap( *mkeyboard->icon() );
00265                 break;
00266             }
00267     }
00268 
00269 }
00270 
00271 void InputMethods::loadInputMethods()
00272 {
00273 #ifndef QT_NO_COMPONENT
00274     hideInputMethod();
00275     mkeyboard = 0;
00276 
00277     unloadInputMethods();
00278 
00279     QString path = QPEApplication::qpeDir() + "plugins/inputmethods";
00280     QStringList list = plugins();
00281     QStringList::Iterator it;
00282     for ( it = list.begin(); it != list.end(); ++it ) {
00283         InputMethodInterface *iface = 0;
00284         ExtInputMethodInterface *eface = 0;
00285         QLibrary *lib = new QLibrary( path + "/" + *it );
00286 
00287         if ( lib->queryInterface( IID_InputMethod, (QUnknownInterface**)&iface ) == QS_OK ) {
00288             InputMethod input;
00289             input.newIM = FALSE;
00290             input.library = lib;
00291             input.libName = *it;
00292             input.interface = iface;
00293             input.widget = input.interface->inputMethod( 0, inputWidgetStyle );
00294             input.interface->onKeyPress( this, SLOT(sendKey(ushort,ushort,ushort,bool,bool)) );
00295             inputMethodList.append( input );
00296         } else if ( lib->queryInterface( IID_ExtInputMethod, (QUnknownInterface**)&eface ) == QS_OK ) {
00297             InputMethod input;
00298             input.newIM = TRUE;
00299             input.library = lib;
00300             input.libName = *it;
00301             input.extInterface = eface;
00302             input.widget = input.extInterface->keyboardWidget( 0, inputWidgetStyle );
00303             // may be either a simple, or advanced.
00304             if (input.widget) {
00305         //odebug << "its a keyboard" << oendl;
00306                 inputMethodList.append( input );
00307             } else {
00308         //odebug << "its a real im" << oendl;
00309                 input.widget = input.extInterface->statusWidget( 0, 0 );
00310                 if (input.widget) {
00311             //odebug << "blah" << oendl;
00312                     inputModifierList.append( input );
00313                     imButton->addWidget(input.widget, inputModifierList.count());
00314                 }
00315             }
00316         }else{
00317             delete lib;
00318             lib = 0l;
00319         }
00320         installTranslator(  (*it).left( (*it).find(".") ) );
00321     }
00322     qHeapSort( inputMethodList );
00323 #endif /* killed BUILT in cause they would not compile */
00324 
00325     QWSServer::setCurrentInputMethod( 0 );
00326 
00327     /* set the prefered IM + handler */
00328     setPreferedHandlers();
00329     if ( !inputModifierList.isEmpty() ) {
00330         if (!imethod)
00331             imethod = &inputModifierList[0];
00332         imButton->raiseWidget(imethod->widget);
00333         QWSServer::setCurrentInputMethod( imethod->extInterface->inputMethod() );
00334     } else {
00335         imethod = 0;
00336     }
00337 
00338     // we need to update keyboards afterwards, as some of them may not be compatible with
00339     // the current input method
00340     updateKeyboards(imethod);
00341 
00342     if ( !inputModifierList.isEmpty() )
00343         imButton->show();
00344     else
00345         imButton->hide();
00346 
00347     if ( inputModifierList.count() > 1 )
00348         imChoice->show();
00349     else
00350         imChoice->hide();
00351 }
00352 
00353 void InputMethods::chooseKbd()
00354 {
00355     QPopupMenu pop( this );
00356     pop.setFocusPolicy( NoFocus ); //don't reset IM
00357 
00358     QString imname;
00359     if (imethod)
00360         imname = imethod->libName.mid(imethod->libName.findRev('/') + 1);
00361 
00362     int i = 0;
00363     int firstDepKbd = 0;
00364 
00365     QValueList<InputMethod>::Iterator it;
00366     for ( it = inputMethodList.begin(); it != inputMethodList.end(); ++it, i++ ) {
00367         // add empty new items, all old items.
00368         if (!(*it).newIM || (*it).extInterface->compatible().count() == 0 ) {
00369             pop.insertItem( (*it).name(), i, firstDepKbd);
00370             if ( mkeyboard == &(*it) )
00371                 pop.setItemChecked( i, TRUE );
00372 
00373             firstDepKbd++;
00374         } else if ( (*it).extInterface->compatible().contains(imname)) {
00375             // check if we need to insert a sep.
00376             if (firstDepKbd == i)
00377                 pop.insertSeparator();
00378             pop.insertItem( (*it).name(), i, -1);
00379             if ( mkeyboard == &(*it) )
00380                 pop.setItemChecked( i, TRUE );
00381         }
00382     }
00383 
00384     QPoint pt = mapToGlobal(kbdChoice->geometry().topRight());
00385     QSize s = pop.sizeHint();
00386     pt.ry() -= s.height();
00387     pt.rx() -= s.width();
00388     i = pop.exec( pt );
00389     if ( i == -1 )
00390         return;
00391     InputMethod *im = &inputMethodList[i];
00392     chooseKeyboard(im);
00393 }
00394 
00395 void InputMethods::chooseIm()
00396 {
00397     QPopupMenu pop( this );
00398 
00399     int i = 0;
00400     QValueList<InputMethod>::Iterator it;
00401     for ( it = inputModifierList.begin(); it != inputModifierList.end(); ++it, i++ ) {
00402         pop.insertItem( (*it).name(), i );
00403         if ( imethod == &(*it) )
00404             pop.setItemChecked( i, TRUE );
00405     }
00406 
00407     QPoint pt = mapToGlobal(imChoice->geometry().topRight());
00408     QSize s = pop.sizeHint();
00409     pt.ry() -= s.height();
00410     pt.rx() -= s.width();
00411     i = pop.exec( pt );
00412     if ( i == -1 )
00413         return;
00414     InputMethod *im = &inputModifierList[i];
00415 
00416     chooseMethod(im);
00417 }
00418 
00419 void InputMethods::chooseKeyboard(InputMethod* im)
00420 {
00421     if ( im != mkeyboard ) {
00422         if ( mkeyboard && mkeyboard->widget->isVisible() )
00423             mkeyboard->widget->hide();
00424         mkeyboard = im;
00425         kbdButton->setPixmap( *mkeyboard->icon() );
00426     }
00427     if ( !kbdButton->isOn() )
00428         kbdButton->setOn( TRUE );
00429     else
00430         showKbd( TRUE );
00431 }
00432 
00433 static bool keyboardCompatible(InputMethod *keyb, const QString &imname )
00434 {
00435     if ( !keyb || !keyb->newIM || !keyb->extInterface->compatible().count() )
00436         return TRUE;
00437 
00438     if ( keyb->extInterface->compatible().contains(imname) )
00439         return TRUE;
00440 
00441     return FALSE;
00442 }
00443 
00444 // Updates the display of the soft keyboards available to the current input method
00445 void InputMethods::updateKeyboards(InputMethod *im )
00446 {
00447     uint count;
00448 
00449     if ( im ) {
00450         QString imname = im->libName.mid(im->libName.findRev('/') + 1);
00451 
00452         if ( mkeyboard && !keyboardCompatible(mkeyboard, imname) ) {
00453             kbdButton->setOn( FALSE );
00454             showKbd( FALSE );
00455             mkeyboard = 0;
00456         }
00457 
00458         count = 0;
00459 
00460         QValueList<InputMethod>::Iterator it;
00461         for ( it = inputMethodList.begin(); it != inputMethodList.end(); ++it ) {
00462             if ( keyboardCompatible( &(*it), imname ) ) {
00463                 if ( !mkeyboard ) {
00464                     mkeyboard = &(*it);
00465                     kbdButton->setPixmap( *mkeyboard->icon() );
00466                 }
00467 
00468                 count++;
00469             }
00470         }
00471     } else {
00472         count = inputMethodList.count();
00473         if ( count && !mkeyboard ) {
00474             mkeyboard = &inputMethodList[0];
00475             kbdButton->setPixmap( *mkeyboard->icon() );
00476         } else if (!count){
00477             mkeyboard = 0;  //might be redundant
00478         }
00479     }
00480 
00481     if ( count > 1 )
00482         kbdChoice->show();
00483     else
00484         kbdChoice->hide();
00485 
00486     if ( count )
00487         kbdButton->show();
00488     else
00489         kbdButton->hide();
00490 }
00491 
00492 void InputMethods::chooseMethod(InputMethod* im)
00493 {
00494     if ( im != imethod ) {
00495         updateKeyboards( im );
00496 
00497         Config cfg("qpe");
00498         cfg.setGroup("InputMethod");
00499         if (im )
00500             cfg.writeEntry("im", im->name() );
00501         if (mkeyboard)
00502             cfg.writeEntry("current", mkeyboard->name() );
00503 
00504         QWSServer::setCurrentInputMethod( 0 );
00505         imethod = im;
00506         if ( imethod && imethod->newIM )
00507             QWSServer::setCurrentInputMethod( imethod->extInterface->inputMethod() );
00508         else
00509             QWSServer::setCurrentInputMethod( 0 );
00510 
00511         if ( im )
00512             imButton->raiseWidget(im->widget);
00513         else
00514             imButton->hide(); //### good UI? make sure it is shown again!
00515     }
00516 }
00517 
00518 void InputMethods::qcopReceive( const QCString &msg, const QByteArray &data )
00519 {
00520     if ( imethod && imethod->newIM )
00521         imethod->extInterface->qcopReceive( msg, data );
00522 }
00523 
00524 
00525 void InputMethods::showKbd( bool on )
00526 {
00527     if ( !mkeyboard )
00528         return;
00529 
00530     if ( on )
00531     {
00532         mkeyboard->resetState();
00533 
00534         int height = QMIN( mkeyboard->widget->sizeHint().height(), 134 );
00535         int width = static_cast<int>( qApp->desktop()->width() * (inputWidgetWidth*0.01) );
00536         int left = 0;
00537         int top = mapToGlobal( QPoint() ).y() - height;
00538 
00539         if ( inputWidgetStyle & QWidget::WStyle_DialogBorder )
00540         {
00541             odebug << "InputMethods: reading geometry." << oendl;
00542             Config cfg( "Launcher" );
00543             cfg.setGroup( "InputMethods" );
00544             int l = cfg.readNumEntry( "absX", -1 );
00545             int t = cfg.readNumEntry( "absY", -1 );
00546             int w = cfg.readNumEntry( "absWidth", -1 );
00547             int h = cfg.readNumEntry( "absHeight", -1 );
00548 
00549             if ( l > -1 && t > -1 && w > -1 && h > -1 )
00550             {
00551                 odebug << "InputMethods: config values ( " << l << ", " << t << ", " << w << ", " << h << " ) are ok." << oendl;
00552                 left = l;
00553                 top = t;
00554                 width = w;
00555                 height = h;
00556             }
00557             else
00558             {
00559                 odebug << "InputMethods: config values are new or not ok." << oendl;
00560             }
00561         }
00562         else
00563         {
00564             odebug << "InputMethods: no floating selected." << oendl;
00565         }
00566         mkeyboard->widget->resize( width, height );
00567         mkeyboard->widget->move( left, top );
00568         mkeyboard->widget->show();
00569         mkeyboard->widget->installEventFilter( this );
00570     }
00571     else
00572     {
00573         if ( inputWidgetStyle & QWidget::WStyle_DialogBorder )
00574         {
00575             QPoint pos = mkeyboard->widget->pos();
00576             QSize siz = mkeyboard->widget->size();
00577             odebug << "InputMethods: saving geometry." << oendl;
00578             Config cfg( "Launcher" );
00579             cfg.setGroup( "InputMethods" );
00580             cfg.writeEntry( "absX", pos.x() );
00581             cfg.writeEntry( "absY", pos.y() );
00582             cfg.writeEntry( "absWidth", siz.width() );
00583             cfg.writeEntry( "absHeight", siz.height() );
00584             cfg.write();
00585             mkeyboard->widget->removeEventFilter( this );
00586         }
00587         mkeyboard->widget->hide();
00588     }
00589 
00590     emit inputToggled( on );
00591 }
00592 
00593 bool InputMethods::shown() const
00594 {
00595     return mkeyboard && mkeyboard->widget->isVisible();
00596 }
00597 
00598 QString InputMethods::currentShown() const
00599 {
00600     return mkeyboard && mkeyboard->widget->isVisible()
00601         ? mkeyboard->name() : QString::null;
00602 }
00603 
00604 void InputMethods::sendKey( ushort unicode, ushort scancode, ushort mod, bool press, bool repeat )
00605 {
00606 #if defined(Q_WS_QWS)
00607     QWSServer::sendKeyEvent( unicode, scancode, mod, press, repeat );
00608 #endif
00609 }
00610 
00611 bool InputMethods::eventFilter( QObject* , QEvent* e )
00612 {
00613     if ( e->type() == QEvent::Close )
00614     {
00615         ( (QCloseEvent*) e )->ignore();
00616         showKbd( false );
00617         kbdButton->setOn( false );
00618         return true;
00619     }
00620     return false;
00621 }
00622 
00623 void InputMethods::readConfig() {
00624     Config cfg( "Launcher" );
00625     cfg.setGroup( "InputMethods" );
00626 
00627     inputWidgetStyle = QWidget::WStyle_Customize | QWidget::WStyle_StaysOnTop | QWidget::WGroupLeader | QWidget::WStyle_Tool;
00628     inputWidgetStyle |= cfg.readBoolEntry( "Float", false ) ?
00629                         QWidget::WStyle_DialogBorder : 0;
00630     inputWidgetWidth = cfg.readNumEntry( "Width", 100 );
00631 }

Generated on Sat Nov 5 16:15:30 2005 for OPIE by  doxygen 1.4.2