src/gui/kernel/qapplication_s60.cpp
author Alex Gilkes <alex.gilkes@nokia.com>
Mon, 11 Jan 2010 14:00:40 +0000
changeset 0 1918ee327afb
child 3 41300fa6a67c
permissions -rw-r--r--
Revision: 200952

/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qapplication_p.h"
#include "qsessionmanager.h"
#include "qevent.h"
#include "qsymbianevent.h"
#include "qeventdispatcher_s60_p.h"
#include "qwidget.h"
#include "qdesktopwidget.h"
#include "private/qbackingstore_p.h"
#include "qt_s60_p.h"
#include "private/qevent_p.h"
#include "qstring.h"
#include "qdebug.h"
#include "qimage.h"
#include "private/qkeymapper_p.h"
#include "private/qfont_p.h"
#ifndef QT_NO_STYLE_S60
#include "private/qs60style_p.h"
#endif
#include "private/qwindowsurface_s60_p.h"
#include "qpaintengine.h"
#include "private/qmenubar_p.h"
#include "private/qsoftkeymanager_p.h"

#include "apgwgnam.h" // For CApaWindowGroupName
#include <MdaAudioTonePlayer.h>     // For CMdaAudioToneUtility

#if defined(Q_WS_S60)
# if !defined(QT_NO_IM)
#  include "qinputcontext.h"
#  include <private/qcoefepinputcontext_p.h>
# endif
# include <private/qs60mainapplication_p.h>
#endif

#include "private/qstylesheetstyle_p.h"

#include <hal.h>
#include <hal_data.h>

QT_BEGIN_NAMESPACE

#if defined(QT_DEBUG)
static bool        appNoGrab        = false;        // Grabbing enabled
#endif
static bool        app_do_modal        = false;        // modal mode
Q_GLOBAL_STATIC(QS60Data, qt_s60Data);

extern bool qt_sendSpontaneousEvent(QObject*,QEvent*);
extern QWidgetList *qt_modal_stack;              // stack of modal widgets
extern QDesktopWidget *qt_desktopWidget; // qapplication.cpp

QWidget *qt_button_down = 0;                     // widget got last button-down

QSymbianControl *QSymbianControl::lastFocusedControl = 0;

QS60Data* qGlobalS60Data()
{
    return qt_s60Data();
}

bool qt_nograb()                                // application no-grab option
{
#if defined(QT_DEBUG)
    return appNoGrab;
#else
    return false;
#endif
}

// Modified from http://www3.symbian.com/faq.nsf/0/0F1464EE96E737E780256D5E00503DD1?OpenDocument
class QS60Beep : public CBase, public MMdaAudioToneObserver
{
public:
    static QS60Beep* NewL(TInt aFrequency,  TTimeIntervalMicroSeconds iDuration);
    void Play();
    ~QS60Beep();
private:
    void ConstructL(TInt aFrequency,  TTimeIntervalMicroSeconds iDuration);
    void MatoPrepareComplete(TInt aError);
    void MatoPlayComplete(TInt aError);
private:
    typedef enum
        {
        EBeepNotPrepared,
        EBeepPrepared,
        EBeepPlaying
        } TBeepState;
private:
    CMdaAudioToneUtility* iToneUtil;
    TBeepState iState;
    TInt iFrequency;
    TTimeIntervalMicroSeconds iDuration;
};

QS60Beep::~QS60Beep()
{
    delete iToneUtil;
}

QS60Beep* QS60Beep::NewL(TInt aFrequency, TTimeIntervalMicroSeconds aDuration)
{
    QS60Beep* self=new (ELeave) QS60Beep();
    CleanupStack::PushL(self);
    self->ConstructL(aFrequency, aDuration);
    CleanupStack::Pop();
    return self;
};

void QS60Beep::ConstructL(TInt aFrequency, TTimeIntervalMicroSeconds aDuration)
{
    iToneUtil=CMdaAudioToneUtility::NewL(*this);
    iState=EBeepNotPrepared;
    iFrequency=aFrequency;
    iDuration=aDuration;
    iToneUtil->PrepareToPlayTone(iFrequency,iDuration);
}

void QS60Beep::Play()
{
    if (iState != EBeepNotPrepared) {
        if (iState == EBeepPlaying) {
            iToneUtil->CancelPlay();
            iState = EBeepPrepared;
        }
    }

    iToneUtil->Play();
    iState = EBeepPlaying;
}

void QS60Beep::MatoPrepareComplete(TInt aError)
{
    if (aError == KErrNone) {
        iState = EBeepPrepared;
    }
}

void QS60Beep::MatoPlayComplete(TInt aError)
{
    Q_UNUSED(aError);
    iState=EBeepPrepared;
}


QHash<TInt, TUint> QApplicationPrivate::scanCodeCache;

static Qt::KeyboardModifiers mapToQtModifiers(TUint s60Modifiers)
{
    Qt::KeyboardModifiers result = Qt::NoModifier;

    if (s60Modifiers & EModifierKeypad)
        result |= Qt::KeypadModifier;
    if (s60Modifiers & EModifierShift || s60Modifiers & EModifierLeftShift
            || s60Modifiers & EModifierRightShift)
        result |= Qt::ShiftModifier;
    if (s60Modifiers & EModifierCtrl || s60Modifiers & EModifierLeftCtrl
            || s60Modifiers & EModifierRightCtrl)
        result |= Qt::ControlModifier;
    if (s60Modifiers & EModifierAlt || s60Modifiers & EModifierLeftAlt
            || s60Modifiers & EModifierRightAlt)
        result |= Qt::AltModifier;

    return result;
}

static void mapS60MouseEventTypeToQt(QEvent::Type *type, Qt::MouseButton *button, const TPointerEvent *pEvent)
{
    switch (pEvent->iType) {
    case TPointerEvent::EButton1Down:
        *type = QEvent::MouseButtonPress;
        *button = Qt::LeftButton;
        break;
    case TPointerEvent::EButton1Up:
        *type = QEvent::MouseButtonRelease;
        *button = Qt::LeftButton;
        break;
    case TPointerEvent::EButton2Down:
        *type = QEvent::MouseButtonPress;
        *button = Qt::MidButton;
        break;
    case TPointerEvent::EButton2Up:
        *type = QEvent::MouseButtonRelease;
        *button = Qt::MidButton;
        break;
    case TPointerEvent::EButton3Down:
        *type = QEvent::MouseButtonPress;
        *button = Qt::RightButton;
        break;
    case TPointerEvent::EButton3Up:
        *type = QEvent::MouseButtonRelease;
        *button = Qt::RightButton;
        break;
    case TPointerEvent::EDrag:
        *type = QEvent::MouseMove;
        *button = Qt::NoButton;
        break;
    case TPointerEvent::EMove:
        // Qt makes no distinction between move and drag
        *type = QEvent::MouseMove;
        *button = Qt::NoButton;
        break;
    default:
        *type = QEvent::None;
        *button = Qt::NoButton;
        break;
    }
    if (pEvent->iModifiers & EModifierDoubleClick){
        *type = QEvent::MouseButtonDblClick;
    }

    if (*type == QEvent::MouseButtonPress || *type == QEvent::MouseButtonDblClick)
        QApplicationPrivate::mouse_buttons = QApplicationPrivate::mouse_buttons | (*button);
    else if (*type == QEvent::MouseButtonRelease)
        QApplicationPrivate::mouse_buttons = QApplicationPrivate::mouse_buttons &(~(*button));

    QApplicationPrivate::mouse_buttons = QApplicationPrivate::mouse_buttons & Qt::MouseButtonMask;
}

//### Can be replaced with CAknLongTapDetector if animation is required.
//NOTE: if CAknLongTapDetector is used make sure it gets variated out of 3.1 and 3.2,.
//also MLongTapObserver needs to be changed to MAknLongTapDetectorCallBack if CAknLongTapDetector is used.
class QLongTapTimer : public CTimer
{
public:
    static QLongTapTimer* NewL(QAbstractLongTapObserver *observer);
    QLongTapTimer(QAbstractLongTapObserver *observer);
    void ConstructL();
public:
    void PointerEventL(const TPointerEvent &event);
    void RunL();
protected:
private:
    QAbstractLongTapObserver *m_observer;
    TPointerEvent m_event;
    QPoint m_pressedCoordinates;
    int m_dragDistance;
};

