/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** 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, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $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 "qcombobox.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" #ifdef QT_GRAPHICSSYSTEM_RUNTIME #include "private/qgraphicssystem_runtime_p.h" #endif #include "private/qcursor_p.h" #include "apgwgnam.h" // For CApaWindowGroupName #include // For CMdaAudioToneUtility #if defined(Q_OS_SYMBIAN) # include # include # include "qs60mainappui.h" # include "qinputcontext.h" # include #endif #if defined(Q_WS_S60) # if !defined(QT_NO_IM) # include # endif #endif #include "private/qstylesheetstyle_p.h" #include #include #ifdef Q_SYMBIAN_TRANSITION_EFFECTS #include #endif #ifdef COE_GROUPED_POINTER_EVENT_VERSION #include #endif QT_BEGIN_NAMESPACE // Goom Events through Window Server static const int KGoomMemoryLowEvent = 0x10282DBF; static const int KGoomMemoryGoodEvent = 0x20026790; // Split view open/close events from AVKON static const int KSplitViewOpenEvent = 0x2001E2C0; static const int KSplitViewCloseEvent = 0x2001E2C1; #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; static Qt::KeyboardModifiers app_keyboardModifiers = Qt::NoModifier; QS60Data* qGlobalS60Data() { return qt_s60Data(); } #ifdef Q_WS_S60 void QS60Data::setStatusPaneAndButtonGroupVisibility(bool statusPaneVisible, bool buttonGroupVisible) { bool buttonGroupVisibilityChanged = false; if (CEikButtonGroupContainer *const b = buttonGroupContainer()) { buttonGroupVisibilityChanged = (b->IsVisible() != buttonGroupVisible); b->MakeVisible(buttonGroupVisible); } bool statusPaneVisibilityChanged = false; if (CEikStatusPane *const s = statusPane()) { statusPaneVisibilityChanged = (s->IsVisible() != statusPaneVisible); s->MakeVisible(statusPaneVisible); } if (buttonGroupVisibilityChanged || statusPaneVisibilityChanged) { const QSize size = qt_TRect2QRect(S60->clientRect()).size(); const QSize oldSize; // note that QDesktopWidget::resizeEvent ignores the QResizeEvent contents QResizeEvent event(size, oldSize); QApplication::instance()->sendEvent(QApplication::desktop(), &event); } if (buttonGroupVisibilityChanged && !statusPaneVisibilityChanged && QApplication::activeWindow()) // Ensure that control rectangle is updated static_cast(QApplication::activeWindow()->winId())->handleClientAreaChange(); } bool QS60Data::setRecursiveDecorationsVisibility(QWidget *window, Qt::WindowStates newState) { // Show statusbar: // Topmost parent: Show unless fullscreen/minimized. // Child windows: Follow topmost parent, unless fullscreen, in which case do not show statusbar // Show CBA: // Topmost parent: Show unless fullscreen/minimized. // Exception: Show if fullscreen with Qt::WindowSoftkeysVisibleHint. // Child windows: // Minimized: Unclear if there is an use case for having focused minimized window at all. // Always follow topmost parent just to be safe. // Maximized and normal: follow topmost parent. // Exception: If topmost parent is not showing CBA, show CBA if any softkey actions are // defined. // Fullscreen: Show only if Qt::WindowSoftkeysVisibleHint set. Qt::WindowStates comparisonState = newState; QWidget *parentWindow = window->parentWidget(); if (parentWindow) { while (parentWindow->parentWidget()) parentWindow = parentWindow->parentWidget(); comparisonState = parentWindow->windowState(); } else { parentWindow = window; } bool decorationsVisible = !(comparisonState & (Qt::WindowFullScreen | Qt::WindowMinimized)); const bool parentIsFullscreen = comparisonState & Qt::WindowFullScreen; const bool parentCbaVisibilityHint = parentWindow->windowFlags() & Qt::WindowSoftkeysVisibleHint; bool buttonGroupVisibility = (decorationsVisible || (parentIsFullscreen && parentCbaVisibilityHint)); // Do extra checking for child windows if (window->parentWidget()) { if (newState & Qt::WindowFullScreen) { decorationsVisible = false; if (window->windowFlags() & Qt::WindowSoftkeysVisibleHint) buttonGroupVisibility = true; else buttonGroupVisibility = false; } else if (!(newState & Qt::WindowMinimized) && !buttonGroupVisibility) { for (int i = 0; i < window->actions().size(); ++i) { if (window->actions().at(i)->softKeyRole() != QAction::NoSoftKey) { buttonGroupVisibility = true; break; } } } } S60->setStatusPaneAndButtonGroupVisibility(decorationsVisible, buttonGroupVisibility); return decorationsVisible; } #endif void QS60Data::createStatusPaneAndCBA() { CEikAppUi *ui = static_cast(S60->appUi()); MEikAppUiFactory *factory = CEikonEnv::Static()->AppUiFactory(); QT_TRAP_THROWING( factory->CreateResourceIndependentFurnitureL(ui); CEikButtonGroupContainer *cba = CEikButtonGroupContainer::NewL(CEikButtonGroupContainer::ECba, CEikButtonGroupContainer::EHorizontal, ui, R_AVKON_SOFTKEYS_EMPTY_WITH_IDS); CEikButtonGroupContainer *oldCba = factory->SwapButtonGroup(cba); Q_ASSERT(!oldCba); S60->setButtonGroupContainer(cba); CEikMenuBar *menuBar = new(ELeave) CEikMenuBar; menuBar->ConstructL(ui, 0, R_AVKON_MENUPANE_EMPTY); menuBar->SetMenuType(CEikMenuBar::EMenuOptions); S60->appUi()->AddToStackL(menuBar, ECoeStackPriorityMenu, ECoeStackFlagRefusesFocus); CEikMenuBar *oldMenu = factory->SwapMenuBar(menuBar); Q_ASSERT(!oldMenu); ) if (S60->statusPane()) { // Use QDesktopWidget as the status pane observer to proxy for the AppUi. // Can't use AppUi directly because it privately inherits from MEikStatusPaneObserver. QSymbianControl *desktopControl = static_cast(QApplication::desktop()->winId()); S60->statusPane()->SetObserver(desktopControl); } } void QS60Data::controlVisibilityChanged(CCoeControl *control, bool visible) { if (QWidgetPrivate::mapper && QWidgetPrivate::mapper->contains(control)) { QWidget *const widget = QWidgetPrivate::mapper->value(control); QWidget *const window = widget->window(); if (QTLWExtra *topData = qt_widget_private(window)->maybeTopData()) { QWidgetBackingStoreTracker &backingStore = topData->backingStore; if (visible) { QApplicationPrivate *d = QApplicationPrivate::instance(); d->emitAboutToUseGpuResources(); // (Re)create the backing store and force repaint if we have no // backing store already, or EGL surface cration failed on last attempt. if (backingStore.data() && !S60->eglSurfaceCreationError) { backingStore.registerWidget(widget); } else { S60->eglSurfaceCreationError = false; backingStore.create(window); backingStore.registerWidget(widget); qt_widget_private(widget)->invalidateBuffer(widget->rect()); widget->repaint(); if (S60->eglSurfaceCreationError) backingStore.unregisterWidget(widget); } } else { QApplicationPrivate *d = QApplicationPrivate::instance(); d->emitAboutToReleaseGpuResources(); // In certain special scenarios we may get an ENotVisible event // without a previous EPartiallyVisible. The backingstore must // still be destroyed, hence the registerWidget() call below. if (backingStore.data() && widget->internalWinId() && qt_widget_private(widget)->maybeBackingStore() == backingStore.data()) backingStore.registerWidget(widget); backingStore.unregisterWidget(widget); // In order to ensure that any resources used by the window surface // are immediately freed, we flush the WSERV command buffer. S60->wsSession().Flush(); } } } } TRect QS60Data::clientRect() { TRect r = static_cast(S60->appUi())->ClientRect(); if (S60->partialKeyboardOpen && !QApplication::testAttribute(Qt::AA_S60DontConstructApplicationPanes)) { // Adjust client rect when splitview is open // We want it to take the client rect space as if the splitview keyboard was not there TRect statusPaneRect; TRect mainRect; AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EStatusPane, statusPaneRect); AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EMainPane, mainRect); int clientAreaHeight = mainRect.Height(); CEikStatusPane *const s = S60->statusPane(); if (!(s && s->IsVisible())) clientAreaHeight += statusPaneRect.Height(); r.SetHeight(clientAreaHeight); } return r; } 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; }; static QS60Beep* qt_S60Beep = 0; QS60Beep::~QS60Beep() { if (iToneUtil) { switch (iState) { case EBeepPlaying: iToneUtil->CancelPlay(); break; case EBeepNotPrepared: iToneUtil->CancelPrepare(); break; } } 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 == EBeepPlaying) { iToneUtil->CancelPlay(); iState = EBeepPrepared; } iToneUtil->Play(); iState = EBeepPlaying; } void QS60Beep::MatoPrepareComplete(TInt aError) { if (aError == KErrNone) { iState = EBeepPrepared; Play(); } } void QS60Beep::MatoPlayComplete(TInt aError) { Q_UNUSED(aError); iState = EBeepPrepared; } 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_longTapDetector(0) , m_ignoreFocusChanged(0) , m_symbianPopupIsOpen(0) , m_inExternalScreenOverride(false) , m_lastStatusPaneVisibility(0) { } void QSymbianControl::ConstructL(bool isWindowOwning, bool desktop) { if (!desktop) { if (isWindowOwning || !qwidget->parentWidget() || qwidget->parentWidget()->windowType() == Qt::Desktop) { RWindowGroup &wg(S60->windowGroup(qwidget)); CreateWindowL(wg); } 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(); if (!qwidget->d_func()->isGLGlobalShareWidget) { SetFocusing(true); m_longTapDetector = QLongTapTimer::NewL(this); m_doubleClickTimer.invalidate(); DrawableWindow()->SetPointerGrab(ETrue); } } #ifdef Q_SYMBIAN_TRANSITION_EFFECTS if (OwnsWindow()) { TTfxWindowPurpose windowPurpose(ETfxPurposeNone); switch (qwidget->windowType()) { case Qt::Dialog: windowPurpose = ETfxPurposeDialogWindow; break; case Qt::Popup: windowPurpose = ETfxPurposePopupWindow; break; case Qt::Tool: windowPurpose = ETfxPurposeToolWindow; break; case Qt::ToolTip: windowPurpose = ETfxPurposeToolTipWindow; break; case Qt::SplashScreen: windowPurpose = ETfxPurposeSplashScreenWindow; break; default: windowPurpose = (isWindowOwning || !qwidget->parentWidget() || qwidget->parentWidget()->windowType() == Qt::Desktop) ? ETfxPurposeWindow : ETfxPurposeChildWindow; break; } Window().SetPurpose(windowPurpose); } #endif } QSymbianControl::~QSymbianControl() { if (!qwidget->d_func()->isGLGlobalShareWidget) { // GLGlobalShareWidget doesn't interact with scene // Ensure backing store is deleted before the top-level // window is destroyed QT_TRY { qt_widget_private(qwidget)->topData()->backingStore.destroy(); } QT_CATCH(const std::exception&) { // ignore exceptions, nothing can be done } if (S60->curWin == this) S60->curWin = 0; if (!QApplicationPrivate::is_app_closing) { QT_TRY { setFocusSafely(false); } QT_CATCH(const std::exception&) { // ignore exceptions, nothing can be done } } S60->appUi()->RemoveFromStack(this); delete m_longTapDetector; } } void QSymbianControl::setWidget(QWidget *w) { qwidget = w; } QPoint QSymbianControl::translatePointForFixedNativeOrientation(const TPoint &pointerEventPos, TTranslationType translationType) const { QPoint pos(pointerEventPos.iX, pointerEventPos.iY); if (qwidget->d_func()->fixNativeOrientationCalled) { QSize wsize = qwidget->size(); // always same as the size in the native orientation TSize size = Size(); // depends on the current orientation // pixel center translations, eg touch points, should be reflected against the last pixel center, which is at size-1 int offset = (translationType == ETranslatePixelCenter) ? 1 : 0; if (size.iWidth == wsize.height() && size.iHeight == wsize.width()) { qreal x = pos.x(); qreal y = pos.y(); if (S60->screenRotation == QS60Data::ScreenRotation90) { // DisplayRightUp pos.setX(size.iHeight - offset - y); pos.setY(x); } else if (S60->screenRotation == QS60Data::ScreenRotation270) { // DisplayLeftUp pos.setX(y); pos.setY(size.iWidth - offset - x); } } } return pos; } TRect QSymbianControl::translateRectForFixedNativeOrientation(const TRect &controlRect) const { TRect rect = controlRect; if (qwidget->d_func()->fixNativeOrientationCalled) { QPoint a = translatePointForFixedNativeOrientation(rect.iTl, ETranslatePixelEdge); QPoint b = translatePointForFixedNativeOrientation(rect.iBr, ETranslatePixelEdge); if (a.x() < b.x()) { rect.iTl.iX = a.x(); rect.iBr.iX = b.x(); } else { rect.iTl.iX = b.x(); rect.iBr.iX = a.x(); } if (a.y() < b.y()) { rect.iTl.iY = a.y(); rect.iBr.iY = b.y(); } else { rect.iTl.iY = b.y(); rect.iBr.iY = a.y(); } } return rect; } void QSymbianControl::HandleLongTapEventL( const TPoint& aPenEventLocation, const TPoint& aPenEventScreenLocation ) { QWidget *alienWidget; QPoint widgetPos = translatePointForFixedNativeOrientation(aPenEventLocation, ETranslatePixelCenter); QPoint globalPos = translatePointForFixedNativeOrientation(aPenEventScreenLocation, ETranslatePixelCenter); alienWidget = qwidget->childAt(widgetPos); if (!alienWidget) alienWidget = qwidget; #if !defined(QT_NO_CONTEXTMENU) QContextMenuEvent contextMenuEvent(QContextMenuEvent::Mouse, widgetPos, globalPos, Qt::NoModifier); qt_sendSpontaneousEvent(alienWidget, &contextMenuEvent); #endif } #ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER #ifdef COE_GROUPED_POINTER_EVENT_VERSION void QSymbianControl::translateMultiEventPointerEvent(const CCoeEventData &eventData ) { TUint count = eventData.Count(); QVector touches; touches.reserve(count); for (int i = 0; i < count; i++) { const TPointerEvent *pointerEvent = eventData.Pointer(i); const TAdvancedPointerEvent *advEvent = pointerEvent->AdvancedPointerEvent(); if (advEvent) touches.push_back(TouchEventFromAdvancedPointerEvent(advEvent)); } if (touches.size()) processTouchEvents(touches); } #endif void QSymbianControl::translateAdvancedPointerEvent(const TAdvancedPointerEvent *event) { processTouchEvents(QVector(1, TouchEventFromAdvancedPointerEvent(event))); } QSymbianControl::TouchEventParams QSymbianControl::TouchEventFromAdvancedPointerEvent(const TAdvancedPointerEvent *event) { QApplicationPrivate *d = QApplicationPrivate::instance(); QPointF screenPos = qwidget->mapToGlobal(translatePointForFixedNativeOrientation(event->iPosition, ETranslatePixelCenter)); qreal pressure; if (d->pressureSupported && event->Pressure() > 0) //workaround for misconfigured HAL pressure = event->Pressure() / qreal(d->maxTouchPressure); else pressure = qreal(1.0); return TouchEventParams(event->PointerNumber(), event->iType, screenPos, pressure); } #endif QSymbianControl::TouchEventParams::TouchEventParams() {} QSymbianControl::TouchEventParams::TouchEventParams(int pointerNumber, TPointerEvent::TType type, QPointF screenPos, qreal pressure) : pointerNumber(pointerNumber), type(type), screenPos(screenPos), pressure(pressure) {} void QSymbianControl::processTouchEvents(const QVector &touches) { QRect screenGeometry = qApp->desktop()->screenGeometry(qwidget); QApplicationPrivate *d = QApplicationPrivate::instance(); // get the maximum pointer number int numUpdates = touches.size(); int maxPointerNumber = 0; for (int i = 0; i < numUpdates; ++i) { const TouchEventParams &touch = touches[i]; maxPointerNumber = qMax(maxPointerNumber, touch.pointerNumber); } // ensure there are sufficient touch events in the list, // touch events will be indexed by pointerNumber QList points = d->appAllTouchPoints; while (points.count() <= maxPointerNumber) points.append(QTouchEvent::TouchPoint(points.count())); // first set all active touch points to stationary for (int i = 0; i < points.count(); ++i) { QTouchEvent::TouchPoint &touchPoint = points[i]; if (touchPoint.state() != Qt::TouchPointReleased) { touchPoint.setState(Qt::TouchPointStationary); } } // Add all info about moving or state changed touch points for (int i = 0; i < numUpdates; ++i) { const TouchEventParams &touch = touches[i]; QTouchEvent::TouchPoint &touchPoint = points[touch.pointerNumber]; Qt::TouchPointStates state; switch (touch.type) { case TPointerEvent::EButton1Down: #ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER case TPointerEvent::EEnterHighPressure: #endif state = Qt::TouchPointPressed; break; case TPointerEvent::EButton1Up: #ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER case TPointerEvent::EExitCloseProximity: #endif state = Qt::TouchPointReleased; break; case TPointerEvent::EDrag: state = Qt::TouchPointMoved; break; default: // how likely is this to happen? state = Qt::TouchPointStationary; break; } if (touch.pointerNumber == 0) state |= Qt::TouchPointPrimary; touchPoint.setState(state); touchPoint.setScreenPos(touch.screenPos); touchPoint.setNormalizedPos(QPointF(touch.screenPos.x() / screenGeometry.width(), touch.screenPos.y() / screenGeometry.height())); touchPoint.setPressure(touch.pressure); } // check the resulting state of all touch points Qt::TouchPointStates allStates = 0; for (int i = 0; i < points.count(); ++i) { QTouchEvent::TouchPoint &touchPoint = points[i]; allStates |= touchPoint.state(); } if ((allStates & Qt::TouchPointStateMask) == Qt::TouchPointReleased) { // all touch points released d->appAllTouchPoints.clear(); } else { d->appAllTouchPoints = points; } QApplicationPrivate::translateRawTouchEvent(qwidget, QTouchEvent::TouchScreen, points); } void QSymbianControl::HandlePointerEventL(const TPointerEvent& pEvent) { #ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER #ifdef COE_GROUPED_POINTER_EVENT_VERSION if (pEvent.iType == TPointerEvent::EDataCCoeEventData) { // only advanced pointers can be data type pointer events const TAdvancedPointerEvent *advEvent = pEvent.AdvancedPointerEvent(); if (!advEvent) return; const CCoeEventData& eventData = CCoeEventData::EventData(*advEvent); if (eventData.Type() == CWsEventWithData::EPointerEvent) { QT_TRYCATCH_LEAVING(translateMultiEventPointerEvent(eventData)); // pointer 0 events and unnumbered events should also be handled as mouse events for (int i=0; iAdvancedPointerEvent(); if (!advEvent || advEvent->PointerNumber() == 0) { if (m_longTapDetector) m_longTapDetector->PointerEventL(*pointerEvent); QT_TRYCATCH_LEAVING(HandlePointerEvent(*pointerEvent)); } } return; } } #endif 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 if (m_longTapDetector) m_longTapDetector->PointerEventL(pEvent); QT_TRYCATCH_LEAVING(HandlePointerEvent(pEvent)); } void QSymbianControl::HandlePointerEvent(const TPointerEvent& pEvent) { QMouseEvent::Type type; Qt::MouseButton button; mapS60MouseEventTypeToQt(&type, &button, &pEvent); Qt::KeyboardModifiers modifiers = mapToQtModifiers(pEvent.iModifiers); app_keyboardModifiers = modifiers; QPoint widgetPos = translatePointForFixedNativeOrientation(pEvent.iPosition, ETranslatePixelCenter); TPoint controlScreenPos = PositionRelativeToScreen(); QPoint globalPos = QPoint(controlScreenPos.iX, controlScreenPos.iY) + widgetPos; S60->lastCursorPos = globalPos; S60->lastPointerEventPos = widgetPos; QWidget *mouseGrabber = QWidget::mouseGrabber(); QWidget *popupWidget = qApp->activePopupWidget(); QWidget *popupReceiver = 0; if (popupWidget) { QWidget *popupChild = popupWidget->childAt(popupWidget->mapFromGlobal(globalPos)); popupReceiver = popupChild ? popupChild : popupWidget; } if (mouseGrabber) { if (popupReceiver) { sendMouseEvent(popupReceiver, type, globalPos, button, modifiers); } else { sendMouseEvent(mouseGrabber, type, globalPos, button, modifiers); } // No Enter/Leave events in grabbing mode. return; } QWidget *widgetUnderPointer = qwidget->childAt(widgetPos); if (!widgetUnderPointer) widgetUnderPointer = qwidget; QApplicationPrivate::dispatchEnterLeave(widgetUnderPointer, S60->lastPointerEventTarget); S60->lastPointerEventTarget = widgetUnderPointer; QWidget *receiver; if (!popupReceiver && S60->mousePressTarget && type != QEvent::MouseButtonPress) { receiver = S60->mousePressTarget; if (type == QEvent::MouseButtonRelease) S60->mousePressTarget = 0; } else { receiver = popupReceiver ? popupReceiver : widgetUnderPointer; if (type == QEvent::MouseButtonPress) S60->mousePressTarget = receiver; } #if !defined(QT_NO_CURSOR) && !defined(Q_SYMBIAN_FIXED_POINTER_CURSORS) if (S60->brokenPointerCursors) qt_symbian_move_cursor_sprite(); #endif //Generate single touch event for S60 5.0 (has touchscreen, does not have advanced pointers) #ifndef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER if (S60->hasTouchscreen) { processTouchEvents(QVector(1, TouchEventParams(0, pEvent.iType, QPointF(globalPos), 1.0))); } #endif sendMouseEvent(receiver, type, globalPos, button, modifiers); } #ifdef Q_WS_S60 void QSymbianControl::HandleStatusPaneSizeChange() { QS60MainAppUi *s60AppUi = static_cast(S60->appUi()); s60AppUi->HandleStatusPaneSizeChange(); } #endif void QSymbianControl::sendMouseEvent( QWidget *receiver, QEvent::Type type, const QPoint &globalPos, Qt::MouseButton button, Qt::KeyboardModifiers modifiers) { Q_ASSERT(receiver); QMouseEvent mEvent(type, receiver->mapFromGlobal(globalPos), globalPos, button, QApplicationPrivate::mouse_buttons, modifiers); QEventDispatcherS60 *dispatcher; // It is theoretically possible for someone to install a different event dispatcher. if ((dispatcher = qobject_cast(receiver->d_func()->threadData->eventDispatcher)) != 0) { if (dispatcher->excludeUserInputEvents()) { dispatcher->saveInputEvent(this, receiver, new QMouseEvent(mEvent)); return; } } sendMouseEvent(receiver, &mEvent); } 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) { /* S60 has a confusing way of delivering key events. There are three types of events: EEventKey, EEventKeyDown and EEventKeyUp. When a key is pressed, EEventKeyDown is first generated, followed by EEventKey. Then, when the key is released, EEventKeyUp is generated. However, it is possible that only the EEventKey is generated alone, typically in relation to virtual keyboards. In that case we need to take care to generate both press and release events in Qt, since applications expect that. We do this by having three states for each used scan code, depending on the events received. See the switch below for what happens in each state transition. */ if (type != EEventKeyDown) if (handleVirtualMouse(keyEvent, type) == EKeyWasConsumed) return EKeyWasConsumed; TKeyResponse ret = EKeyWasNotConsumed; #define GET_RETURN(x) (ret = ((x) == EKeyWasConsumed) ? EKeyWasConsumed : ret) // This top level switch corresponds to the states, and the inner switches // correspond to the transitions. QS60Data::ScanCodeState &scanCodeState = S60->scanCodeStates[keyEvent.iScanCode]; switch (scanCodeState) { case QS60Data::Unpressed: switch (type) { case EEventKeyDown: scanCodeState = QS60Data::KeyDown; break; case EEventKey: GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyPress)); GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyRelease)); break; case EEventKeyUp: // No action. break; } break; case QS60Data::KeyDown: switch (type) { case EEventKeyDown: // This should never happen, just stay in this state to be safe. break; case EEventKey: GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyPress)); scanCodeState = QS60Data::KeyDownAndKey; break; case EEventKeyUp: scanCodeState = QS60Data::Unpressed; break; } break; case QS60Data::KeyDownAndKey: switch (type) { case EEventKeyDown: // This should never happen, just stay in this state to be safe. break; case EEventKey: GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyRelease)); GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyPress)); break; case EEventKeyUp: GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyRelease)); scanCodeState = QS60Data::Unpressed; break; } break; } return ret; #undef GET_RETURN } TKeyResponse QSymbianControl::sendSymbianKeyEvent(const TKeyEvent &keyEvent, QEvent::Type type) { // Because S60 does not generate keysyms for EKeyEventDown and EKeyEventUp // events, we need to cache the keysyms from the EKeyEvent events. This is what // resolveS60ScanCode does. TUint s60Keysym = QApplicationPrivate::resolveS60ScanCode(keyEvent.iScanCode, keyEvent.iCode); int keyCode; if (s60Keysym == EKeyNull){ //some key events have 0 in iCode, for them iScanCode should be used keyCode = qt_keymapper_private()->mapS60ScanCodesToQt(keyEvent.iScanCode); } else if ((s60Keysym >= 0x20 && s60Keysym < ENonCharacterKeyBase) || s60Keysym >= (ENonCharacterKeyBase + ENonCharacterKeyCount)) { // Normal characters keys. keyCode = s60Keysym; } else { // Special S60 keys. keyCode = qt_keymapper_private()->mapS60KeyToQt(s60Keysym); } Qt::KeyboardModifiers mods = mapToQtModifiers(keyEvent.iModifiers); TInt code = keyEvent.iCode; if (mods == Qt::ControlModifier) { //only support ctrl+a .. ctrl+z, 0x40 is the key value before Qt::Key_A if (code > 0 && code < 27) keyCode = 0x40 + code; } QKeyEventEx qKeyEvent(type, keyCode, mods, qt_keymapper_private()->translateKeyEvent(keyCode, mods), (keyEvent.iRepeats != 0), 1, keyEvent.iScanCode, s60Keysym, keyEvent.iModifiers); 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(widget->d_func()->threadData->eventDispatcher)) != 0) { if (dispatcher->excludeUserInputEvents()) { dispatcher->saveInputEvent(this, widget, new QKeyEventEx(qKeyEvent)); return EKeyWasConsumed; } } return sendKeyEvent(widget, &qKeyEvent); } TKeyResponse QSymbianControl::handleVirtualMouse(const TKeyEvent& keyEvent,TEventCode type) { #ifndef QT_NO_CURSOR if (S60->mouseInteractionEnabled && S60->virtualMouseRequired) { //translate keys to pointer if ((keyEvent.iScanCode >= EStdKeyLeftArrow && keyEvent.iScanCode <= EStdKeyDownArrow) || (keyEvent.iScanCode >= EStdKeyDevice10 && keyEvent.iScanCode <= EStdKeyDevice13) || keyEvent.iScanCode == EStdKeyDevice3) { QPoint pos = QCursor::pos(); TPointerEvent fakeEvent; fakeEvent.iType = (TPointerEvent::TType)(-1); fakeEvent.iModifiers = keyEvent.iModifiers; TInt x = pos.x(); TInt y = pos.y(); if (type == EEventKeyUp) { S60->virtualMouseAccelTimeout.start(); switch (keyEvent.iScanCode) { case EStdKeyLeftArrow: S60->virtualMousePressedKeys &= ~QS60Data::Left; break; case EStdKeyRightArrow: S60->virtualMousePressedKeys &= ~QS60Data::Right; break; case EStdKeyUpArrow: S60->virtualMousePressedKeys &= ~QS60Data::Up; break; case EStdKeyDownArrow: S60->virtualMousePressedKeys &= ~QS60Data::Down; break; // diagonal keys (named aliases don't exist in 3.1 SDK) case EStdKeyDevice10: S60->virtualMousePressedKeys &= ~QS60Data::LeftUp; break; case EStdKeyDevice11: S60->virtualMousePressedKeys &= ~QS60Data::RightUp; break; case EStdKeyDevice12: S60->virtualMousePressedKeys &= ~QS60Data::RightDown; break; case EStdKeyDevice13: S60->virtualMousePressedKeys &= ~QS60Data::LeftDown; break; case EStdKeyDevice3: //select if (S60->virtualMousePressedKeys & QS60Data::Select) fakeEvent.iType = TPointerEvent::EButton1Up; S60->virtualMousePressedKeys &= ~QS60Data::Select; break; } } else if (type == EEventKey) { int dx = 0; int dy = 0; if (keyEvent.iScanCode != EStdKeyDevice3) { m_doubleClickTimer.invalidate(); //reset mouse accelleration after a short time with no moves const int maxTimeBetweenKeyEventsMs = 500; if (S60->virtualMouseAccelTimeout.isValid() && S60->virtualMouseAccelTimeout.hasExpired(maxTimeBetweenKeyEventsMs)) { S60->virtualMouseAccelDX = 0; S60->virtualMouseAccelDY = 0; } S60->virtualMouseAccelTimeout.invalidate(); } switch (keyEvent.iScanCode) { case EStdKeyLeftArrow: S60->virtualMousePressedKeys |= QS60Data::Left; dx = -1; fakeEvent.iType = TPointerEvent::EMove; break; case EStdKeyRightArrow: S60->virtualMousePressedKeys |= QS60Data::Right; dx = 1; fakeEvent.iType = TPointerEvent::EMove; break; case EStdKeyUpArrow: S60->virtualMousePressedKeys |= QS60Data::Up; dy = -1; fakeEvent.iType = TPointerEvent::EMove; break; case EStdKeyDownArrow: S60->virtualMousePressedKeys |= QS60Data::Down; dy = 1; fakeEvent.iType = TPointerEvent::EMove; break; case EStdKeyDevice10: S60->virtualMousePressedKeys |= QS60Data::LeftUp; dx = -1; dy = -1; fakeEvent.iType = TPointerEvent::EMove; break; case EStdKeyDevice11: S60->virtualMousePressedKeys |= QS60Data::RightUp; dx = 1; dy = -1; fakeEvent.iType = TPointerEvent::EMove; break; case EStdKeyDevice12: S60->virtualMousePressedKeys |= QS60Data::RightDown; dx = 1; dy = 1; fakeEvent.iType = TPointerEvent::EMove; break; case EStdKeyDevice13: S60->virtualMousePressedKeys |= QS60Data::LeftDown; dx = -1; dy = 1; fakeEvent.iType = TPointerEvent::EMove; break; case EStdKeyDevice3: // Platform bug. If you start pressing several keys simultaneously (for // example for drag'n'drop), Symbian starts producing spurious up and // down messages for some keys. Therefore, make sure we have a clean slate // of pressed keys before starting a new button press. if (S60->virtualMousePressedKeys & QS60Data::Select) { return EKeyWasConsumed; } else { S60->virtualMousePressedKeys |= QS60Data::Select; fakeEvent.iType = TPointerEvent::EButton1Down; if (m_doubleClickTimer.isValid() && !m_doubleClickTimer.hasExpired(QApplication::doubleClickInterval())) { fakeEvent.iModifiers |= EModifierDoubleClick; m_doubleClickTimer.invalidate(); } else { m_doubleClickTimer.start(); } } break; } if (dx) { int cdx = S60->virtualMouseAccelDX; //reset accel on change of sign, else double accel if (dx * cdx <= 0) cdx = dx; else cdx *= 4; //cap accelleration if (dx * cdx > S60->virtualMouseMaxAccel) cdx = dx * S60->virtualMouseMaxAccel; //move mouse position x += cdx; S60->virtualMouseAccelDX = cdx; } if (dy) { int cdy = S60->virtualMouseAccelDY; if (dy * cdy <= 0) cdy = dy; else cdy *= 4; if (dy * cdy > S60->virtualMouseMaxAccel) cdy = dy * S60->virtualMouseMaxAccel; y += cdy; S60->virtualMouseAccelDY = cdy; } } //clip to screen size (window server allows a sprite hotspot to be outside the screen) int screenNumber = S60->screenNumberForWidget(qwidget); if (x < 0) x = 0; else if (x >= S60->screenWidthInPixelsForScreen[screenNumber]) x = S60->screenWidthInPixelsForScreen[screenNumber] - 1; if (y < 0) y = 0; else if (y >= S60->screenHeightInPixelsForScreen[screenNumber]) y = S60->screenHeightInPixelsForScreen[screenNumber] - 1; TPoint epos(x, y); TPoint cpos = epos - PositionRelativeToScreen(); fakeEvent.iPosition = cpos; fakeEvent.iParentPosition = epos; if(fakeEvent.iType != -1) HandlePointerEvent(fakeEvent); return EKeyWasConsumed; } } #endif return EKeyWasNotConsumed; } void QSymbianControl::sendInputEvent(QWidget *widget, QInputEvent *inputEvent) { switch (inputEvent->type()) { case QEvent::KeyPress: case QEvent::KeyRelease: sendKeyEvent(widget, static_cast(inputEvent)); break; case QEvent::MouseButtonDblClick: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseMove: sendMouseEvent(widget, static_cast(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_OS_SYMBIAN) 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(w->inputContext()))) { return ic->inputCapabilities(); } else { return TCoeInputCapabilities(TCoeInputCapabilities::ENone, 0, 0); } } #endif void QSymbianControl::Draw(const TRect& aRect) const { int leaveCode = 0; int exceptionCode = 0; // Implementation of CCoeControl::Draw() must never leave or throw exception. // In native Symbian code this is considered a fatal error, and it causes // process termination. TRAP(leaveCode, QT_TRYCATCH_ERROR(exceptionCode, doDraw(aRect))); if (leaveCode) qWarning() << "QSymbianControl::doDraw leaved with code " << leaveCode; else if (exceptionCode) qWarning() << "QSymbianControl::doDraw threw exception with code " << exceptionCode; } void QSymbianControl::doDraw(const TRect& controlRect) const { // Bail out immediately, if we don't have a drawing surface. Surface is attempted to be recreated // when this application becomes visible for the next time. if (S60->eglSurfaceCreationError) { qWarning() << "QSymbianControl::doDraw: EGL surface creation has failed, abort"; return; } // Set flag to avoid calling DrawNow in window surface QWidget *window = qwidget->window(); Q_ASSERT(window); QTLWExtra *topExtra = window->d_func()->maybeTopData(); Q_ASSERT(topExtra); TRect wcontrolRect = translateRectForFixedNativeOrientation(controlRect); if (!topExtra->inExpose) { topExtra->inExpose = true; if (!qwidget->isWindow()) { // If we get here, then it means we have a native child window // Since no content should ever be painted to these windows, we // erase them with a transparent brush when they get an expose. CWindowGc &gc = SystemGc(); gc.SetBrushColor(TRgb(0, 0, 0, 0)); gc.Clear(controlRect); } QRect exposeRect = qt_TRect2QRect(wcontrolRect); qwidget->d_func()->syncBackingStore(exposeRect); topExtra->inExpose = false; } QWindowSurface *surface = qwidget->windowSurface(); QPaintEngine *engine = surface ? surface->paintDevice()->paintEngine() : NULL; if (!engine) return; const bool sendNativePaintEvents = qwidget->d_func()->extraData()->receiveNativePaintEvents; if (sendNativePaintEvents) { const QRect r = qt_TRect2QRect(wcontrolRect); QMetaObject::invokeMethod(qwidget, "beginNativePaintEvent", Qt::DirectConnection, Q_ARG(QRect, r)); } // 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; #ifdef QT_GRAPHICSSYSTEM_RUNTIME if (QApplicationPrivate::runtime_graphics_system) { QRuntimeWindowSurface *rtSurface = static_cast(qwidget->windowSurface()); s60Surface = static_cast(rtSurface->m_windowSurface.data()); } else #endif s60Surface = static_cast(qwidget->windowSurface()); CFbsBitmap *bitmap = s60Surface->symbianBitmap(); CWindowGc &gc = SystemGc(); QWExtra::NativePaintMode nativePaintMode = qwidget->d_func()->extraData()->nativePaintMode; if(qwidget->d_func()->paintOnScreen()) nativePaintMode = QWExtra::Disable; switch(nativePaintMode) { case QWExtra::Disable: // Do nothing break; case QWExtra::Blit: case QWExtra::BlitWriteAlpha: if (qwidget->d_func()->isOpaque || nativePaintMode == QWExtra::BlitWriteAlpha) gc.SetDrawMode(CGraphicsContext::EDrawModeWriteAlpha); gc.BitBlt(controlRect.iTl, bitmap, backingStoreRect); break; case QWExtra::ZeroFill: if (Window().DisplayMode() == EColor16MA || Window().DisplayMode() == Q_SYMBIAN_ECOLOR16MAP) { gc.SetBrushStyle(CGraphicsContext::ESolidBrush); gc.SetDrawMode(CGraphicsContext::EDrawModeWriteAlpha); gc.SetBrushColor(TRgb::Color16MA(0)); gc.Clear(controlRect); } else { gc.SetBrushColor(TRgb(0x000000)); gc.Clear(controlRect); }; break; default: Q_ASSERT(false); } } if (sendNativePaintEvents) { const QRect r = qt_TRect2QRect(wcontrolRect); // The draw ops aren't actually sent to WSERV until the graphics // context is deactivated, which happens in the function calling // this one. We therefore delay the delivery of endNativePaintEvent, // to ensure that drawing has completed by the time the widget // receives the event. Note that, if the widget needs to ensure // that the draw ops have actually been executed into the output // framebuffer, a call to RWsSession::Flush is required in the // endNativePaintEvent implementation. QMetaObject::invokeMethod(qwidget, "endNativePaintEvent", Qt::QueuedConnection, Q_ARG(QRect, r)); } } void QSymbianControl::qwidgetResize_helper(const QSize &newSize) { QRect cr = qwidget->geometry(); QSize oldSize(cr.size()); 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; } else { if (!qwidget->testAttribute(Qt::WA_PendingResizeEvent)) { QResizeEvent *e = new QResizeEvent(newSize, oldSize); QApplication::postEvent(qwidget, e); } } } void QSymbianControl::SizeChanged() { CCoeControl::SizeChanged(); // When FixNativeOrientation had been called, the RWindow/CCoeControl size // and the surface/QWidget size have nothing to do with each other. if (qwidget->d_func()->fixNativeOrientationCalled) return; QSize oldSize = qwidget->size(); QSize newSize(Size().iWidth, Size().iHeight); if (oldSize != newSize) { // Enforce the proper size for fullscreen widgets on the secondary screen. const bool isFullscreen = qwidget->windowState() & Qt::WindowFullScreen; const int screenNumber = S60->screenNumberForWidget(qwidget); if (!m_inExternalScreenOverride && isFullscreen && screenNumber > 0) { int screenWidth = S60->screenWidthInPixelsForScreen[screenNumber]; int screenHeight = S60->screenHeightInPixelsForScreen[screenNumber]; TSize screenSize(screenWidth, screenHeight); if (screenWidth > 0 && screenHeight > 0 && screenSize != Size()) { m_inExternalScreenOverride = true; SetExtent(TPoint(0, 0), screenSize); return; } } qwidgetResize_helper(newSize); } m_inExternalScreenOverride = false; // CCoeControl::SetExtent calls SizeChanged, but does not call // PositionChanged, so we call it here to ensure that the widget's // position is updated. PositionChanged(); } 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 && (qwidget->windowState() & (~Qt::WindowActive)) == Qt::WindowNoState) top->normalGeometry.moveTopLeft(newPos); if (qwidget->isVisible()) { QMoveEvent e(newPos, oldPos); qt_sendSpontaneousEvent(qwidget, &e); } else { QMoveEvent * e = new QMoveEvent(newPos, oldPos); QApplication::postEvent(qwidget, e); } } } // Search recursively if there is a child widget that is both visible and focused. bool QSymbianControl::hasFocusedAndVisibleChild(QWidget *parentWidget) { for (int i = 0; i < parentWidget->children().size(); ++i) { QObject *object = parentWidget->children().at(i); if (object && object->isWidgetType()) { QWidget *w = static_cast(object); WId winId = w->internalWinId(); if (winId && winId->IsFocused() && winId->IsVisible()) return true; if (hasFocusedAndVisibleChild(w)) return true; } } return false; } void QSymbianControl::FocusChanged(TDrawNow /* aDrawNow */) { QT_TRY { if (m_ignoreFocusChanged || (qwidget->windowType() & Qt::WindowType_Mask) == Qt::Desktop) return; // just in case if (qwidget->d_func()->isGLGlobalShareWidget) return; #ifdef Q_WS_S60 if (S60->splitViewLastWidget) return; #endif // Popups never get focused, but still receive the FocusChanged when they are hidden. if (QApplicationPrivate::popupWidgets != 0 || (qwidget->windowType() & Qt::Popup) == Qt::Popup) return; QWidget *parentWindow = qwidget->window(); if (IsFocused() && IsVisible()) { if (m_symbianPopupIsOpen) { QWidget *fw = QApplication::focusWidget(); if (fw) { QFocusEvent event(QEvent::FocusIn, Qt::PopupFocusReason); QCoreApplication::sendEvent(fw, &event); } m_symbianPopupIsOpen = false; } QApplication::setActiveWindow(qwidget->window()); qwidget->d_func()->setWindowIcon_sys(true); qwidget->d_func()->setWindowTitle_sys(qwidget->windowTitle()); #ifdef Q_WS_S60 if (parentWindow->isWindow()) S60->setRecursiveDecorationsVisibility(parentWindow, parentWindow->windowState()); #endif } else { if (QApplication::activeWindow() == parentWindow && !hasFocusedAndVisibleChild(parentWindow)) { if (CCoeEnv::Static()->AppUi()->IsDisplayingMenuOrDialog() || S60->menuBeingConstructed) { QWidget *fw = QApplication::focusWidget(); if (fw) { QFocusEvent event(QEvent::FocusOut, Qt::PopupFocusReason); QCoreApplication::sendEvent(fw, &event); } m_symbianPopupIsOpen = true; return; } QApplication::setActiveWindow(0); } } // else { We don't touch the active window unless we were explicitly activated or deactivated } } QT_CATCH(const std::exception&) { // ignore errors } } void QSymbianControl::handleClientAreaChange() { const bool cbaVisibilityHint = qwidget->windowFlags() & Qt::WindowSoftkeysVisibleHint; if (qwidget->isFullScreen() && !cbaVisibilityHint) { SetExtentToWholeScreen(); } else if (qwidget->isMaximized() || (qwidget->isFullScreen() && cbaVisibilityHint)) { // Note that if there is S60->splitViewLastWidget, it means the resizing is done // by input context handling and we can use just default ClientRect. TRect r = (!S60->splitViewLastWidget) ? S60->clientRect() : static_cast(S60->appUi())->ClientRect(); SetExtent(r.iTl, r.Size()); } else if (!qwidget->isMinimized()) { // Normal geometry if (!qwidget->testAttribute(Qt::WA_Resized)) { qwidget->adjustSize(); qwidget->setAttribute(Qt::WA_Resized, false); //not a user resize } if (!qwidget->testAttribute(Qt::WA_Moved) && qwidget->windowType() != Qt::Dialog) { TRect r = S60->clientRect(); SetPosition(r.iTl); qwidget->setAttribute(Qt::WA_Moved, false); // not really an explicit position } } } bool QSymbianControl::isSplitViewWidget(QWidget *widget) { bool returnValue = true; // Ignore events sent to non-active windows, not visible widgets and not parents of input widget // and non-Qt dialogs. if (!qwidget->isActiveWindow() || !qwidget->isVisible() || !qwidget->isAncestorOf(widget) || CCoeEnv::Static()->AppUi()->IsDisplayingMenuOrDialog()) { returnValue = false; } return returnValue; } void QSymbianControl::HandleResourceChange(int resourceType) { switch (resourceType) { case KSplitViewCloseEvent: //intentional fall-through case KSplitViewOpenEvent: { #if !defined(QT_NO_IM) && defined(Q_WS_S60) //Fetch widget getting the text input QWidget *widget = QWidget::keyboardGrabber(); if (!widget) { if (QApplicationPrivate::popupWidgets) { widget = QApplication::activePopupWidget()->focusWidget(); if (!widget) { widget = QApplication::activePopupWidget(); } } else { widget = QApplicationPrivate::focus_widget; if (!widget) { widget = qwidget; } } } if (widget) { QCoeFepInputContext *ic = qobject_cast(widget->inputContext()); if (!ic) { ic = qobject_cast(qApp->inputContext()); } if (ic) { if (resourceType == KSplitViewCloseEvent) { S60->partialKeyboardOpen = false; ic->resetSplitViewWidget(); } else { S60->partialKeyboardOpen = true; if (isSplitViewWidget(widget)) ic->ensureFocusWidgetVisible(widget); } } } #endif // !defined(QT_NO_IM) && defined(Q_WS_S60) } break; case KInternalStatusPaneChange: // When status pane is not visible, only handle client area change if status pane was // previously visible, as size changes to hidden status pane should not affect // client area. if (S60->statusPane() && (S60->statusPane()->IsVisible() || m_lastStatusPaneVisibility)) { m_lastStatusPaneVisibility = S60->statusPane()->IsVisible(); if (S60->handleStatusPaneResizeNotifications) handleClientAreaChange(); } if (IsFocused() && IsVisible()) { qwidget->d_func()->setWindowIcon_sys(true); qwidget->d_func()->setWindowTitle_sys(qwidget->windowTitle()); } break; case KUidValueCoeFontChangeEvent: // font change event break; #ifdef Q_WS_S60 case KEikDynamicLayoutVariantSwitch: { #ifdef QT_SOFTKEYS_ENABLED // Update needed just in case softkeys contain icons QSoftKeyManager::updateSoftKeys(); #endif handleClientAreaChange(); // Send resize event to trigger desktopwidget workAreaResized signal if (qt_desktopWidget) { QResizeEvent e(qt_desktopWidget->size(), qt_desktopWidget->size()); QApplication::sendEvent(qt_desktopWidget, &e); } // Send resize event to dialogs so they can adjust their position if necessary. if (qwidget->windowType() & Qt::Dialog) { QResizeEvent e(qwidget->size(), qwidget->size()); QApplication::sendEvent(qwidget, &e); } break; } #endif default: break; } CCoeControl::HandleResourceChange(resourceType); } void QSymbianControl::CancelLongTapTimer() { if (m_longTapDetector) 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) { if (qwidget->d_func()->isGLGlobalShareWidget) return; // 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()). // Close any popups. CEikonEnv::Static()->EikAppUi()->StopDisplayingMenuBar(); 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); } } bool QSymbianControl::isControlActive() { return IsActivated() ? true : false; } void QSymbianControl::ensureFixNativeOrientation() { #if defined(Q_SYMBIAN_SUPPORTS_FIXNATIVEORIENTATION) if (!qwidget->isWindow() || qwidget->windowType() == Qt::Desktop) return; if (S60->screenNumberForWidget(qwidget) > 0) return; const bool isFixed = qwidget->d_func()->fixNativeOrientationCalled; const bool isFixEnabled = qwidget->testAttribute(Qt::WA_SymbianNoSystemRotation); const bool isFullScreen = qwidget->windowState().testFlag(Qt::WindowFullScreen); if (isFullScreen && isFixEnabled) { const bool surfaceBasedGs = QApplicationPrivate::graphics_system_name == QLatin1String("openvg") || QApplicationPrivate::graphics_system_name == QLatin1String("opengl"); if (!surfaceBasedGs) qwidget->setAttribute(Qt::WA_SymbianNoSystemRotation, false); if (!isFixed && surfaceBasedGs) { if (Window().FixNativeOrientation() == KErrNone) { qwidget->d_func()->fixNativeOrientationCalled = true; // The EGL window surface is now fixed to the native orientation // of the device, no matter what size we pass when creating it. // Enforce the same size for the QWidget too. For the underlying // CCoeControl and RWindow it is up to the system to resize them // when the standard auto-rotation mechanism is in use, we must not // change that behavior by forcing any size for those. In practice // this means that the QWidget and the underlying native control // dimensions will be out of sync when FixNativeOrientation was // called and the device is turned to the non-native (typically // landscape) orientation. The pointer event handling and certain // functions like Draw() will need to compensate for this. QSize newSize(S60->nativeScreenWidthInPixels, S60->nativeScreenHeightInPixels); if (qwidget->size() != newSize) qwidgetResize_helper(newSize); } else { qwidget->setAttribute(Qt::WA_SymbianNoSystemRotation, false); } } } else if (isFixed) { qwidget->setAttribute(Qt::WA_SymbianNoSystemRotation, false); qwidget->d_func()->fixNativeOrientationCalled = false; qwidget->hide(); qwidget->d_func()->create_sys(0, false, true); qwidget->show(); } #else qwidget->setAttribute(Qt::WA_SymbianNoSystemRotation, false); #endif } /*! \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, 0x040000)) { 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, _internal)) { 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 initialized. We need to do it. TApaApplicationFactory factory(S60->s60ApplicationFactory ? S60->s60ApplicationFactory : newS60Application); CApaCommandLine* commandLine = q_check_ptr(QCoreApplicationPrivate::symbianCommandLine()); if (commandLine) { // After this construction, CEikonEnv will be available from CEikonEnv::Static(). // (much like our qApp). CEikonEnv* coe = new CEikonEnv; //not using QT_TRAP_THROWING, because coe owns the cleanupstack so it can't be pushed there. TRAPD(err, coe->ConstructAppFromCommandLineL(factory, *commandLine)); if(err != KErrNone) { qWarning() << "qt_init: Eikon application construct failed (" << err << "), maybe missing resource file on S60 3.1?"; delete coe; qt_symbian_throwIfError(err); } } 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); #ifdef Q_SYMBIAN_WINDOW_SIZE_CACHE TRAP_IGNORE(S60->wsSession().EnableWindowSizeCacheL()); #endif 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(); #ifdef QT_SYMBIAN_HAVE_AKNTRANSEFFECT_H // Notify uiaccelerator, that we are qt application. This info is used for // decision making how startup effects are shown. GfxTransEffect::BeginFullScreen(AknTransEffect::ENone, TRect(0, 0, 0, 0), AknTransEffect::EParameterAvkonInternal, AknTransEffect::GfxTransParam(S60->uid, KQtAppExitFlag)); #endif // 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; } S60->avkonComponentsSupportTransparency = false; S60->menuBeingConstructed = false; #ifdef Q_WS_S60 TUid KCRUidAvkon = { 0x101F876E }; TUint32 KAknAvkonTransparencyEnabled = 0x0000000D; CRepository* repository = 0; TRAP(err, repository = CRepository::NewL(KCRUidAvkon)); if(err == KErrNone) { TInt value = 0; err = repository->Get(KAknAvkonTransparencyEnabled, value); if(err == KErrNone) { S60->avkonComponentsSupportTransparency = (value==1) ? true : false; } } delete repository; repository = 0; #endif qt_keymapper_private()->updateInputLanguage(); #ifdef QT_KEYPAD_NAVIGATION if (touch) { QApplicationPrivate::navigationMode = Qt::NavigationModeNone; } else { QApplicationPrivate::navigationMode = Qt::NavigationModeKeypadDirectional; } #endif #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); QObject::connect(qApp, SIGNAL(aboutToQuit()), qApp, SLOT(_q_aboutToQuit())); #ifdef Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE QApplicationPrivate::instance()->useTranslucentEGLSurfaces = true; if (QSymbianGraphicsSystemEx::hasBCM2727()) { // We have only 32MB GPU memory. Use raster surfaces // for transparent TLWs. QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false; } if (QApplicationPrivate::graphics_system_name == QLatin1String("raster")) QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false; #else QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false; #endif /* ### 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("WId"); } #ifdef QT_NO_FREETYPE extern void qt_cleanup_symbianFontDatabase(); // qfontdatabase_s60.cpp #endif /***************************************************************************** qt_cleanup() - cleans up when the application is finished *****************************************************************************/ void qt_cleanup() { if(qt_S60Beep) { delete qt_S60Beep; qt_S60Beep = 0; } QFontCache::cleanup(); // Has to happen now, since QFontEngineS60 has FBS handles QPixmapCache::clear(); // Has to happen now, since QSymbianRasterPixmapData has FBS handles #ifdef QT_NO_FREETYPE qt_cleanup_symbianFontDatabase(); #endif // 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); #ifdef Q_WS_S60 // Clear CBA CEikonEnv::Static()->AppUiFactory()->SwapButtonGroup(0); delete S60->buttonGroupContainer(); S60->setButtonGroupContainer(0); #endif // Call EndFullScreen() to prevent confusing the system effect state machine. qt_endFullScreenEffect(); #ifndef QT_NO_CURSOR QCursorData::cleanup(); #endif 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) { #ifdef Q_SYMBIAN_TRANSITION_EFFECTS S60->wsSession().SendEffectCommand(ETfxCmdAppModalModeEnter); #endif if (widget) { static_cast(widget->effectiveWinId())->FadeBehindPopup(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) { #ifdef Q_SYMBIAN_TRANSITION_EFFECTS S60->wsSession().SendEffectCommand(ETfxCmdAppModalModeExit); #endif if (widget) { static_cast(widget->effectiveWinId())->FadeBehindPopup(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 (popup && qobject_cast(popup->parentWidget())) static_cast(popup->effectiveWinId())->FadeBehindPopup(ETrue); if (!QApplicationPrivate::popupWidgets) QApplicationPrivate::popupWidgets = new QWidgetList; QApplicationPrivate::popupWidgets->append(popup); // Cancel focus widget pointer capture and long tap timer if (QApplication::focusWidget() && QApplication::focusWidget()->effectiveWinId()) { static_cast(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(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 (popup && qobject_cast(popup->parentWidget())) static_cast(popup->effectiveWinId())->FadeBehindPopup(EFalse); 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(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->window()->isModal()) // restore pointer capture for modal window fw->effectiveWinId()->SetPointerCapture(true); 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() { if (!qt_S60Beep) { TInt frequency = 880; TTimeIntervalMicroSeconds duration(500000); TRAP_IGNORE(qt_S60Beep=QS60Beep::NewL(frequency, duration)); } if (qt_S60Beep) qt_S60Beep->Play(); } static inline bool callSymbianEventFilters(const QSymbianEvent *event) { long unused; return qApp->filterEvent(const_cast(event), &unused); } /*! \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); QT_TRY { if (d->eventDispatcher->filterEvent(const_cast(event))) return 1; 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; } QT_CATCH(const std::exception& ex) { // don't allow an exception to stop exit command handling if (event->type() != QSymbianEvent::CommandEvent || event->command() != EEikCmdExit) QT_RETHROW; } switch (event->type()) { case QSymbianEvent::WindowServerEvent: return d->symbianProcessWsEvent(event); case QSymbianEvent::CommandEvent: return d->symbianHandleCommand(event); case QSymbianEvent::ResourceChangeEvent: return d->symbianResourceChange(event); default: return -1; } } int QApplicationPrivate::symbianProcessWsEvent(const QSymbianEvent *symbianEvent) { // Qt event handling. Handle some events regardless of if the handle is in our // widget map or not. const TWsEvent *event = symbianEvent->windowServerEvent(); CCoeControl* control = reinterpret_cast(event->Handle()); const bool controlInMap = QWidgetPrivate::mapper && QWidgetPrivate::mapper->contains(control); switch (event->Type()) { case EEventPointerEnter: if (controlInMap) { callSymbianEventFilters(symbianEvent); return 1; // Qt::Enter will be generated in HandlePointerL } break; case EEventPointerExit: if (controlInMap) { if (callSymbianEventFilters(symbianEvent)) return 1; 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: // fallthrough #if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) case EEventDisplayChanged: #endif { if (callSymbianEventFilters(symbianEvent)) return 1; 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); } // Close non-native QMenus (that should act like context menus, i.e. close // automatically when the orientation changes). QMenu *activeMenu = qobject_cast(QApplication::activePopupWidget()); if (activeMenu) activeMenu->close(); } return 0; // Propagate to CONE case EEventWindowVisibilityChanged: if (controlInMap) { if (callSymbianEventFilters(symbianEvent)) return 1; const TWsVisibilityChangedEvent *visChangedEvent = event->VisibilityChanged(); if (visChangedEvent->iFlags & TWsVisibilityChangedEvent::ENotVisible) S60->controlVisibilityChanged(control, false); else if (visChangedEvent->iFlags & TWsVisibilityChangedEvent::EPartiallyVisible) S60->controlVisibilityChanged(control, true); return 1; } break; case EEventFocusGained: if (callSymbianEventFilters(symbianEvent)) return 1; #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 #ifdef QT_SOFTKEYS_ENABLED if (!CEikonEnv::Static()->EikAppUi()->IsDisplayingMenuOrDialog()) QSoftKeyManager::updateSoftKeys(); #endif break; case EEventFocusLost: if (callSymbianEventFilters(symbianEvent)) return 1; #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; case KGoomMemoryLowEvent: #ifdef QT_DEBUG qDebug() << "QApplicationPrivate::symbianProcessWsEvent - KGoomMemoryLowEvent"; #endif if (callSymbianEventFilters(symbianEvent)) return 1; #ifdef QT_GRAPHICSSYSTEM_RUNTIME if(QApplicationPrivate::runtime_graphics_system) { bool switchToSwRendering(false); foreach (QWidget *w, QApplication::topLevelWidgets()) { if(w->d_func()->topData()->backingStore) { switchToSwRendering = true; break; } } if (switchToSwRendering) { QRuntimeGraphicsSystem *gs = static_cast(QApplicationPrivate::graphics_system); gs->setGraphicsSystem(QLatin1String("raster")); } } #endif break; case KGoomMemoryGoodEvent: #ifdef QT_DEBUG qDebug() << "QApplicationPrivate::symbianProcessWsEvent - KGoomMemoryGoodEvent"; #endif if (callSymbianEventFilters(symbianEvent)) return 1; #ifdef QT_GRAPHICSSYSTEM_RUNTIME if(QApplicationPrivate::runtime_graphics_system) { QRuntimeGraphicsSystem *gs = static_cast(QApplicationPrivate::graphics_system); gs->setGraphicsSystem(QLatin1String("openvg")); } #endif break; #ifdef Q_SYMBIAN_SUPPORTS_SURFACES case EEventUser: { // GOOM is looking for candidates to kill so indicate that we are // capable of cleaning up by handling this event TInt32 *data = reinterpret_cast(event->EventData()); if (data[0] == EApaSystemEventShutdown && data[1] == KGoomMemoryLowEvent) return 1; } break; #endif #ifdef Q_WS_S60 case KEikInputLanguageChange: qt_keymapper_private()->updateInputLanguage(); break; #endif 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(const QSymbianEvent *symbianEvent) { Q_Q(QApplication); int ret = 0; if (callSymbianEventFilters(symbianEvent)) return 1; int command = symbianEvent->command(); 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: #ifdef Q_WS_S60 bool handled = QSoftKeyManager::handleCommand(command); if (handled) ret = 1; 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(const QSymbianEvent *symbianEvent) { int ret = 0; int type = symbianEvent->resourceChangeType(); switch (type) { #ifdef Q_WS_S60 case KEikDynamicLayoutVariantSwitch: { if (callSymbianEventFilters(symbianEvent)) return 1; if (S60) S60->updateScreenSize(); #ifndef QT_NO_STYLE_S60 QS60Style *s60Style = 0; #ifndef QT_NO_STYLE_STYLESHEET QStyleSheetStyle *proxy = qobject_cast(QApplication::style()); if (proxy) s60Style = qobject_cast(proxy->baseStyle()); else #endif s60Style = qobject_cast(QApplication::style()); if (s60Style) { s60Style->d_func()->handleDynamicLayoutVariantSwitch(); ret = 1; } #endif } break; #ifndef QT_NO_STYLE_S60 case KAknsMessageSkinChange: if (callSymbianEventFilters(symbianEvent)) return 1; if (QS60Style *s60Style = qobject_cast(QApplication::style())) { s60Style->d_func()->handleSkinChange(); ret = 1; } break; #endif #endif // Q_WS_S60 default: break; } return ret; } void QApplicationPrivate::symbianHandleLiteModeStartup() { if (QCoreApplication::arguments().contains(QLatin1String("--startup-lite"))) { if (!QApplication::testAttribute(Qt::AA_S60DontConstructApplicationPanes) && !S60->buttonGroupContainer() && !S60->statusPane()) { // hide and force this app to the background before creating screen furniture to avoid flickers CAknAppUi *appui = static_cast(CCoeEnv::Static()->AppUi()); if (appui) appui->HideApplicationFromFSW(ETrue); CCoeEnv::Static()->RootWin().SetOrdinalPosition(-1); S60->createStatusPaneAndCBA(); if (S60->statusPane()) { S60->setStatusPaneAndButtonGroupVisibility(false, false); } } } } #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) } Qt::KeyboardModifiers QApplication::queryKeyboardModifiers() { return app_keyboardModifiers; } TUint QApplicationPrivate::resolveS60ScanCode(TInt scanCode, TUint keysym) { if (!scanCode) return keysym; QApplicationPrivate *d = QApplicationPrivate::instance(); if (keysym) { // If keysym is specified, cache it. d->scanCodeCache.insert(scanCode, keysym); return keysym; } else { // If not, retrieve the cached version. return d->scanCodeCache[scanCode]; } } void QApplicationPrivate::initializeMultitouch_sys() { #ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER if (HAL::Get(HALData::EPointer3DPressureSupported, pressureSupported) != KErrNone) pressureSupported = 0; if (HAL::Get(HALData::EPointer3DMaxPressure, maxTouchPressure) != KErrNone) maxTouchPressure = KMaxTInt; #else pressureSupported = 0; 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 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 void QApplicationPrivate::_q_aboutToQuit() { qt_beginFullScreenEffect(); #ifdef Q_SYMBIAN_TRANSITION_EFFECTS // Send the shutdown tfx command S60->wsSession().SendEffectCommand(ETfxCmdAppShutDown); #endif } void QApplicationPrivate::emitAboutToReleaseGpuResources() { #ifdef Q_SYMBIAN_SUPPORTS_SURFACES Q_Q(QApplication); QPointer guard(q); emit q->aboutToReleaseGpuResources(); #endif } void QApplicationPrivate::emitAboutToUseGpuResources() { #ifdef Q_SYMBIAN_SUPPORTS_SURFACES Q_Q(QApplication); QPointer guard(q); emit q->aboutToUseGpuResources(); #endif } QS60ThreadLocalData::QS60ThreadLocalData() { CCoeEnv *env = CCoeEnv::Static(); if (env) { //if this is the UI thread, share objects owned by CONE usingCONEinstances = true; wsSession = env->WsSession(); screenDevice = env->ScreenDevice(); } else { usingCONEinstances = false; qt_symbian_throwIfError(wsSession.Connect(qt_s60GetRFs())); screenDevice = new CWsScreenDevice(wsSession); screenDevice->Construct(); } } QS60ThreadLocalData::~QS60ThreadLocalData() { for (int i = 0; i < releaseFuncs.count(); ++i) releaseFuncs[i](); releaseFuncs.clear(); if (!usingCONEinstances) { // wserv has a thread specific handle, do not close it, or delete the screenDevice, if it is not open in this thread THandleInfo handleInfo; wsSession.HandleInfo(&handleInfo); if (handleInfo.iNumOpenInThread) { delete screenDevice; wsSession.Close(); } } } QT_END_NAMESPACE