/****************************************************************************
** $Id$
**
** Implementation of QMultiInputContext class
**
** Copyright (C) 2004 immodule for Qt Project.  All rights reserved.
**
** This file is written to contribute to Trolltech AS under their own
** licence. You may use this file under your Qt license. Following
** description is copied from their original file headers. Contact
** immodule-qt@freedesktop.org if any conditions of this licensing are
** not clear to you.
**
**
** This file is part of the input method module of the Qt GUI Toolkit.
**
** This file may be distributed under the terms of the Q Public License
** as defined by Trolltech AS of Norway and appearing in the file
** LICENSE.QPL included in the packaging of this file.
**
** 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.
**
** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
** licenses may use this file in accordance with the Qt Commercial License
** Agreement provided with the Software.
**
** 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/pricing.html or email sales@trolltech.com for
**   information about Qt Commercial License Agreements.
** See http://www.trolltech.com/qpl/ for QPL licensing information.
** 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 QT_NO_IM
#include "qmultiinputcontext.h"
#include <qinputcontextfactory.h>
#include <qstringlist.h>
#include <qpopupmenu.h>
#ifndef QT_NO_IM_EXTENSIONS
#include <qsettings.h>
#endif

#include <cstdlib>

#define QT_NO_IM_QMULTIINPUTCONTEXT_IMINDEX

QMultiInputContext::QMultiInputContext()
    : QInputContext(), _slave( 0 ), imIndex( 0 ), cachedFocus( FALSE ),
      cachedFocusWidget( 0 ), cachedHolderWidget( 0 ),
      beIndirectlyConnected( FALSE ), popup( NULL ), currentIMKey( QString::null )
{
    keyDict.setAutoDelete( true );
    keyDict.clear();

    if ( getenv( "QT_IM_MODULE" ) ) {
        currentIMKey = getenv( "QT_IM_MODULE" );
    } else {
#ifndef QT_NO_IM_EXTENSIONS
	QSettings settings;
        currentIMKey = settings.readEntry( "/qt/DefaultInputMethod", "xim" );
#else
	currentIMKey = "xim";
#endif
    }
}

QMultiInputContext::~QMultiInputContext()
{
    keyDict.clear();
}


QString QMultiInputContext::identifierName()
{
    return ( slave() ) ? slave()->identifierName() : "";
}

QString QMultiInputContext::language()
{
    return ( slave() ) ? slave()->language() : "";
}


#if defined(Q_WS_X11)
bool QMultiInputContext::x11FilterEvent( QWidget *keywidget, XEvent *event )
{
    return ( slave() ) ? slave()->x11FilterEvent( keywidget, event ) : FALSE;
}
#endif // Q_WS_X11


bool QMultiInputContext::filterEvent( const QEvent *event )
{
#if !defined(QT_NO_IM_QMULTIINPUTCONTEXT_IMINDEX)
    if ( event->type() == QEvent::KeyPress ) {
	QKeyEvent *keyevent = (QKeyEvent *)event;

	// filter selection key
	// Control+Alt+Key_Down: change to next input method
	// Control+Alt+Key_Up:   change to previous input method
	if ( ( keyevent->state() & Qt::ControlButton ) &&
	     ( keyevent->state() & Qt::AltButton ) ) {
	    if ( keyevent->key() == Qt::Key_Up ) {
		changeInputMethod( --imIndex );
		return TRUE;
	    } else if ( keyevent->key() == Qt::Key_Down ) {
		changeInputMethod( ++imIndex );
		return TRUE;
	    }
	}
    }
#endif

    return ( slave() ) ? slave()->filterEvent( event ) : FALSE;
}

void QMultiInputContext::reset()
{
    if ( slave() )
	slave()->reset();
}


void QMultiInputContext::setFocus()
{
    cachedFocus = TRUE;
    if ( slave() )
	slave()->setFocus();
}

void QMultiInputContext::unsetFocus()
{
    cachedFocus = FALSE;
    if ( slave() )
	slave()->unsetFocus();
}

void QMultiInputContext::setMicroFocus( int x, int y, int w, int h, QFont *f )
{
    if ( slave() )
	slave()->setMicroFocus( x, y, w, h, f );
}

void QMultiInputContext::mouseHandler( int x, QEvent::Type type,
				       Qt::ButtonState button,
				       Qt::ButtonState state )
{
    if ( slave() )
	slave()->mouseHandler( x, type, button, state );
}

QFont QMultiInputContext::font() const
{
    return ( slave() ) ? slave()->font() : QInputContext::font();
}

void QMultiInputContext::destroyInputContext()
{
    if ( _slave ) {
	// _slave->reset() may not properly work in the case, so we
	// manually resets the composing state of text widget
	if ( _slave->focusWidget() ) {
	    QIMEvent *terminator = new QIMEvent( QEvent::IMEnd, QString::null, -1 );
	    emit imEventGenerated( _slave->focusWidget(), terminator );
	}
	_slave->deleteLater();
	_slave = 0;
    }
}


/*!
    This function is a placeholder for future experiment or extension
    such as commit string snooping. set beIndirectlyConnected = TRUE
    to activate this virtual function.
*/
void QMultiInputContext::postIMEvent( QObject *receiver, QIMEvent *event )
{
    emit imEventGenerated( receiver, event );
}