QLongTapTimer* QLongTapTimer::NewL(QAbstractLongTapObserver *observer)
{
    QLongTapTimer* self = new QLongTapTimer(observer);
    self->ConstructL();
    return self;
}
void QLongTapTimer::ConstructL()
{
    CTimer::ConstructL();
}

QLongTapTimer::QLongTapTimer(QAbstractLongTapObserver *observer):CTimer(CActive::EPriorityHigh)
{
    m_observer = observer;
    m_dragDistance = qApp->startDragDistance();
    CActiveScheduler::Add(this);
}

void QLongTapTimer::PointerEventL(const TPointerEvent& event)
{
    if ( event.iType == TPointerEvent::EDrag || event.iType == TPointerEvent::EButtonRepeat)
    {
        QPoint diff(QPoint(event.iPosition.iX,event.iPosition.iY) - m_pressedCoordinates);
        if (diff.manhattanLength() < m_dragDistance)
            return;
    }
    Cancel();
    m_event = event;
    if (event.iType == TPointerEvent::EButton1Down)
    {
        m_pressedCoordinates = QPoint(event.iPosition.iX,event.iPosition.iY);
        // must be same as KLongTapDelay in aknlongtapdetector.h
        After(800000);
    }
}
void QLongTapTimer::RunL()
{
    if (m_observer)
        m_observer->HandleLongTapEventL(m_event.iPosition, m_event.iParentPosition);
}

QSymbianControl::QSymbianControl(QWidget *w)
    : CCoeControl(), qwidget(w), m_ignoreFocusChanged(false)
{
}

void QSymbianControl::ConstructL(bool isWindowOwning, bool desktop)
{
    if (!desktop)
    {
        if (isWindowOwning or !qwidget->parentWidget())
            CreateWindowL(S60->windowGroup());
        else
            /**
             * TODO: in order to avoid creating windows for all ancestors of
             * this widget up to the root window, the parameter passed to
             * CreateWindowL should be
             * qwidget->parentWidget()->effectiveWinId().  However, if we do
             * this, then we need to take care of re-parenting when a window
             * is created for a widget between this one and the root window.
             */
            CreateWindowL(qwidget->parentWidget()->winId());

        // Necessary in order to be able to track the activation status of
        // the control's window
        qwidget->d_func()->createExtra();

        SetFocusing(true);
        m_longTapDetector = QLongTapTimer::NewL(this);
    }
}

QSymbianControl::~QSymbianControl()
{
    if (S60->curWin == this)
        S60->curWin = 0;
    if (!QApplicationPrivate::is_app_closing)
        setFocusSafely(false);
    S60->appUi()->RemoveFromStack(this);
    delete m_longTapDetector;
}

void QSymbianControl::setWidget(QWidget *w)
{
    qwidget = w;
}
void QSymbianControl::HandleLongTapEventL( const TPoint& aPenEventLocation, const TPoint& aPenEventScreenLocation )
{
    QWidget *alienWidget;
    QPoint widgetPos = QPoint(aPenEventLocation.iX, aPenEventLocation.iY);
    QPoint globalPos = QPoint(aPenEventScreenLocation.iX,aPenEventScreenLocation.iY);
    alienWidget = qwidget->childAt(widgetPos);
    if (!alienWidget)
        alienWidget = qwidget;
    QApplicationPrivate::mouse_buttons = QApplicationPrivate::mouse_buttons &(~Qt::LeftButton);
    QApplicationPrivate::mouse_buttons = QApplicationPrivate::mouse_buttons | Qt::RightButton;
    QMouseEvent mEvent(QEvent::MouseButtonPress, alienWidget->mapFrom(qwidget, widgetPos), globalPos,
        Qt::RightButton, QApplicationPrivate::mouse_buttons, Qt::NoModifier);

    bool res = sendMouseEvent(alienWidget, &mEvent);

#if !defined(QT_NO_CONTEXTMENU)
    QContextMenuEvent contextMenuEvent(QContextMenuEvent::Mouse, widgetPos, globalPos, mEvent.modifiers());
    qt_sendSpontaneousEvent(alienWidget, &contextMenuEvent);
#endif

    m_previousEventLongTap = true;
}

#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER
void QSymbianControl::translateAdvancedPointerEvent(const TAdvancedPointerEvent *event)
{
    QApplicationPrivate *d = QApplicationPrivate::instance();

    QRect screenGeometry = qApp->desktop()->screenGeometry(qwidget);

    while (d->appAllTouchPoints.count() <= event->PointerNumber())
        d->appAllTouchPoints.append(QTouchEvent::TouchPoint(d->appAllTouchPoints.count()));

    Qt::TouchPointStates allStates = 0;
    for (int i = 0; i < d->appAllTouchPoints.count(); ++i) {
        QTouchEvent::TouchPoint &touchPoint = d->appAllTouchPoints[i];

        if (touchPoint.id() == event->PointerNumber()) {
            Qt::TouchPointStates state;
            switch (event->iType) {
            case TPointerEvent::EButton1Down:
            case TPointerEvent::EEnterHighPressure:
                state = Qt::TouchPointPressed;
                break;
            case TPointerEvent::EButton1Up:
            case TPointerEvent::EExitCloseProximity:
                state = Qt::TouchPointReleased;
                break;
            case TPointerEvent::EDrag:
                state = Qt::TouchPointMoved;
                break;
            default:
                // how likely is this to happen?
                state = Qt::TouchPointStationary;
                break;
            }
            if (event->PointerNumber() == 0)
                state |= Qt::TouchPointPrimary;
            touchPoint.setState(state);

            QPointF screenPos = QPointF(event->iPosition.iX, event->iPosition.iY);
            touchPoint.setScreenPos(screenPos);
            touchPoint.setNormalizedPos(QPointF(screenPos.x() / screenGeometry.width(),
                                                screenPos.y() / screenGeometry.height()));

            touchPoint.setPressure(event->Pressure() / qreal(d->maxTouchPressure));
        } else if (touchPoint.state() != Qt::TouchPointReleased) {
            // all other active touch points should be marked as stationary
            touchPoint.setState(Qt::TouchPointStationary);
        }

        allStates |= touchPoint.state();
    }

    if ((allStates & Qt::TouchPointStateMask) == Qt::TouchPointReleased) {
        // all touch points released
        d->appAllTouchPoints.clear();
    }

    QApplicationPrivate::translateRawTouchEvent(qwidget,
                                                QTouchEvent::TouchScreen,
                                                d->appAllTouchPoints);
}
#endif

void QSymbianControl::HandlePointerEventL(const TPointerEvent& pEvent)
{
#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER
    if (pEvent.IsAdvancedPointerEvent()) {
        const TAdvancedPointerEvent *advancedPointerEvent = pEvent.AdvancedPointerEvent();
        translateAdvancedPointerEvent(advancedPointerEvent);
        if (advancedPointerEvent->PointerNumber() != 0) {
            // only send mouse events for the first touch point
            return;
        }
    }
#endif

    m_longTapDetector->PointerEventL(pEvent);
    QT_TRYCATCH_LEAVING(HandlePointerEvent(pEvent));
}

typedef QPair<QWidget*,QMouseEvent> Event;

/*
 * Helper function called by HandlePointerEvent - separated to keep that function readable
 */
