/************************************************************************** ** ** This file is part of Qt Simulator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** ** 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. ** ** 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. ** ** If you have questions regarding the use of this file, please contact ** Nokia at info@qt.nokia.com. ** **************************************************************************/ #include "widgetmanager.h" #include "widget.h" #include "application.h" #include "displaywidget.h" #include "qsimulatordata_p.h" #include #include #include #include #include #include #include namespace { int gWidgetIdCounter = 100; // 0 is the desktop. } inline QString stringForCoord(QRect geometry) { QString result; QLocale locale; result += locale.toString(geometry.left()) + QLatin1String(",") + locale.toString(geometry.top()) + QLatin1String(";"); result += locale.toString(geometry.width()) + QLatin1String(",") + locale.toString(geometry.height()); return result; } WidgetManager::WidgetManager(DisplayWidget *dw, QObject *parent) : QObject(parent) , displayWidget(dw) , mFocusWidget(0) , mSharedMemoryIndex(qrand()) , mSymbianSoftKeysSupported(false) , mCurrentMouseInputMode(MultiPointTouchUi::defaultMode) { // Some client applications repaint themselves continuously and send an inordinate amount // of paint events. If we call update for every one of these, the event loop stalls on unix // and no timers get executed any more. So we just store the update request // (Widget::wantsUpdate) and handle them regularly. QTimer *updateTimer = new QTimer(this); connect(updateTimer, SIGNAL(timeout()), this, SLOT(handleUpdateRequests())); updateTimer->start(1000/60); mImageFormat = SIMULATOR_SHARED_IMAGE_FORMAT; tableWidget = new QTableWidget(0, 3); QStringList headerItems; headerItems << "ID" << "Widget Name" << "Rect"; tableWidget->setHorizontalHeaderLabels(headerItems); configWidget = new QWidget(); QHBoxLayout* layout = new QHBoxLayout; layout->addWidget(tableWidget); configWidget->setLayout(layout); connect(this, SIGNAL(topWidgetChanged(Widget*)), this, SLOT(updateSymbianSoftKeys())); connect(this, SIGNAL(topWidgetChanged(Widget*)), this, SLOT(maybeFullscreen(Widget *))); connect(this, SIGNAL(topWidgetChanged(Widget*)), this, SLOT(updateMaemoNavigate(Widget*))); connect(this, SIGNAL(topWidgetChanged(Widget*)), this, SLOT(handleTopOrientation(Widget*))); mScriptInterface = new TouchScriptInterface(this); } WidgetManager::~WidgetManager() { } DisplayWidget* WidgetManager::display() const { return displayWidget; } QImage::Format WidgetManager::imageFormat() const { return mImageFormat; } // Returns the widget with the given id, asserts if not found. Widget* WidgetManager::widgetForId(int id) const { // ignore requests for the root widget if (id == 0 || id == SIMULATOR_DISPLAY_ID) return 0; foreach(Widget* w, widgets) { if (w->widgetId == id) return w; } Q_ASSERT(false && "widgetId does not exist"); return 0; } int WidgetManager::widgetAt(const QPoint &p) const { QPoint actualPosition = p + mOffset; //qDebug("WidgetManager::idForPosition %d, %d", p.x(), p.y()); foreach(Widget* w, widgets) { if (!w->isWidgetVisible()) continue; const QPoint t(w->parentItem()->mapFromItem(displayWidget, actualPosition).toPoint()); const QRect wGeometry = w->geometry(); if (wGeometry.contains(t)) return w->widgetId; } return 0; } /*! Widget that has keyboard focus */ Widget *WidgetManager::focusWidget() const { return mFocusWidget; } /*! The 'current' widget. The one with focus or otherwise the topmost one. */ Widget *WidgetManager::activeWidget() const { if (mFocusWidget) return mFocusWidget; else return topWidget(); } /*! The topmost, visible widget */ Widget *WidgetManager::topWidget() const { Widget *top = 0; foreach(Widget* w, widgets) { if ((!top || w->zValue() > top->zValue()) && w->isWidgetVisible()) { top = w; continue; } } return top; } QtSimulatorPrivate::NewWindowInfo WidgetManager::create( int parentWidgetId, QRect geometry, const QString &title, Application* app) { //qDebug() << "Creating Widget... parent: " << parentWidgetId << " geometry: " << geometry << " id: " << gWidgetIdCounter; QGraphicsItem *parent = displayWidget; if (parentWidgetId) parent = widgetForId(parentWidgetId); if (!parent) qFatal("Could not create child widget of unknown parent with id %d", parentWidgetId); const QString sharedMemoryName = freeSharedMemoryName(); geometry = QRect(parent->mapFromItem(displayWidget, geometry.topLeft() + mOffset).toPoint(), geometry.size()); Widget *w = new Widget(geometry, mImageFormat, title, app, gWidgetIdCounter++, sharedMemoryName, parent); w->setWidgetManager(this); w->setWidgetVisible(false); w->offset = mOffset; w->setMouseInputMode(mCurrentMouseInputMode); widgets.push_front(w); setHighestZ(w); // insert into debug window widgets list tableWidget->insertRow(0); tableWidget->setItem(0, 0, new QTableWidgetItem(QLocale().toString(w->widgetId))); tableWidget->setItem(0, 1, new QTableWidgetItem(w->title)); tableWidget->setItem(0, 2, new QTableWidgetItem(stringForCoord(w->geometry()))); QtSimulatorPrivate::NewWindowInfo info; info.id = w->widgetId; info.sharedMemoryName = sharedMemoryName; return info; } void WidgetManager::showWidget(int id) { //qDebug("WidgetManager::show %d", id); Widget* w = widgetForId(id); if (!w) return; w->setWidgetVisible(true); updateLogItem(id); emit topWidgetChanged(topWidget()); } void WidgetManager::paintWidget(qint32 id) { //qDebug("WidgetManager::paintWidget %d", id); Widget* w = widgetForId(id); if (!w) return; w->memoryFilled = true; w->wantsUpdate = true; } void WidgetManager::setWidgetGeometry(int id, const QRect &geometry) { //qDebug() << Q_FUNC_INFO << id << " geometry: " << geometry; Widget *w = widgetForId(id); if (!w) return; QRect transformed(geometry); transformed.moveTopLeft(w->parentItem()->mapFromItem(displayWidget, transformed.topLeft() + mOffset).toPoint()); w->setGeometry(transformed); if (!w->createMemory()) qFatal("Could not create memory for widget."); updateLogItem(id); } void WidgetManager::moveWidget(int id, const QPoint &to) { Widget *w = widgetForId(id); if (!w) return; QPoint p; p = QPoint(w->parentItem()->mapFromItem(displayWidget, to + mOffset).toPoint()); w->setPos(p); updateLogItem(id); } void WidgetManager::updateLogItem(int id) { // Find the list item int row = -1; for (int i = 0; i < tableWidget->rowCount(); ++i) { if (tableWidget->item(i,0)->text().toInt() == id) { row = i; break; } } // Find the widget Widget *w = 0; for (int i = 0; i < widgets.size(); ++i) { if (widgets.at(i)->widgetId == id) { w = widgets.at(i); break; } } if (row == -1 || w == 0) return; QLocale locale; // update the content tableWidget->setItem(row, 0, new QTableWidgetItem(locale.toString(w->widgetId))); tableWidget->setItem(row, 1, new QTableWidgetItem(w->title)); QRect actualGeometry = w->geometry(); actualGeometry.adjust(-mOffset.x(), -mOffset.y(), 0, 0); tableWidget->setItem(row, 2, new QTableWidgetItem(stringForCoord(actualGeometry) + QString(";z:") + locale.toString(w->zValue()))); } void WidgetManager::hideWidget(int id) { //qDebug() << Q_FUNC_INFO << id; Widget *w = widgetForId(id); if (!w) return; w->setWidgetVisible(false); emit topWidgetChanged(topWidget()); } void WidgetManager::destroyWidget(int id) { Widget *w = widgetForId(id); if (!w) return; removeWidgetFromTable(id); widgets.removeAll(w); if (mFocusWidget == w) { Widget *newTopWidget = topWidget(); if (newTopWidget) setFocusWidget(newTopWidget->widgetId); else mFocusWidget = 0; emit topWidgetChanged(newTopWidget); } delete w; } void WidgetManager::raiseAboveMenus(int id) { //qDebug() << Q_FUNC_INFO << id; Widget *w = widgetForId(id); if (!w) return; w->setFullscreen(true); setHighestZ(w); } void WidgetManager::dropBelowMenus(int id) { //qDebug() << Q_FUNC_INFO << id; Widget *w = widgetForId(id); if (!w) return; w->setFullscreen(false); setHighestZ(w); } void WidgetManager::removeWidgetFromTable(int id) { // Find the list item int row = -1; for (int i = 0; i < tableWidget->rowCount(); ++i) { if (tableWidget->item(i,0)->text().toInt() == id) { row = i; break; } } if (row != -1) tableWidget->removeRow(row); } void WidgetManager::onApplicationUnregistered(int appId) { for (int i =0; i < widgets.length(); i++) { Widget *current = widgets.at(i); if (current->owner->id() == appId) { destroyWidget(current->widgetId); --i; } } } void WidgetManager::stackWidget(int id, int pos) { //qDebug() << Q_FUNC_INFO << id << pos; Widget *item = widgetForId(id); if (!item) return; if (pos == -1) { // -1 front setHighestZ(item); } else if (pos == -2) { // -2 back setLowestZ(item); } else if (pos > 0) { Widget *under = widgetForId(pos); if (!under) return; setZBefore(item, under); } } void WidgetManager::setWidgetWindowTitle(int id, const QString &title) { Widget *w = widgetForId(id); if (!w) return; w->title = title; // ### TODO: Update the window decorations when they exist. } void WidgetManager::setFocusWidget(int id) { Widget *w = widgetForId(id); if (!w) return; mFocusWidget = w; mFocusWidget->setFocus(); displayWidget->setFocusProxy(w); } void WidgetManager::setWidgetOpacity(int id, double opacity) { Widget *w = widgetForId(id); if (!w) return; w->setOpacity(opacity); } void WidgetManager::setWidgetParent(int id, int newParentId) { Widget *w = widgetForId(id); if (!w) return; Widget *newParent = widgetForId(newParentId); if (!newParent) return; //qDebug() << "Setting parent for " << id << " to " << newParentId; w->setParentItem(newParent); } void WidgetManager::setMaemo5StackedWindowFlag(int id, bool stacked) { Widget *w = widgetForId(id); if (!w) return; w->setMaemo5Stacked(stacked); } void WidgetManager::setAcceptsTouchEvents(int id, bool accept) { Widget *w = widgetForId(id); if (!w) return; w->setAcceptsHoverEvents(accept); } void WidgetManager::handleUpdateRequests() { foreach (Widget *w, widgets) { if (w->isWidgetVisible() && w->wantsUpdate) { w->update(); w->wantsUpdate = false; } } } void WidgetManager::sendKeyPress(Qt::Key key, const QString &text) { Widget *fw = focusWidget(); if (!fw) return; QScopedPointer ev(new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier, text)); fw->handleKeyEvent(ev.data()); } void WidgetManager::sendKeyRelease(Qt::Key key) { // BUG: Can we be certain the focus widget didn't change since the key press? Widget *fw = focusWidget(); if (!fw) return; QScopedPointer ev(new QKeyEvent(QEvent::KeyRelease, key, Qt::NoModifier)); fw->handleKeyEvent(ev.data()); } void WidgetManager::closeCurrentWindow() { Widget *tw = topWidget(); if (!tw) return; QtSimulatorPrivate::RemoteMetacall::call(tw->owner->socket(), QtSimulatorPrivate::NoSync, "closeWindow", tw->widgetId); } void WidgetManager::changeOffset(const QPoint &offset) { mOffset = offset; updateWidgetOffset(mOffset); } void WidgetManager::updateWidgetOffset(const QPoint &newOffset) { foreach(Widget* w, widgets) { w->updateOffset(newOffset); } } void WidgetManager::updateMaemoNavigate() { Widget *top = topWidget(); if (!top) return; updateMaemoNavigate(top); } void WidgetManager::updateMaemoNavigate(Widget *topWidget) { if (!topWidget) return; QGraphicsItem *parent = topWidget->parentItem(); if (topWidget->maemo5Stacked() && parent != displayWidget) { Widget *parentWidget = static_cast(parent); if (parentWidget->maemo5Stacked()) emit maemoNavigationChanged(maemoBack); else emit maemoNavigationChanged(maemoClose); } else { emit maemoNavigationChanged(maemoClose); } } /*! Adjust the Z order of this and all siblings to make this have the highest value and thus be painted over all siblings. */ void WidgetManager::setHighestZ(Widget *w) { //qDebug() << Q_FUNC_INFO << w->widgetId; if (!w->parentItem()) return; // get the existing z order of everything but this QMap order; foreach (Widget *child, widgets) { if (child != w) order.insert(child->zValue(), child); } // reset all the other z values int z = 0; foreach (Widget *v, order) v->setZValue(z++); // set w on top w->setZValue(z); // show the active menu bar emit topWidgetChanged(topWidget()); #ifndef QT_NO_PHONON // make sure that video widgets stay on top ++z; foreach (Widget *child, widgets) { QVariant objectName = child->data(0); if (objectName.isNull() || objectName.toString() != QLatin1String("PhononVideoWidget")) continue; child->setZValue(z); } #endif } /*! Adjust the Z order of this and all siblings to make this have the lowest value and thus be painted under all siblings. */ void WidgetManager::setLowestZ(Widget *w) { //qDebug() << Q_FUNC_INFO << w->widgetId; if (!w->parentItem()) return; // get the existing z order of everything but this QMap order; foreach (Widget *child, widgets) { if (child != w) order.insert(child->zValue(), child); } if (order.isEmpty()) { w->setZValue(0); } else { w->setZValue(order.begin().key() - 1); } emit topWidgetChanged(topWidget()); } /*! Adjust the Z order of this and all siblings to make this be painted just before other. */ void WidgetManager::setZBefore(Widget *w, Widget *other) { //qDebug() << Q_FUNC_INFO << w->widgetId << "just before" << other->widgetId; if (!w->parentItem()) return; QMap order; bool otherFound = false; // get the existing z order of everything but this foreach (Widget *child, widgets) { if (child != w) order.insert(child->zValue(), child); if (child == other) otherFound = true; } if (!otherFound) { qWarning("Could not reorder widgets. %s is no sibling of %s", qPrintable(other->title), qPrintable(w->title)); return; } // reset all the other z values int z = 0; foreach (Widget *v, order) { v->setZValue(z++); if (v == other) w->setZValue(z++); } emit topWidgetChanged(topWidget()); } void WidgetManager::maybeFullscreen(Widget *topWidget) { if (!topWidget || !topWidget->isFullScreen()) return; // get the existing z order QMap order; foreach (Widget *child, widgets) { order.insert(child->zValue(), child); } // rearrange, so the fullscreen flag of topWidget is respected while preserving z order int z = 0; foreach (Widget *o, order) { if (o == topWidget) z = SIMULATOR_MENU_Z + 1; o->setZValue(z++); } } void WidgetManager::updateSymbianSoftKeys() { Widget *active = activeWidget(); if (!active) { for (int i = 0; i < SymbianSoftKeyButtonData::MaxButtons; ++i) emit symbianSoftKeyTextChanged(i, QString()); return; } for (int i = 0; i < SymbianSoftKeyButtonData::MaxButtons; ++i) emit symbianSoftKeyTextChanged(i, active->owner->symbianSoftKeyText(i)); } void WidgetManager::triggerSymbianSoftKeyAction(int buttonNr) { Widget *active = activeWidget(); if (!active) return; active->owner->triggerSymbianSoftKeyAction(buttonNr); } QString WidgetManager::freeSharedMemoryName() { for (int i = 0; i < 1000; ++i) { const QString name = QString("QTSIMULATOR_SHARED_MEMORY_%1").arg(QString::number(mSharedMemoryIndex++)); QSharedMemory memory(name); if (memory.create(1024)) return name; mSharedMemoryIndex = qrand(); } qFatal("Could not find a free shared memory area"); return QString(); } void WidgetManager::setOrientationAttribute(int id, int orientation, bool on) { Qt::WidgetAttribute realOrientation = static_cast(orientation); switch (realOrientation) { case Qt::WA_LockLandscapeOrientation: case Qt::WA_LockPortraitOrientation: case Qt::WA_AutoOrientation: break; default: return; } Widget* w = widgetForId(id); if (!w) return; if (w->orientation() == realOrientation && !on) realOrientation = Qt::WA_AutoOrientation; w->setOrientation(realOrientation); if (topWidget() == w) { emit topOrientationChanged(realOrientation); } } void WidgetManager::handleTopOrientation(Widget *topWidget) { if (!topWidget) emit topOrientationChanged(Qt::WA_AutoOrientation); else emit topOrientationChanged(topWidget->orientation()); } void WidgetManager::setMouseInputMode(MultiPointTouchUi::InputMode newMode) { mCurrentMouseInputMode = newMode; foreach (Widget* w, widgets) w->setMouseInputMode(newMode); } /*! \class TouchScriptInterface \brief Exposed as touch */ TouchScriptInterface::TouchScriptInterface(WidgetManager *wm) : QObject(wm) , mWidgetManager(wm) { } TouchScriptInterface::~TouchScriptInterface() { } QtSimulatorPrivate::TouchPointData TouchScriptInterface::updateTouchPoint(const QtSimulatorPrivate::TouchPointData &touchPoint, QEvent::Type touchType, const QPointF ¢er, const QSizeF &size) { QtSimulatorPrivate::TouchPointData newTouchPoint; DisplayWidget *displayWidget = mWidgetManager->display(); switch (touchType) { case QEvent::TouchBegin: newTouchPoint.startPos = newTouchPoint.lastPos = center; newTouchPoint.startScenePos = newTouchPoint.lastScenePos = displayWidget->mapToScene(center); newTouchPoint.startScreenPos = newTouchPoint.lastScreenPos = displayWidget->mapToItem(displayWidget, center); newTouchPoint.startNormalizedPos = newTouchPoint.lastNormalizedPos = QPointF(newTouchPoint.startScreenPos.x() / displayWidget->width(), newTouchPoint.startScreenPos.y() / displayWidget->height()); break; case QEvent::TouchUpdate: newTouchPoint.startPos = touchPoint.startPos; newTouchPoint.startScenePos = touchPoint.startScenePos; newTouchPoint.startScreenPos = touchPoint.startScreenPos; newTouchPoint.startNormalizedPos = touchPoint.startNormalizedPos; newTouchPoint.lastPos = touchPoint.rect.center(); newTouchPoint.lastScenePos = displayWidget->mapToScene(touchPoint.rect.center()); newTouchPoint.lastScreenPos = displayWidget->mapToItem(displayWidget, touchPoint.rect.center()); newTouchPoint.lastNormalizedPos = QPointF(newTouchPoint.lastScreenPos.x() / displayWidget->width(), newTouchPoint.lastScreenPos.y() / displayWidget->height()); break; default: return touchPoint; } newTouchPoint.rect = QRectF(center - QPointF(size.width() / 2, size.height() / 2), size); newTouchPoint.sceneRect = QRectF(displayWidget->mapToScene(center - QPointF(size.width() / 2, size.height() / 2)), size); newTouchPoint.screenRect = QRectF(displayWidget->mapToItem(displayWidget, center - QPointF(size.width() / 2, size.height() / 2)), size); newTouchPoint.normalizedPos = QPointF((newTouchPoint.screenRect.center().x() - size.width() / 2) / displayWidget->width(), (newTouchPoint.screenRect.center().y() - size.height() / 2) / displayWidget->height()); newTouchPoint.pressure = .75; return newTouchPoint; } void TouchScriptInterface::beginTouch(int id, int x, int y) { fireTouchEvent(id, x, y, QEvent::TouchBegin); } void TouchScriptInterface::updateTouch(int id, int x, int y) { fireTouchEvent(id, x, y, QEvent::TouchUpdate); } void TouchScriptInterface::endTouch(int id, int x, int y) { fireTouchEvent(id, x, y, QEvent::TouchEnd); } void TouchScriptInterface::fireTouchEvent(int id, int x, int y, QEvent::Type type) { Widget *widget = mWidgetManager->widgetForId(mWidgetManager->widgetAt(QPoint(x, y))); if (!widget) return; QtSimulatorPrivate::TouchPointData point; int i = 0; bool found = false; for (;i < data.touchPoints.length(); ++i) { if (data.touchPoints.at(i).id == id) { found = true; point = data.touchPoints.at(i); break; } } point = updateTouchPoint(point, type, QPointF(x, y), QSizeF(15, 15)); point.id = id; if (found) data.touchPoints[i] = point; else { data.touchPoints.append(point); } data.type = static_cast(type); switch (type) { case QEvent::TouchBegin: data.touchPointStates = Qt::TouchPointPressed; break; case QEvent::TouchUpdate: data.touchPointStates = Qt::TouchPointMoved; break; case QEvent::TouchEnd: data.touchPointStates = Qt::TouchPointReleased; data.touchPoints.takeAt(i); break; default: break; } data.deviceType = QTouchEvent::TouchScreen; data.modifiers = Qt::NoModifier; QtSimulatorPrivate::RemoteMetacall::call(widget->owner->socket(), QtSimulatorPrivate::NoSync, "dispatchTouchEvent", widget->widgetId, data); } void WidgetManager::runCurrentGestureScript() { emit gestureScriptRequested(mCurrentGesturePath); } void WidgetManager::setCurrentGesturePath(const QString &path) { mCurrentGesturePath = path; }