| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the QtGui module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** 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. |
| ** |
| ** 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. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $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 "apgwgnam.h" // For CApaWindowGroupName |
| #include <mdaaudiotoneplayer.h> // For CMdaAudioToneUtility |
| |
| #if defined(Q_OS_SYMBIAN) |
| # include <private/qs60mainapplication_p.h> |
| # include <centralrepository.h> |
| # include "qs60mainappui.h" |
| # include "qinputcontext.h" |
| # include <private/qgraphicssystemex_symbian_p.h> |
| #endif |
| |
| #if defined(Q_WS_S60) |
| # if !defined(QT_NO_IM) |
| # include <private/qcoefepinputcontext_p.h> |
| # endif |
| #endif |
| |
| #include "private/qstylesheetstyle_p.h" |
| |
| #include <hal.h> |
| #include <hal_data.h> |
| |
| #ifdef Q_SYMBIAN_TRANSITION_EFFECTS |
| #include <graphics/wstfxconst.h> |
| #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; |
| |
| 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<QSymbianControl *>(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::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) { |
| if (backingStore.data()) { |
| backingStore.registerWidget(widget); |
| } else { |
| backingStore.create(window); |
| backingStore.registerWidget(widget); |
| qt_widget_private(widget)->invalidateBuffer(widget->rect()); |
| widget->repaint(); |
| } |
| } else { |
| // 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<CEikAppUi*>(S60->appUi())->ClientRect(); |
| if (S60->partialKeyboardOpen) { |
| // Adjust client rect when splitview is open, since for some curious reason |
| // native side insists that clientRect starts from (0,0) even though status |
| // pane might be visible. |
| 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()) |
| r.Move(0, statusPaneRect.Height()); |
| else |
| 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(); |
| |
| 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() |
| { |
| // Ensure backing store is deleted before the top-level |
| // window is destroyed |
| qt_widget_private(qwidget)->topData()->backingStore.destroy(); |
| |
| 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) const |
| { |
| QPoint pos(pointerEventPos.iX, pointerEventPos.iY); |
| if (qwidget->d_func()->fixNativeOrientationCalled) { |
| QSize wsize = qwidget->size(); |
| TSize size = Size(); |
| if (size.iWidth == wsize.height() && size.iHeight == wsize.width()) { |
| qreal x = pos.x(); |
| qreal y = pos.y(); |
| pos.setX(size.iHeight - y); |
| pos.setY(x); |
| } |
| } |
| return pos; |
| } |
| |
| TRect QSymbianControl::translateRectForFixedNativeOrientation(const TRect &controlRect) const |
| { |
| TRect rect = controlRect; |
| if (qwidget->d_func()->fixNativeOrientationCalled) { |
| QPoint a = translatePointForFixedNativeOrientation(rect.iTl); |
| QPoint b = translatePointForFixedNativeOrientation(rect.iBr); |
| 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); |
| QPoint globalPos = translatePointForFixedNativeOrientation(aPenEventScreenLocation); |
| 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 |
| void QSymbianControl::translateAdvancedPointerEvent(const TAdvancedPointerEvent *event) |
| { |
| QApplicationPrivate *d = QApplicationPrivate::instance(); |
| QPointF screenPos = qwidget->mapToGlobal(translatePointForFixedNativeOrientation(event->iPosition)); |
| qreal pressure; |
| if(d->pressureSupported |
| && event->Pressure() > 0) //workaround for misconfigured HAL |
| pressure = event->Pressure() / qreal(d->maxTouchPressure); |
| else |
| pressure = qreal(1.0); |
| processTouchEvent(event->PointerNumber(), event->iType, screenPos, pressure); |
| } |
| #endif |
| |
| void QSymbianControl::processTouchEvent(int pointerNumber, TPointerEvent::TType type, QPointF screenPos, qreal pressure) |
| { |
| QRect screenGeometry = qApp->desktop()->screenGeometry(qwidget); |
| |
| QApplicationPrivate *d = QApplicationPrivate::instance(); |
| |
| QList<QTouchEvent::TouchPoint> points = d->appAllTouchPoints; |
| while (points.count() <= pointerNumber) |
| points.append(QTouchEvent::TouchPoint(points.count())); |
| |
| Qt::TouchPointStates allStates = 0; |
| for (int i = 0; i < points.count(); ++i) { |
| QTouchEvent::TouchPoint &touchPoint = points[i]; |
| |
| if (touchPoint.id() == pointerNumber) { |
| Qt::TouchPointStates state; |
| switch (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 (pointerNumber == 0) |
| state |= Qt::TouchPointPrimary; |
| touchPoint.setState(state); |
| |
| touchPoint.setScreenPos(screenPos); |
| touchPoint.setNormalizedPos(QPointF(screenPos.x() / screenGeometry.width(), |
| screenPos.y() / screenGeometry.height())); |
| |
| touchPoint.setPressure(pressure); |
| } 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(); |
| } else { |
| d->appAllTouchPoints = points; |
| } |
| |
| QApplicationPrivate::translateRawTouchEvent(qwidget, |
| QTouchEvent::TouchScreen, |
| points); |
| } |
| |
| 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)); |
| } |
| |
| void QSymbianControl::HandlePointerEvent(const TPointerEvent& pEvent) |
| { |
| QMouseEvent::Type type; |
| Qt::MouseButton button; |
| mapS60MouseEventTypeToQt(&type, &button, &pEvent); |
| Qt::KeyboardModifiers modifiers = mapToQtModifiers(pEvent.iModifiers); |
| |
| QPoint widgetPos = translatePointForFixedNativeOrientation(pEvent.iPosition); |
| 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) { |
| processTouchEvent(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<QS60MainAppUi *>(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<QEventDispatcherS60 *>(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) { |
| // 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<QEventDispatcherS60 *>(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<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_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<QCoeFepInputContext *>(w->inputContext()))) { |
| return ic->inputCapabilities(); |
| } else { |
| return TCoeInputCapabilities(TCoeInputCapabilities::ENone, 0, 0); |
| } |
| } |
| #endif |
| |
| void QSymbianControl::Draw(const TRect& controlRect) const |
| { |
| // 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<QRuntimeWindowSurface*>(qwidget->windowSurface()); |
| s60Surface = static_cast<QS60WindowSurface *>(rtSurface->m_windowSurface.data()); |
| } else |
| #endif |
| s60Surface = static_cast<QS60WindowSurface *>(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); |
| } |
| } |
| } |
| |
| void QSymbianControl::FocusChanged(TDrawNow /* aDrawNow */) |
| { |
| if (m_ignoreFocusChanged || (qwidget->windowType() & Qt::WindowType_Mask) == Qt::Desktop) |
| 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; |
| |
| 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 (qwidget->isWindow()) |
| S60->setRecursiveDecorationsVisibility(qwidget, qwidget->windowState()); |
| #endif |
| } else if (QApplication::activeWindow() == qwidget->window()) { |
| bool focusedControlFound = false; |
| WId winId = 0; |
| for (QWidget *w = qwidget->parentWidget(); w && (winId = w->internalWinId()); w = w->parentWidget()) { |
| if (winId->IsFocused() && winId->IsVisible()) { |
| focusedControlFound = true; |
| break; |
| } else if (w->isWindow()) |
| break; |
| } |
| if (!focusedControlFound) { |
| 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 } |
| } |
| |
| 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<CEikAppUi*>(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. |
| if (!qwidget->isActiveWindow() |
| || !qwidget->isVisible() |
| || !qwidget->isAncestorOf(widget)) { |
| |
| 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<QCoeFepInputContext *>(widget->inputContext()); |
| if (!ic) { |
| ic = qobject_cast<QCoeFepInputContext *>(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: |
| { |
| 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() |
| { |
| 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()). |
| |
| // 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)) |
| { |
| 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 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). |
| QtEikonEnv* coe = new QtEikonEnv; |
| //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(); |
| |
| // 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 |
| |
| #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<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 invocation in order |
| // to destroy WId objects during reparenting. |
| qRegisterMetaType<WId>("WId"); |
| } |
| |
| extern void qt_cleanup_symbianFontDatabase(); // qfontdatabase_s60.cpp |
| |
| /***************************************************************************** |
| 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 |
| |
| qt_cleanup_symbianFontDatabase(); |
| // 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(); |
| |
| 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<QSymbianControl *>(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<QSymbianControl *>(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<QComboBox *>(popup->parentWidget())) |
| static_cast<QSymbianControl *>(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<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 (popup && qobject_cast<QComboBox *>(popup->parentWidget())) |
| static_cast<QSymbianControl *>(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<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->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<QSymbianEvent *>(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); |
| |
| if (d->eventDispatcher->filterEvent(const_cast<QSymbianEvent *>(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; |
| |
| 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<CCoeControl*>(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<QMenu *>(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<QRuntimeGraphicsSystem*>(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<QRuntimeGraphicsSystem*>(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<TInt32 *>(event->EventData()); |
| if (data[0] == EApaSystemEventShutdown && data[1] == KGoomMemoryLowEvent) |
| return 1; |
| } |
| 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<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 (callSymbianEventFilters(symbianEvent)) |
| return 1; |
| 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 (!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<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 |
| |
| void QApplicationPrivate::_q_aboutToQuit() |
| { |
| qt_beginFullScreenEffect(); |
| |
| #ifdef Q_SYMBIAN_TRANSITION_EFFECTS |
| // Send the shutdown tfx command |
| S60->wsSession().SendEffectCommand(ETfxCmdAppShutDown); |
| #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) { |
| delete screenDevice; |
| wsSession.Close(); |
| } |
| } |
| |
| QT_END_NAMESPACE |