static void generateEnterLeaveEvents(QList<Event> &events, QWidget *widgetUnderPointer,
    QPoint globalPos, Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
{
    //moved to another widget, create enter and leave events
    if (S60->lastPointerEventTarget) {
        QMouseEvent mEventLeave(QEvent::Leave, S60->lastPointerEventTarget->mapFromGlobal(
            S60->lastCursorPos), S60->lastCursorPos, button, QApplicationPrivate::mouse_buttons,
            modifiers);
        events.append(Event(S60->lastPointerEventTarget, mEventLeave));
    }
    if (widgetUnderPointer) {
        QMouseEvent mEventEnter(QEvent::Enter, widgetUnderPointer->mapFromGlobal(globalPos),
            globalPos, button, QApplicationPrivate::mouse_buttons, modifiers);

        events.append(Event(widgetUnderPointer, mEventEnter));
#ifndef QT_NO_CURSOR
        S60->curWin = widgetUnderPointer->effectiveWinId();
        if (!QApplication::overrideCursor()) {
#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
            if (S60->brokenPointerCursors)
                qt_symbian_set_pointer_sprite(widgetUnderPointer->cursor());
            else
#endif
                qt_symbian_setWindowCursor(widgetUnderPointer->cursor(), S60->curWin);
        }
#endif
    }
}


void QSymbianControl::HandlePointerEvent(const TPointerEvent& pEvent)
{
    QMouseEvent::Type type;
    Qt::MouseButton button;
    mapS60MouseEventTypeToQt(&type, &button, &pEvent);
    Qt::KeyboardModifiers modifiers = mapToQtModifiers(pEvent.iModifiers);

    if (m_previousEventLongTap)
        if (type == QEvent::MouseButtonRelease){
            button = Qt::RightButton;
            QApplicationPrivate::mouse_buttons = QApplicationPrivate::mouse_buttons & ~Qt::RightButton;
            m_previousEventLongTap = false;
        }
    if (type == QMouseEvent::None)
        return;

    // store events for later sending/saving
    QList<Event > events;

    QPoint widgetPos = QPoint(pEvent.iPosition.iX, pEvent.iPosition.iY);
    TPoint controlScreenPos = PositionRelativeToScreen();
    QPoint globalPos = QPoint(controlScreenPos.iX, controlScreenPos.iY) + widgetPos;

    // widgets interested in the event
    QWidget *widgetUnderPointer = qwidget->childAt(widgetPos);
    if (!widgetUnderPointer)
        widgetUnderPointer = qwidget; //i.e. this container widget

    QWidget *widgetWithMouseGrab = QWidget::mouseGrabber();

    // handle auto grab of pointer when pressing / releasing
    if (!widgetWithMouseGrab && type == QEvent::MouseButtonPress) {
        //if previously auto-grabbed, generate a fake mouse release (platform bug: mouse release event was lost)
        if (S60->mousePressTarget) {
            QMouseEvent mEvent(QEvent::MouseButtonRelease, S60->mousePressTarget->mapFromGlobal(globalPos), globalPos,
                button, QApplicationPrivate::mouse_buttons, modifiers);
            events.append(Event(S60->mousePressTarget,mEvent));
        }
        //auto grab the mouse
        widgetWithMouseGrab = S60->mousePressTarget = widgetUnderPointer;
        widgetWithMouseGrab->grabMouse();
    }
    if (widgetWithMouseGrab && widgetWithMouseGrab == S60->mousePressTarget && type == QEvent::MouseButtonRelease) {
        //release the auto grab - note this release event still goes to the autograb widget
        S60->mousePressTarget = 0;
        widgetWithMouseGrab->releaseMouse();
    }

    QWidget *widgetToReceiveMouseEvent;
    if (widgetWithMouseGrab)
        widgetToReceiveMouseEvent = widgetWithMouseGrab;
    else
        widgetToReceiveMouseEvent = widgetUnderPointer;

    //queue QEvent::Enter and QEvent::Leave, if the pointer has moved
    if (widgetUnderPointer != S60->lastPointerEventTarget && (type == QEvent::MouseButtonPress || type == QEvent::MouseButtonDblClick || type == QEvent::MouseMove))
        generateEnterLeaveEvents(events, widgetUnderPointer, globalPos, button, modifiers);

    //save global state
    S60->lastCursorPos = globalPos;
    S60->lastPointerEventPos = widgetPos;
    S60->lastPointerEventTarget = widgetUnderPointer;

#if !defined(QT_NO_CURSOR) && !defined(Q_SYMBIAN_FIXED_POINTER_CURSORS)
    if (S60->brokenPointerCursors)
        qt_symbian_move_cursor_sprite();
#endif

    //queue this event.
    Q_ASSERT(widgetToReceiveMouseEvent);
    QMouseEvent mEvent(type, widgetToReceiveMouseEvent->mapFromGlobal(globalPos), globalPos,
        button, QApplicationPrivate::mouse_buttons, modifiers);
    events.append(Event(widgetToReceiveMouseEvent,mEvent));
    QEventDispatcherS60 *dispatcher;
    // It is theoretically possible for someone to install a different event dispatcher.
    if ((dispatcher = qobject_cast<QEventDispatcherS60 *>(widgetToReceiveMouseEvent->d_func()->threadData->eventDispatcher)) != 0) {
        if (dispatcher->excludeUserInputEvents()) {
            for (int i=0;i < events.count();++i)
            {
                Event next = events[i];
                dispatcher->saveInputEvent(this, next.first, new QMouseEvent(next.second));
            }
            return;
        }
    }

    //send events in the queue
    for (int i=0;i < events.count();++i)
    {
        Event next = events[i];
        sendMouseEvent(next.first, &(next.second));
    }
}

bool QSymbianControl::sendMouseEvent(QWidget *widget, QMouseEvent *mEvent)
{
    return qt_sendSpontaneousEvent(widget, mEvent);
}

TKeyResponse QSymbianControl::OfferKeyEventL(const TKeyEvent& keyEvent, TEventCode type)
{
    TKeyResponse r = EKeyWasNotConsumed;
    QT_TRYCATCH_LEAVING(r = OfferKeyEvent(keyEvent, type));
    return r;
}

TKeyResponse QSymbianControl::OfferKeyEvent(const TKeyEvent& keyEvent, TEventCode type)
{
    switch (type) {
    //case EEventKeyDown: // <-- Intentionally left out. See below.
    case EEventKeyUp:
    case EEventKey:
    {
        // S60 has a confusing way of delivering key events. There are three types of
        // events: EKeyEvent, EKeyEventDown and EKeyEventUp. When a key is pressed, the
        // two first events are generated. When releasing the key, the last one is
        // generated.
        // Because S60 does not generate keysyms for EKeyEventDown and EKeyEventUp events,
        // we need to do some special tricks to map it to the Qt way. First, we completely
        // discard EKeyEventDown events, since they are redundant. Second, since
        // EKeyEventUp does not give us a keysym, we need to cache the keysyms from
        // the EKeyEvent events. This is what resolveS60ScanCode does.


        // ### hackish way to send Qt application to background when pressing right softkey
        /*
        if( keyEvent.iScanCode == EStdKeyDevice1 ) {
            S60->window_group->SetOrdinalPosition(-1);
            qApp->setActiveWindow(0);
            return EKeyWasNotConsumed;
        }
        */

        TUint s60Keysym = QApplicationPrivate::resolveS60ScanCode(keyEvent.iScanCode,
                keyEvent.iCode);
        int keyCode;
        if (s60Keysym >= 0x20 && s60Keysym < ENonCharacterKeyBase) {
            // Normal characters keys.
            keyCode = s60Keysym;
        } else {
            // Special S60 keys.
            keyCode = qt_keymapper_private()->mapS60KeyToQt(s60Keysym);
        }

#ifndef QT_NO_CURSOR
        if (S60->mouseInteractionEnabled && S60->virtualMouseRequired) {
            //translate keys to pointer
            if (keyCode >= Qt::Key_Left && keyCode <= Qt::Key_Down || keyCode == Qt::Key_Select) {
                /*Explanation about virtualMouseAccel:
                 Tapping an arrow key allows precise pixel positioning
                 Holding an arrow key down, acceleration is applied to allow cursor
                 to be quickly moved to another part of the screen by key repeats.
                 */
                if (S60->virtualMouseLastKey == keyCode) {
                    S60->virtualMouseAccel *= 2;
                    if (S60->virtualMouseAccel > S60->virtualMouseMaxAccel)
                        S60->virtualMouseAccel = S60->virtualMouseMaxAccel;
                }
                else
                    S60->virtualMouseAccel = 1;
                S60->virtualMouseLastKey = keyCode;

                QPoint pos = QCursor::pos();
                TPointerEvent fakeEvent;
                TInt x = pos.x();
                TInt y = pos.y();
                if (type == EEventKeyUp) {
                    if (keyCode == Qt::Key_Select)
                        fakeEvent.iType = TPointerEvent::EButton1Up;
                    S60->virtualMouseAccel = 1;
                    S60->virtualMouseLastKey = 0;
                }
                else if (type == EEventKey) {
                    switch (keyCode) {
                    case Qt::Key_Left:
                        x -= S60->virtualMouseAccel;
                        fakeEvent.iType = TPointerEvent::EMove;
                        break;
                    case Qt::Key_Right:
                        x += S60->virtualMouseAccel;
                        fakeEvent.iType = TPointerEvent::EMove;
                        break;
                    case Qt::Key_Up:
                        y -= S60->virtualMouseAccel;
                        fakeEvent.iType = TPointerEvent::EMove;
                        break;
                    case Qt::Key_Down:
                        y += S60->virtualMouseAccel;
                        fakeEvent.iType = TPointerEvent::EMove;
                        break;
                    case Qt::Key_Select:
                        fakeEvent.iType = TPointerEvent::EButton1Down;
                        break;
                    }
                }
                //clip to screen size (window server allows a sprite hotspot to be outside the screen)
                if (x < 0)
                    x = 0;
                else if (x >= S60->screenWidthInPixels)
                    x = S60->screenWidthInPixels - 1;
                if (y < 0)
                    y = 0;
                else if (y >= S60->screenHeightInPixels)
                    y = S60->screenHeightInPixels - 1;
                TPoint epos(x, y);
                TPoint cpos = epos - PositionRelativeToScreen();
                fakeEvent.iModifiers = keyEvent.iModifiers;
                fakeEvent.iPosition = cpos;
                fakeEvent.iParentPosition = epos;
                HandlePointerEvent(fakeEvent);
                return EKeyWasConsumed;
            }
            else {
                S60->virtualMouseLastKey = keyCode;
                S60->virtualMouseAccel = 1;
            }
        }
#endif

        Qt::KeyboardModifiers mods = mapToQtModifiers(keyEvent.iModifiers);
        QKeyEventEx qKeyEvent(type == EEventKeyUp ? QEvent::KeyRelease : QEvent::KeyPress, keyCode,
                mods, qt_keymapper_private()->translateKeyEvent(keyCode, mods),
                false, 1, keyEvent.iScanCode, s60Keysym, keyEvent.iModifiers);
//        WId wid = reinterpret_cast<RWindowGroup *>(keyEvent.Handle())->Child();
//        if (!wid)
//             Could happen if window isn't shown yet.
//            return EKeyWasNotConsumed;
        QWidget *widget;
        widget = QWidget::keyboardGrabber();
        if (!widget) {
            if (QApplicationPrivate::popupWidgets != 0) {
                widget = QApplication::activePopupWidget()->focusWidget();
                if (!widget) {
                    widget = QApplication::activePopupWidget();
                }
            } else {
                widget = QApplicationPrivate::focus_widget;
                if (!widget) {
                    widget = qwidget;
                }
            }
        }

        QEventDispatcherS60 *dispatcher;
        // It is theoretically possible for someone to install a different event dispatcher.
        if ((dispatcher = qobject_cast<QEventDispatcherS60 *>(widget->d_func()->threadData->eventDispatcher)) != 0) {
            if (dispatcher->excludeUserInputEvents()) {
                dispatcher->saveInputEvent(this, widget, new QKeyEventEx(qKeyEvent));
                return EKeyWasConsumed;
            }
        }
        return sendKeyEvent(widget, &qKeyEvent);
    }
    }
    return EKeyWasNotConsumed;
}

void QSymbianControl::sendInputEvent(QWidget *widget, QInputEvent *inputEvent)
{
    switch (inputEvent->type()) {
    case QEvent::KeyPress:
    case QEvent::KeyRelease:
        sendKeyEvent(widget, static_cast<QKeyEvent *>(inputEvent));
        break;
    case QEvent::MouseButtonDblClick:
    case QEvent::MouseButtonPress:
    case QEvent::MouseButtonRelease:
    case QEvent::MouseMove:
        sendMouseEvent(widget, static_cast<QMouseEvent *>(inputEvent));
        break;
    default:
        // Shouldn't get here.
        Q_ASSERT_X(0 == 1, "QSymbianControl::sendInputEvent()", "inputEvent->type() is unknown");
        break;
    }
}

TKeyResponse QSymbianControl::sendKeyEvent(QWidget *widget, QKeyEvent *keyEvent)
{
#if !defined(QT_NO_IM) && defined(Q_WS_S60)
    if (widget && widget->isEnabled() && widget->testAttribute(Qt::WA_InputMethodEnabled)) {
        QInputContext *qic = widget->inputContext();
        if (qic && qic->filterEvent(keyEvent))
            return EKeyWasConsumed;
    }
#endif // !defined(QT_NO_IM) && defined(Q_WS_S60)

    if (widget && qt_sendSpontaneousEvent(widget, keyEvent))
        if (keyEvent->isAccepted())
            return EKeyWasConsumed;

    return EKeyWasNotConsumed;
}

#if !defined(QT_NO_IM) && defined(Q_WS_S60)
TCoeInputCapabilities QSymbianControl::InputCapabilities() const
{
    QWidget *w = 0;

    if (qwidget->hasFocus())
        w = qwidget;
    else
        w = qwidget->focusWidget();

    QCoeFepInputContext *ic;
    if (w && w->isEnabled() && w->testAttribute(Qt::WA_InputMethodEnabled)
            && (ic = qobject_cast<QCoeFepInputContext *>(w->inputContext()))) {
        return ic->inputCapabilities();
    } else {
        return TCoeInputCapabilities(TCoeInputCapabilities::ENone, 0, 0);
    }
}
#endif

void QSymbianControl::Draw(const TRect& controlRect) const
{
    QWindowSurface *surface = qwidget->windowSurface();
    QPaintEngine *engine = surface ? surface->paintDevice()->paintEngine() : NULL;

    if (!engine)
        return;

    // Map source rectangle into coordinates of the backing store.
    const QPoint controlBase(controlRect.iTl.iX, controlRect.iTl.iY);
    const QPoint backingStoreBase = qwidget->mapTo(qwidget->window(), controlBase);
    const TRect backingStoreRect(TPoint(backingStoreBase.x(), backingStoreBase.y()), controlRect.Size());

    if (engine->type() == QPaintEngine::Raster) {
        QS60WindowSurface *s60Surface = static_cast<QS60WindowSurface *>(qwidget->windowSurface());
        CFbsBitmap *bitmap = s60Surface->symbianBitmap();
        CWindowGc &gc = SystemGc();

        if(!qwidget->d_func()->extraData()->disableBlit) {
            if (qwidget->d_func()->isOpaque)
                gc.SetDrawMode(CGraphicsContext::EDrawModeWriteAlpha);
            gc.BitBlt(controlRect.iTl, bitmap, backingStoreRect);
	    }
    } else {
        surface->flush(qwidget, QRegion(qt_TRect2QRect(backingStoreRect)), QPoint());
    }
}

void QSymbianControl::SizeChanged()
{
    CCoeControl::SizeChanged();

    QSize oldSize = qwidget->size();
    QSize newSize(Size().iWidth, Size().iHeight);

    if (oldSize != newSize) {
        QRect cr = qwidget->geometry();
        cr.setSize(newSize);
        qwidget->data->crect = cr;
        if (qwidget->isVisible()) {
            QTLWExtra *tlwExtra = qwidget->d_func()->maybeTopData();
            bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt();
            if (!slowResize && tlwExtra)
                tlwExtra->inTopLevelResize = true;
            QResizeEvent e(newSize, oldSize);
            qt_sendSpontaneousEvent(qwidget, &e);
            if (!qwidget->testAttribute(Qt::WA_StaticContents))
                qwidget->d_func()->syncBackingStore();
            if (!slowResize && tlwExtra)
                tlwExtra->inTopLevelResize = false;
        }
    }
}

void QSymbianControl::PositionChanged()
{
    CCoeControl::PositionChanged();

    QPoint oldPos = qwidget->geometry().topLeft();
    QPoint newPos(Position().iX, Position().iY);

    if (oldPos != newPos) {
        QRect cr = qwidget->geometry();
        cr.moveTopLeft(newPos);
        qwidget->data->crect = cr;
        QTLWExtra *top = qwidget->d_func()->maybeTopData();
        if (top)
            top->normalGeometry = cr;
        if (qwidget->isVisible()) {
            QMoveEvent e(newPos, oldPos);
            qt_sendSpontaneousEvent(qwidget, &e);
        } else {
            QMoveEvent * e = new QMoveEvent(newPos, oldPos);
            QApplication::postEvent(qwidget, e);
        }
    }
}

void QSymbianControl::FocusChanged(TDrawNow /* aDrawNow */)
{
    if (m_ignoreFocusChanged)
        return;

    // Popups never get focused, but still receive the FocusChanged when they are hidden.
    if (QApplicationPrivate::popupWidgets != 0
            || (qwidget->windowType() & Qt::Popup) == Qt::Popup)
        return;

    if (IsFocused() && IsVisible()) {
        QApplication::setActiveWindow(qwidget->window());
#ifdef Q_WS_S60
        // If widget is fullscreen, hide status pane and button container
        // otherwise show them.
        CEikStatusPane* statusPane = S60->statusPane();
        CEikButtonGroupContainer* buttonGroup = S60->buttonGroupContainer();
        bool isFullscreen = qwidget->windowState() & Qt::WindowFullScreen;
        if (statusPane && (statusPane->IsVisible() == isFullscreen))
            statusPane->MakeVisible(!isFullscreen);
        if (buttonGroup && (buttonGroup->IsVisible() == isFullscreen))
            buttonGroup->MakeVisible(!isFullscreen);
#endif
    } else if (QApplication::activeWindow() == qwidget->window()) {
        QApplication::setActiveWindow(0);
    }
    // else { We don't touch the active window unless we were explicitly activated or deactivated }
}

void QSymbianControl::HandleResourceChange(int resourceType)
{
    switch (resourceType) {
    case KInternalStatusPaneChange:
        qwidget->d_func()->setWindowIcon_sys(true);
        break;
    case KUidValueCoeFontChangeEvent:
        // font change event
        break;
#ifdef Q_WS_S60
    case KEikDynamicLayoutVariantSwitch:
    {
        if (qwidget->isFullScreen()) {
            SetExtentToWholeScreen();
        } else if (qwidget->isMaximized()) {
            TRect r = static_cast<CEikAppUi*>(S60->appUi())->ClientRect();
            SetExtent(r.iTl, r.Size());
        }
        break;
    }
#endif
    default:
        break;
    }

    CCoeControl::HandleResourceChange(resourceType);

}
void QSymbianControl::CancelLongTapTimer()
{
    m_longTapDetector->Cancel();
}

TTypeUid::Ptr QSymbianControl::MopSupplyObject(TTypeUid id)
{
    if (id.iUid == ETypeId)
        return id.MakePtr(this);

    return CCoeControl::MopSupplyObject(id);
}

void QSymbianControl::setFocusSafely(bool focus)
{
    // The stack hack in here is very unfortunate, but it is the only way to ensure proper
    // focus in Symbian. If this is not executed, the control which happens to be on
    // the top of the stack may randomly be assigned focus by Symbian, for example
    // when creating new windows (specifically in CCoeAppUi::HandleStackChanged()).
    if (focus) {
        S60->appUi()->RemoveFromStack(this);
        // Symbian doesn't automatically remove focus from the last focused control, so we need to
        // remember it and clear focus ourselves.
        if (lastFocusedControl && lastFocusedControl != this)
            lastFocusedControl->SetFocus(false);
        QT_TRAP_THROWING(S60->appUi()->AddToStackL(this,
                ECoeStackPriorityDefault + 1, ECoeStackFlagStandard)); // Note the + 1
        lastFocusedControl = this;
        this->SetFocus(true);
    } else {
        S60->appUi()->RemoveFromStack(this);
        QT_TRAP_THROWING(S60->appUi()->AddToStackL(this,
                ECoeStackPriorityDefault, ECoeStackFlagStandard));
        if(this == lastFocusedControl)
            lastFocusedControl = 0;
        this->SetFocus(false);
    }
}

/*!
    \typedef QApplication::QS60MainApplicationFactory
    \since 4.6

    This is a typedef for a pointer to a function with the following
    signature:

    \snippet doc/src/snippets/code/src_corelib_global_qglobal.cpp 47

    \sa QApplication::QApplication()
*/

/*!
    \since 4.6

    Creates an application using the application factory given in
    \a factory, and using \a argc command line arguments in \a argv.
    \a factory can be leaving, but the error will be converted to a
    standard exception.

    This function is only available on S60.
*/
QApplication::QApplication(QApplication::QS60MainApplicationFactory factory, int &argc, char **argv)
    : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient))
{
    Q_D(QApplication);
    S60->s60ApplicationFactory = factory;
    d->construct();
}