#if defined(Q_WS_X11)
QWidget *QMultiInputContext::focusWidget() const
{
    return ( slave() ) ? slave()->focusWidget() : 0;
}

QWidget *QMultiInputContext::holderWidget() const
{
    return ( slave() ) ? slave()->holderWidget() : 0;
}


void QMultiInputContext::setFocusWidget( QWidget *w )
{
    cachedFocusWidget = w;
    if ( slave() )
	slave()->setFocusWidget( w );
}

void QMultiInputContext::setHolderWidget( QWidget *w )
{
    cachedHolderWidget = w;
    if ( slave() )
	slave()->setHolderWidget( w );
}

void QMultiInputContext::releaseComposingWidget( QWidget *w )
{
    if ( slave() )
	slave()->releaseComposingWidget( w );
}

#endif

bool QMultiInputContext::isComposing() const
{
    return ( slave() ) ? slave()->isComposing() : FALSE;
}

bool QMultiInputContext::isPreeditRelocationEnabled()
{
    return ( slave() ) ? slave()->isPreeditRelocationEnabled() : FALSE;
}

QInputContext *QMultiInputContext::slave()
{
    if ( ! _slave ) {
#if !defined(QT_NO_IM_QMULTIINPUTCONTEXT_IMINDEX)
	changeInputMethod( imIndex );
#else
	changeInputMethod( currentIMKey );
#endif
    }

    return _slave;
}

const QInputContext *QMultiInputContext::slave() const
{
    return _slave;
}

void QMultiInputContext::changeInputMethod( int newIndex )
{
#if !defined(QT_NO_IM_QMULTIINPUTCONTEXT_IMINDEX)
    QStringList keys = QInputContextFactory::keys();
    if ( keys.size() == 0 )
	return;

    if ( newIndex >= (int)keys.size() ) {
	imIndex = 0;
    } else if ( newIndex < 0 ) {
	imIndex = keys.size() - 1;
    } else {
	imIndex = newIndex;
    }

    changeInputMethod( keys[imIndex] );
#endif
}

void QMultiInputContext::changeInputMethod( QString key )
{
    QStringList keys = QInputContextFactory::keys();
    if ( keys.size() == 0 )
	return;

    if ( key.isEmpty() )
	key = keys[0];

    if ( _slave ) {
	_slave->reset();
	delete _slave;
    }

    _slave = QInputContextFactory::create( key, cachedHolderWidget );
    if ( _slave ) {
	insertChild( _slave );

	const char *method;
	if ( beIndirectlyConnected ) {
	    method = SLOT(imEventReceived(QObject *,QIMEvent *));
	} else {
	    method = SIGNAL(imEventGenerated(QObject *,QIMEvent *));
	}
	connect( _slave, SIGNAL(imEventGenerated(QObject *,QIMEvent *)),
		 this, method );
	connect( _slave, SIGNAL(deletionRequested()),
		 this, SLOT(destroyInputContext()) );

	if ( cachedFocus ) {
	    _slave->setFocus();
	    _slave->setFocusWidget( cachedFocusWidget );
	}

        currentIMKey = key;

	//qDebug( "QMultiInputContext::changeInputMethod(): index=%d, slave=%s",
	//	imIndex, (const char *)_slave->identifierName() );
    }
}

QPtrList<QInputContextMenu> *QMultiInputContext::menus()
{
    QInputContextMenu *imSelMenu = new QInputContextMenu;
    imSelMenu->title = tr( "Select Input &Method" );
    imSelMenu->popup = createImSelPopup();

    QPtrList<QInputContextMenu> *result = new QPtrList<QInputContextMenu>;
    result->append( imSelMenu );

    QPtrList<QInputContextMenu> *slaveMenus = ( slave() ) ? slave()->menus() : 0;
    if ( slaveMenus ) {
	for ( QPtrList<QInputContextMenu>::Iterator it = slaveMenus->begin();
	      it != slaveMenus->end();
	      ++it ) {
	    QInputContextMenu *slaveMenu = *it;
	    result->append( slaveMenu );
	}
	delete slaveMenus;
    }

    return result;
}

QPopupMenu *QMultiInputContext::createImSelPopup()
{
    if ( popup )
        delete popup;

    popup = new QPopupMenu();
    keyDict.clear();

    QStringList keys = QInputContextFactory::keys();
    for ( uint i=0; i < keys.size(); i++ ) {
	QString idName = keys[i];
	bool isIMSwitcher = idName.startsWith( "imsw-" );

	if ( ! isIMSwitcher ) {
	    QString dispName = QInputContextFactory::displayName( idName );
	    if ( dispName.isEmpty() )
		dispName = idName;

	    int id = popup->insertItem( dispName );
	    keyDict.insert( (long)id, new QString( idName ) );

	    if ( idName == currentIMKey )
		popup->setItemChecked( id, true );

            QString descriptionStr = QInputContextFactory::description( idName );
            if ( ! descriptionStr.isEmpty() )
                popup->setWhatsThis( id, descriptionStr );
	}
    }

    QObject::connect( popup, SIGNAL(activated(int)),
                      this, SLOT(changeInputMethodWithMenuId(int)) );

    return popup;
}

void QMultiInputContext::changeInputMethodWithMenuId( int menuid )
{
    QString *key = keyDict.find( (long)menuid );
    changeInputMethod( (*key) );
}

#endif