QApplication::QApplication(QApplication::QS60MainApplicationFactory factory, int &argc, char **argv, int _internal)
    : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient))
{
    Q_D(QApplication);
    S60->s60ApplicationFactory = factory;
    d->construct();
    QApplicationPrivate::app_compile_version = _internal;
}

void qt_init(QApplicationPrivate * /* priv */, int)
{
    if (!CCoeEnv::Static()) {
        // The S60 framework creates a new trap handler which will render any existing traps
        // invalid as long as it is active. This means that all code in main() that occurs after
        // the QApplication construction needs to be surrounded by a new trap, despite having
        // an outer one already. To avoid this, we save the original trap handler here, and set
        // it back after the S60 framework is constructed. Then we restore it right before the S60
        // framework destruction.
        TTrapHandler *origTrapHandler = User::TrapHandler();

        // The S60 framework has not been initalized. We need to do it.
        TApaApplicationFactory factory(S60->s60ApplicationFactory ?
                S60->s60ApplicationFactory : newS60Application);
        CApaCommandLine* commandLine = 0;
        TInt err = CApaCommandLine::GetCommandLineFromProcessEnvironment(commandLine);
        // After this construction, CEikonEnv will be available from CEikonEnv::Static().
        // (much like our qApp).
        CEikonEnv* coe = new CEikonEnv;
        QT_TRAP_THROWING(coe->ConstructAppFromCommandLineL(factory,*commandLine));
        delete commandLine;

        S60->s60InstalledTrapHandler = User::SetTrapHandler(origTrapHandler);

        S60->qtOwnsS60Environment = true;
    } else {
        S60->qtOwnsS60Environment = false;
    }

#ifdef QT_NO_DEBUG
    if (!qgetenv("QT_S60_AUTO_FLUSH_WSERV").isEmpty())
#endif
        S60->wsSession().SetAutoFlush(ETrue);

    S60->updateScreenSize();


    TDisplayMode mode = S60->screenDevice()->DisplayMode();
    S60->screenDepth = TDisplayModeUtils::NumDisplayModeBitsPerPixel(mode);

    //NB: RWsSession::GetColorModeList tells you what window modes are supported,
    //not what bitmap formats.
    if(QSysInfo::symbianVersion() == QSysInfo::SV_9_2)
        S60->supportsPremultipliedAlpha = 0;
    else
        S60->supportsPremultipliedAlpha = 1;

    RProcess me;
    TSecureId securId = me.SecureId();
    S60->uid = securId.operator TUid();

    // enable focus events - used to re-enable mouse after focus changed between mouse and non mouse app,
    // and for dimming behind modal windows
	S60->windowGroup().EnableFocusChangeEvents();

	//Check if mouse interaction is supported (either EMouse=1 in the HAL, or EMachineUID is one of the phones known to support this)
    const TInt KMachineUidSamsungI8510 = 0x2000C51E;
    // HAL::Get(HALData::EPen, TInt& result) may set 'result' to 1 on some 3.1 systems (e.g. N95).
    // But we know that S60 systems below 5.0 did not support touch.
    static const bool touchIsUnsupportedOnSystem =
        QSysInfo::s60Version() == QSysInfo::SV_S60_3_1
        || QSysInfo::s60Version() == QSysInfo::SV_S60_3_2;
    TInt machineUID;
    TInt mouse;
    TInt touch;
    TInt err;
    err = HAL::Get(HALData::EMouse, mouse);
    if (err != KErrNone)
        mouse = 0;
    err = HAL::Get(HALData::EMachineUid, machineUID);
    if (err != KErrNone)
        machineUID = 0;
    err = HAL::Get(HALData::EPen, touch);
    if (err != KErrNone || touchIsUnsupportedOnSystem)
        touch = 0;
#ifdef __WINS__
    if(QSysInfo::symbianVersion() <= QSysInfo::SV_9_4) {
        //for symbian SDK emulator, force values to match typical devices.
        mouse = 0;
        touch = touchIsUnsupportedOnSystem ? 0 : 1;
    }
#endif
    if (mouse || machineUID == KMachineUidSamsungI8510) {
        S60->hasTouchscreen = false;
        S60->virtualMouseRequired = false;
    }
    else if (!touch) {
        S60->hasTouchscreen = false;
        S60->virtualMouseRequired = true;
    }
    else {
        S60->hasTouchscreen = true;
        S60->virtualMouseRequired = false;
    }

    if (touch) {
        QApplicationPrivate::navigationMode = Qt::NavigationModeNone;
    } else {
        QApplicationPrivate::navigationMode = Qt::NavigationModeKeypadDirectional;
    }

#ifndef QT_NO_CURSOR
    //Check if window server pointer cursors are supported or not
#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
    //In generic binary, use the HAL and OS version
    //Any other known good phones should be added here.
    if (machineUID == KMachineUidSamsungI8510 || (QSysInfo::symbianVersion() != QSysInfo::SV_9_4
        && QSysInfo::symbianVersion() != QSysInfo::SV_9_3 && QSysInfo::symbianVersion()
        != QSysInfo::SV_9_2)) {
        S60->brokenPointerCursors = false;
        qt_symbian_setWindowGroupCursor(Qt::ArrowCursor, S60->windowGroup());
    }
    else
        S60->brokenPointerCursors = true;
#endif

    if (S60->mouseInteractionEnabled) {
#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
        if (S60->brokenPointerCursors) {
            qt_symbian_set_pointer_sprite(Qt::ArrowCursor);
            qt_symbian_show_pointer_sprite();
        }
        else
#endif
            S60->wsSession().SetPointerCursorMode(EPointerCursorNormal);
    }
#endif

    QFont systemFont;
    systemFont.setFamily(systemFont.defaultFamily());
    QApplicationPrivate::setSystemFont(systemFont);

/*
 ### Commented out for now as parameter handling not needed in SOS(yet). Code below will break testlib with -o flag
    int argc = priv->argc;
    char **argv = priv->argv;

    // Get command line params
    int j = argc ? 1 : 0;
    for (int i=1; i<argc; i++) {
        if (argv[i] && *argv[i] != '-') {
            argv[j++] = argv[i];
            continue;
        }

#if defined(QT_DEBUG)
        if (qstrcmp(argv[i], "-nograb") == 0)
            appNoGrab = !appNoGrab;
        else
#endif // QT_DEBUG
            ;
    }
*/

    // Register WId with the metatype system.  This is to enable
    // QWidgetPrivate::create_sys to used delayed slot invokation in order
    // to destroy WId objects during reparenting.
    qRegisterMetaType<WId>("WId");
}

/*****************************************************************************
  qt_cleanup() - cleans up when the application is finished
 *****************************************************************************/
void qt_cleanup()
{
    QFontCache::cleanup(); // Has to happen now, since QFontEngineS60 has FBS handles
// S60 structure and window server session are freed in eventdispatcher destructor as they are needed there

    // It's important that this happens here, before the event dispatcher gets
    // deleted, because the input context needs the event loop one last time before
    // it dies.
    delete QApplicationPrivate::inputContext;
    QApplicationPrivate::inputContext = 0;

    //Change mouse pointer back
    S60->wsSession().SetPointerCursorMode(EPointerCursorNone);

    if (S60->qtOwnsS60Environment) {
        // Restore the S60 framework trap handler. See qt_init().
        User::SetTrapHandler(S60->s60InstalledTrapHandler);

        CEikonEnv* coe = CEikonEnv::Static();
        coe->PrepareToExit();
        // The CEikonEnv itself is destroyed in here.
        coe->DestroyEnvironment();
    }
}

void QApplicationPrivate::initializeWidgetPaletteHash()
{
    // TODO: Implement QApplicationPrivate::initializeWidgetPaletteHash()
    // Possibly a task fot the S60Style guys
}

void QApplicationPrivate::createEventDispatcher()
{
    Q_Q(QApplication);
    eventDispatcher = new QEventDispatcherS60(q);
}

QString QApplicationPrivate::appName() const
{
    return QCoreApplicationPrivate::appName();
}

bool QApplicationPrivate::modalState()
{
    return app_do_modal;
}

void QApplicationPrivate::enterModal_sys(QWidget *widget)
{
    if (widget) {
        widget->effectiveWinId()->DrawableWindow()->FadeBehind(ETrue);
        // Modal partial screen dialogs (like queries) capture pointer events.
        // ### FixMe: Add specialized behaviour for fullscreen modal dialogs
        widget->effectiveWinId()->SetGloballyCapturing(ETrue);
        widget->effectiveWinId()->SetPointerCapture(ETrue);
    }
    if (!qt_modal_stack)
        qt_modal_stack = new QWidgetList;
    qt_modal_stack->insert(0, widget);
    app_do_modal = true;
}

void QApplicationPrivate::leaveModal_sys(QWidget *widget)
{
    if (widget) {
        widget->effectiveWinId()->DrawableWindow()->FadeBehind(EFalse);
        // ### FixMe: Add specialized behaviour for fullscreen modal dialogs
        widget->effectiveWinId()->SetGloballyCapturing(EFalse);
        widget->effectiveWinId()->SetPointerCapture(EFalse);
    }
    if (qt_modal_stack && qt_modal_stack->removeAll(widget)) {
        if (qt_modal_stack->isEmpty()) {
            delete qt_modal_stack;
            qt_modal_stack = 0;
        }
    }
    app_do_modal = qt_modal_stack != 0;
}

void QApplicationPrivate::openPopup(QWidget *popup)
{
    if (!QApplicationPrivate::popupWidgets)
        QApplicationPrivate::popupWidgets = new QWidgetList;
    QApplicationPrivate::popupWidgets->append(popup);


    // Cancel focus widget pointer capture and long tap timer
    if (QApplication::focusWidget()) {
        static_cast<QSymbianControl*>(QApplication::focusWidget()->effectiveWinId())->CancelLongTapTimer();
        QApplication::focusWidget()->effectiveWinId()->SetPointerCapture(false);
        }

    if (!qt_nograb()) {
        // Cancel pointer capture and long tap timer for earlier popup
        int popupCount = QApplicationPrivate::popupWidgets->count();
        if (popupCount > 1) {
            QWidget* prevPopup = QApplicationPrivate::popupWidgets->at(popupCount-2);
            static_cast<QSymbianControl*>(prevPopup->effectiveWinId())->CancelLongTapTimer();
            prevPopup->effectiveWinId()->SetPointerCapture(false);
        }

        // Enable pointer capture for this (topmost) popup
        Q_ASSERT(popup->testAttribute(Qt::WA_WState_Created));
        WId id = popup->effectiveWinId();
        id->SetPointerCapture(true);
    }

    // popups are not focus-handled by the window system (the first
    // popup grabbed the keyboard), so we have to do that manually: A
    // new popup gets the focus
    QWidget *fw = popup->focusWidget();
    if (fw) {
        fw->setFocus(Qt::PopupFocusReason);
    } else if (QApplicationPrivate::popupWidgets->count() == 1) { // this was the first popup
        fw = QApplication::focusWidget();
        if (fw) {
            QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason);
            q_func()->sendEvent(fw, &e);
        }
    }
}

void QApplicationPrivate::closePopup(QWidget *popup)
{
    if (!QApplicationPrivate::popupWidgets)
        return;
    QApplicationPrivate::popupWidgets->removeAll(popup);

    // Cancel pointer capture and long tap for this popup
    WId id = popup->effectiveWinId();
    id->SetPointerCapture(false);
    static_cast<QSymbianControl*>(id)->CancelLongTapTimer();

    if (QApplicationPrivate::popupWidgets->isEmpty()) { // this was the last popup
        delete QApplicationPrivate::popupWidgets;
        QApplicationPrivate::popupWidgets = 0;
        if (!qt_nograb()) {                        // grabbing not disabled
            Q_ASSERT(popup->testAttribute(Qt::WA_WState_Created));
            if (QWidgetPrivate::mouseGrabber != 0)
                QWidgetPrivate::mouseGrabber->grabMouse();

            if (QWidgetPrivate::keyboardGrabber != 0)
                QWidgetPrivate::keyboardGrabber->grabKeyboard();

        QWidget *fw = QApplicationPrivate::active_window ? QApplicationPrivate::active_window->focusWidget()
              : q_func()->focusWidget();
          if (fw) {
              if (fw != q_func()->focusWidget()) {
                  fw->setFocus(Qt::PopupFocusReason);
              } else {
                  QFocusEvent e(QEvent::FocusIn, Qt::PopupFocusReason);
                  q_func()->sendEvent(fw, &e);
              }
          }
        }
    } else {

        // popups are not focus-handled by the window system (the
        // first popup grabbed the keyboard), so we have to do that
        // manually: A popup was closed, so the previous popup gets
        // the focus.
        QWidget* aw = QApplicationPrivate::popupWidgets->last();
        if (QWidget *fw = QApplication::focusWidget()) {
            QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason);
            q_func()->sendEvent(fw, &e);
        }

        // Enable pointer capture for previous popup
        if (aw) {
            aw->effectiveWinId()->SetPointerCapture(true);
        }
    }
}

QWidget * QApplication::topLevelAt(QPoint const& point)
{
    QWidget *found = 0;
    int lowestZ = INT_MAX;
    QWidgetList list = QApplication::topLevelWidgets();
    for (int i = 0; i < list.count(); ++i) {
        QWidget *widget = list.at(i);
        if (widget->isVisible() && !(widget->windowType() == Qt::Desktop)) {
            Q_ASSERT(widget->testAttribute(Qt::WA_WState_Created));
            if (widget->geometry().adjusted(0,0,1,1).contains(point)) {
                // At this point we know there is a Qt widget under the point.
                // Now we need to make sure it is the top most in the z-order.
                RDrawableWindow *const window = widget->effectiveWinId()->DrawableWindow();
                int z = window->OrdinalPosition();
                if (z < lowestZ) {
                    lowestZ = z;
                    found = widget;
                }
            }
        }
    }
    return found;
}

void QApplication::alert(QWidget * /* widget */, int /* duration */)
{
    // TODO: Implement QApplication::alert(QWidget *widget, int duration)
}

int QApplication::doubleClickInterval()
{
    TTimeIntervalMicroSeconds32 us;
    TInt distance;
    S60->wsSession().GetDoubleClickSettings(us, distance);
    return (us.Int() / 1000);
}

void QApplication::setDoubleClickInterval(int ms)
{
    TTimeIntervalMicroSeconds32 newUs( ms * 1000);
    TTimeIntervalMicroSeconds32 us;
    TInt distance;
    S60->wsSession().GetDoubleClickSettings(us, distance);
    if (us != newUs)
        S60->wsSession().SetDoubleClick(newUs, distance);
}

int QApplication::keyboardInputInterval()
{
    return QApplicationPrivate::keyboard_input_time;
}

void QApplication::setKeyboardInputInterval(int ms)
{
    QApplicationPrivate::keyboard_input_time = ms;
}

int QApplication::cursorFlashTime()
{
    return QApplicationPrivate::cursor_flash_time;
}

void QApplication::setCursorFlashTime(int msecs)
{
    QApplicationPrivate::cursor_flash_time = msecs;
}

void QApplication::beep()
{
    TInt frequency=440;
    TTimeIntervalMicroSeconds duration(500000);
    QS60Beep* beep=NULL;
    TRAPD(err, beep=QS60Beep::NewL(frequency, duration));
    if (!err)
        beep->Play();
    delete beep;
    beep=NULL;
}

/*!
    \warning This function is only available on Symbian.
    \since 4.6

    This function processes an individual Symbian event
    \a event. It returns 1 if the event was handled, 0 if
    the \a event was not handled, and -1 if the event was
    not handled because the event is not known to Qt.
 */

int QApplication::symbianProcessEvent(const QSymbianEvent *event)
{
    Q_D(QApplication);

    QScopedLoopLevelCounter counter(d->threadData);

    QWidget *w = qApp ? qApp->focusWidget() : 0;
    if (w) {
        QInputContext *ic = w->inputContext();
        if (ic && ic->symbianFilterEvent(w, event))
            return 1;
    }

    if (symbianEventFilter(event))
        return 1;

    switch (event->type()) {
    case QSymbianEvent::WindowServerEvent:
        return d->symbianProcessWsEvent(event->windowServerEvent());
    case QSymbianEvent::CommandEvent:
        return d->symbianHandleCommand(event->command());
    case QSymbianEvent::ResourceChangeEvent:
        return d->symbianResourceChange(event->resourceChangeType());
    default:
        return -1;
    }
}

int QApplicationPrivate::symbianProcessWsEvent(const TWsEvent *event)
{
    // Qt event handling. Handle some events regardless of if the handle is in our
    // widget map or not.
    CCoeControl* control = reinterpret_cast<CCoeControl*>(event->Handle());
    const bool controlInMap = QWidgetPrivate::mapper && QWidgetPrivate::mapper->contains(control);
    switch (event->Type()) {
    case EEventPointerEnter:
        if (controlInMap)
            return 1; // Qt::Enter will be generated in HandlePointerL
        break;
    case EEventPointerExit:
        if (controlInMap) {
            if (S60) {
                // mouseEvent outside our window, send leave event to last focused widget
                QMouseEvent mEvent(QEvent::Leave, S60->lastPointerEventPos, S60->lastCursorPos,
                    Qt::NoButton, QApplicationPrivate::mouse_buttons, Qt::NoModifier);
                if (S60->lastPointerEventTarget)
                    qt_sendSpontaneousEvent(S60->lastPointerEventTarget,&mEvent);
                S60->lastPointerEventTarget = 0;
            }
            return 1;
        }
        break;
    case EEventScreenDeviceChanged:
        if (S60)
            S60->updateScreenSize();
        if (qt_desktopWidget) {
            QSize oldSize = qt_desktopWidget->size();
            qt_desktopWidget->data->crect.setWidth(S60->screenWidthInPixels);
            qt_desktopWidget->data->crect.setHeight(S60->screenHeightInPixels);
            QResizeEvent e(qt_desktopWidget->size(), oldSize);
            QApplication::sendEvent(qt_desktopWidget, &e);
        }
        return 0; // Propagate to CONE
    case EEventWindowVisibilityChanged:
        if (controlInMap) {
            const TWsVisibilityChangedEvent *visChangedEvent = event->VisibilityChanged();
            QWidget *w = QWidgetPrivate::mapper->value(control);
            if (!w->d_func()->maybeTopData())
                break;
            if (visChangedEvent->iFlags & TWsVisibilityChangedEvent::ENotVisible) {
                delete w->d_func()->topData()->backingStore;
                w->d_func()->topData()->backingStore = 0;
                // :QTP:QT-2506:remove this when QT-2506 is fixed
                if (S60)
                    S60->wsSession().Flush();
            } else if ((visChangedEvent->iFlags & TWsVisibilityChangedEvent::EPartiallyVisible)
                       && !w->d_func()->maybeBackingStore()) {
                w->d_func()->topData()->backingStore = new QWidgetBackingStore(w);
                w->d_func()->invalidateBuffer(w->rect());
                w->repaint();
            }
            return 1;
        }
        break;
    case EEventFocusGained:
#ifndef QT_NO_CURSOR
        //re-enable mouse interaction
        if (S60->mouseInteractionEnabled) {
#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
            if (S60->brokenPointerCursors)
                qt_symbian_show_pointer_sprite();
            else
#endif
                S60->wsSession().SetPointerCursorMode(EPointerCursorNormal);
        }
#endif
        break;
    case EEventFocusLost:
#ifndef QT_NO_CURSOR
        //disable mouse as may be moving to application that does not support it
        if (S60->mouseInteractionEnabled) {
#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
            if (S60->brokenPointerCursors)
                qt_symbian_hide_pointer_sprite();
            else
#endif
                S60->wsSession().SetPointerCursorMode(EPointerCursorNone);
        }
#endif
        break;
	default:
        break;
    }

    if (!controlInMap)
        return -1;

    return 0;
}

/*!
  \warning This virtual function is only available on Symbian.
  \since 4.6

  If you create an application that inherits QApplication and reimplement
  this function, you get direct access to events that the are received
  from Symbian. The events are passed in the \a event parameter.

  Return true if you want to stop the event from being processed. Return
  false for normal event dispatching. The default implementation returns
  false, and does nothing with \a event.
 */
bool QApplication::symbianEventFilter(const QSymbianEvent *event)
{
    Q_UNUSED(event);
    return false;
}

/*!
  \warning This function is only available on Symbian.
  \since 4.6

  Handles \a{command}s which are typically handled by
  CAknAppUi::HandleCommandL(). Qts Ui integration into Symbian is
  partially achieved by deriving from CAknAppUi. Currently, exit,
  menu and softkey commands are handled.

  \sa s60EventFilter(), s60ProcessEvent()
*/
int QApplicationPrivate::symbianHandleCommand(int command)
{
    Q_Q(QApplication);
    int ret = 0;

    switch (command) {
#ifdef Q_WS_S60
    case EAknSoftkeyExit: {
        QCloseEvent ev;
        QApplication::sendSpontaneousEvent(q, &ev);
        if (ev.isAccepted()) {
            q->quit();
            ret = 1;
        }
        break;
    }
#endif
    case EEikCmdExit:
        q->quit();
        ret = 1;
        break;
    default:
        bool handled = QSoftKeyManager::handleCommand(command);
        if (handled)
            ret = 1;
#ifdef Q_WS_S60
        else
            ret = QMenuBarPrivate::symbianCommands(command);
#endif
        break;
    }

    return ret;
}

/*!
  \warning This function is only available on Symbian.
  \since 4.6

  Handles the resource change specified by \a type.

  Currently, KEikDynamicLayoutVariantSwitch and
  KAknsMessageSkinChange are handled.
 */
int QApplicationPrivate::symbianResourceChange(int type)
{
    int ret = 0;

    switch (type) {
#ifdef Q_WS_S60
    case KEikDynamicLayoutVariantSwitch:
        {
        if (S60)
            S60->updateScreenSize();

#ifndef QT_NO_STYLE_S60
        QS60Style *s60Style = 0;

#ifndef QT_NO_STYLE_STYLESHEET
        QStyleSheetStyle *proxy = qobject_cast<QStyleSheetStyle*>(QApplication::style());
        if (proxy)
            s60Style = qobject_cast<QS60Style*>(proxy->baseStyle());
        else
#endif
            s60Style = qobject_cast<QS60Style*>(QApplication::style());

        if (s60Style) {
            s60Style->d_func()->handleDynamicLayoutVariantSwitch();
            ret = 1;
        }
#endif
        }
        break;

#ifndef QT_NO_STYLE_S60
    case KAknsMessageSkinChange:
        if (QS60Style *s60Style = qobject_cast<QS60Style*>(QApplication::style())) {
            s60Style->d_func()->handleSkinChange();
            ret = 1;
        }
        break;
#endif
#endif // Q_WS_S60
    default:
        break;
    }

    return ret;
}

#ifndef QT_NO_WHEELEVENT
int QApplication::wheelScrollLines()
{
    return QApplicationPrivate::wheel_scroll_lines;
}

void QApplication::setWheelScrollLines(int n)
{
    QApplicationPrivate::wheel_scroll_lines = n;
}
#endif //QT_NO_WHEELEVENT

bool QApplication::isEffectEnabled(Qt::UIEffect /* effect */)
{
    // TODO: Implement QApplication::isEffectEnabled(Qt::UIEffect effect)
    return false;
}

void QApplication::setEffectEnabled(Qt::UIEffect /* effect */, bool /* enable */)
{
    // TODO: Implement QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable)
}

TUint QApplicationPrivate::resolveS60ScanCode(TInt scanCode, TUint keysym)
{
    if (keysym) {
        // If keysym is specified, cache it.
        scanCodeCache.insert(scanCode, keysym);
        return keysym;
    } else {
        // If not, retrieve the cached version.
        return scanCodeCache[scanCode];
    }
}

void QApplicationPrivate::initializeMultitouch_sys()
{
#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER
    if (HAL::Get(HALData::EPointer3DMaxPressure, maxTouchPressure) != KErrNone)
        maxTouchPressure = KMaxTInt;
#endif
}

void QApplicationPrivate::cleanupMultitouch_sys()
{ }

#ifndef QT_NO_SESSIONMANAGER
QSessionManager::QSessionManager(QApplication * /* app */, QString & /* id */, QString& /* key */)
{

}

QSessionManager::~QSessionManager()
{

}

bool QSessionManager::allowsInteraction()
{
    return false;
}

void QSessionManager::cancel()
{

}
#endif //QT_NO_SESSIONMANAGER

#ifdef QT_KEYPAD_NAVIGATION
/*
 * Show/Hide the mouse cursor depending on phone type and chosen mode
 */
void QApplicationPrivate::setNavigationMode(Qt::NavigationMode mode)
{
#ifndef QT_NO_CURSOR
    const bool wasCursorOn = (QApplicationPrivate::navigationMode == Qt::NavigationModeCursorAuto
        && !S60->hasTouchscreen)
        || QApplicationPrivate::navigationMode == Qt::NavigationModeCursorForceVisible;
    const bool isCursorOn = (mode == Qt::NavigationModeCursorAuto
        && !S60->hasTouchscreen)
        || mode == Qt::NavigationModeCursorForceVisible;

    if (!wasCursorOn && isCursorOn) {
        //Show the cursor, when changing from another mode to cursor mode
        qt_symbian_set_cursor_visible(true);
    }
    else if (wasCursorOn && !isCursorOn) {
        //Hide the cursor, when leaving cursor mode
        qt_symbian_set_cursor_visible(false);
    }
#endif
    QApplicationPrivate::navigationMode = mode;
}
#endif

#ifndef QT_NO_CURSOR
/*****************************************************************************
 QApplication cursor stack
 *****************************************************************************/

void QApplication::setOverrideCursor(const QCursor &cursor)
{
    qApp->d_func()->cursor_list.prepend(cursor);
    qt_symbian_setGlobalCursor(cursor);
}

void QApplication::restoreOverrideCursor()
{
    if (qApp->d_func()->cursor_list.isEmpty())
        return;
    qApp->d_func()->cursor_list.removeFirst();

    if (!qApp->d_func()->cursor_list.isEmpty()) {
        qt_symbian_setGlobalCursor(qApp->d_func()->cursor_list.first());
    }
    else {
        //determine which widget has focus
        QWidget *w = QApplication::widgetAt(QCursor::pos());
#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
        if (S60->brokenPointerCursors) {
            qt_symbian_set_pointer_sprite(w ? w->cursor() : Qt::ArrowCursor);
        }
        else
#endif
        {
            //because of the internals of window server, we need to force the cursor
            //to be set in all child windows too, otherwise when the cursor is over
            //the child window it may show a widget cursor or arrow cursor instead,
            //depending on construction order.
            QListIterator<WId> iter(QWidgetPrivate::mapper->uniqueKeys());
            while (iter.hasNext()) {
                CCoeControl *ctrl = iter.next();
                if(ctrl->OwnsWindow()) {
                    ctrl->DrawableWindow()->ClearPointerCursor();
                }
            }
            if (w)
                qt_symbian_setWindowCursor(w->cursor(), w->effectiveWinId());
            else
                qt_symbian_setWindowGroupCursor(Qt::ArrowCursor, S60->windowGroup());
        }
    }
}

#endif // QT_NO_CURSOR

QT_END_NAMESPACE