#include #include "widgeteventhandler.h" #include "webclient.h" bool takeEvents = true; class QLineEditAccess : public QLineEdit { public: void emitTextEdited(const QString &text) { QLineEdit::textChanged(text); QLineEdit::textEdited(text); } }; QWidget * findEventTarget(QWidget *root, QPoint pos) { QWidget *current = root; QWidget *next = current->childAt(pos); while (next) { current = next; next = current->childAt(pos); } return current; } WidgetEventHandler::WidgetEventHandler(QObject *parent) : QObject(parent), graphicsWidget(false), grabbing(false) { focusWidget = 0; } void WidgetEventHandler::setRootWidget(QWidget *root) { DEBUG << "set root widget" << root; rootWidget = root; recursivelyInstallEventHandler(root); } bool WidgetEventHandler::shouldProcessChildWidgets(QWidget *widget) { if (qobject_cast(widget)) return false; return true; } void WidgetEventHandler::recursivelyInstallEventHandler(QWidget *widget) { widget->installEventFilter(this); DEBUG << "event filter on" << widget->metaObject()->className() << widget->objectName() << int(widget); if (QLineEdit *lineEdit = qobject_cast(widget)) { connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(textChange())); events.addEvent((int)widget, EventEntry::TextUpdate); } else if (QTextEdit *textEdit = qobject_cast(widget)) { connect(textEdit, SIGNAL(textChanged()), this, SLOT(textChange())); events.addEvent((int)widget, EventEntry::TextUpdate); } else if (QLabel *label = qobject_cast(widget)) { events.addEvent((int)widget, EventEntry::TextUpdate); } else if (QAbstractScrollArea *scrollArea = qobject_cast(widget)) { recursivelyInstallEventHandler(scrollArea->viewport()); } if (shouldProcessChildWidgets(widget) == false) return; foreach (QObject *child, widget->children()) { // DEBUG() << "descend" << widget << child; if (QWidget *childWidget = qobject_cast(child)) recursivelyInstallEventHandler(childWidget); } } void WidgetEventHandler::setSession(Session *session) { events.setSession(session); } Session *WidgetEventHandler::session() { return events.m_session; } void WidgetEventHandler::addPendingUpdate(QWidget* widget, const QRect &rect) { if (pendingUpdates.isEmpty()) QTimer::singleShot(0, this, SLOT(updatePendingWidgets())); pendingUpdates.insert(widget, rect); } bool WidgetEventHandler::eventFilter(QObject *object, QEvent *event) { QWidget *widget = qobject_cast(object); if (event->type() == QEvent::Paint) { if (QLabel *label = qobject_cast(widget)) { events.addEvent((int)widget, EventEntry::TextUpdate); } else if (QTextEdit *textEdit = qobject_cast(widget)) { events.addEvent((int)widget, EventEntry::TextUpdate); } else if (!grabbing) { DEBUG << " add paint update" << object; addPendingUpdate(widget, static_cast(event)->rect()); } } if (event->type() == QEvent::Show) { // DEBUG << "show" << object; // Add immediate show update if we have an image to serve. if (events.images.contains((int)widget)) { addShowEvent(widget); } // Add pending paint update addPendingUpdate(widget, widget->rect()); recursivelyAddShow(widget); } if (event->type() == QEvent::Hide) { // DEBUG << "hide" << object; events.addEvent((int)widget, EventEntry::Hide); recursivelyAddHide(widget); } if (event->type() == QEvent::Move) { QRect geometry = globalGeometry(widget); // if (object->metaObject()->className() == "QPushbutton") // DEBUG << "move geometry" << object->metaObject()->className() << geometry; if (disableUpdates.contains(sender()) == false) events.addGeometryEvent((int)widget, geometry); } if (event->type() == QEvent::Resize) { QRect geometry = globalGeometry(widget); // if (object->metaObject()->className() == "QPushbutton") // DEBUG << "resize geometry" << geometry; events.addGeometryEvent((int)widget, geometry); } if (event->type() == QEvent::ParentChange) { DEBUG << "parentChange" << "parent"<< widget->parentWidget() << "child" << widget; events.addParentChangeEvent((int)widget); } return false; } void WidgetEventHandler::updatePendingWidgets() { const QHash::const_iterator end = pendingUpdates.end(); QHash::const_iterator it = pendingUpdates.begin(); while(it != end) { widgetPaint(it.key(), it.value()); ++it; } pendingUpdates.clear(); } void WidgetEventHandler::handleRequest(HttpRequest *request, HttpResponse *response) { const QByteArray path = request->path(); bool handled = false; DEBUG << "request" << path; if (path.startsWith("/mousepress") || path.startsWith("/mouserelease") || path.startsWith("/mousedoubleclick")) { // DEBUG << "handle mouse press"; handleMousePress(path); handled = true; } else if (path.startsWith("/keypress") || path.startsWith("/keyrelease")) { // DEBUG << "handle key press"; handleKeyPress(path); handled = true; } else if (path.startsWith("/json")) { handled = handleJsonMessage(path); } if (!handled) { if (path.startsWith("/content")) { events.reset(); // refresh everything recursivelyAddUpdate(rootWidget); } events.handleRequest(request, response); } } bool WidgetEventHandler::handleJsonMessage(const QByteArray &message) { QByteArray jsonText = message.mid(5); // remove "/json" // replace symbols that can't be a part of the url text. jsonText.replace("%7B", "{"); jsonText.replace("%7D", "}"); jsonText.replace("%22", "\""); DEBUG << "json request" << jsonText; json_object* request = json_tokener_parse(jsonText.data()); json_object* type = json_object_object_get(request, "type"); QByteArray typeText = json_object_get_string(type); DEBUG << typeText; if (typeText == "textupdate") { handleTextUpdate(request); json_object_put(request); //free return true; } else if (typeText == "positionupdate") { handlePositionUpdate(request); json_object_put(request); //free return true; } json_object_put(request); //free return false; } void WidgetEventHandler::handleMousePress(const QByteArray &message) { QList tokens = message.split('-'); QPoint p(tokens.at(1).toInt(), tokens.at(2).toInt()); // ### assumes well-formed string QWidget *target = findEventTarget(rootWidget, p); // DEBUG << "target" << target; QPoint local = target->mapFrom(rootWidget, p); if (message.startsWith("/mousepress")) { QMouseEvent press(QEvent::MouseButtonPress, local , Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QApplication::sendEvent(target, &press); } else if (message.startsWith("/mouserelease")) { QMouseEvent release(QEvent::MouseButtonRelease, local , Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QApplication::sendEvent(target, &release); focusWidget = target; } else if (message.startsWith("/mousedoubleclick")) { QMouseEvent press(QEvent::MouseButtonDblClick, local , Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QApplication::sendEvent(target, &press); } } void WidgetEventHandler::handleKeyPress(const QByteArray &message) { if (!focusWidget) return; QList tokens = message.split('-'); int code = tokens.at(1).toInt(); // ### QChar c(code); if (code == 8) { // DEBUG << "backspace"; QKeyEvent press(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier, QString()); QKeyEvent release(QEvent::KeyRelease, Qt::Key_Backspace, Qt::NoModifier, QString()); QApplication::sendEvent(focusWidget, &press); QApplication::sendEvent(focusWidget, &release); } else { QKeyEvent press(QEvent::KeyPress, code, Qt::NoModifier, c); QKeyEvent release(QEvent::KeyRelease, code, Qt::NoModifier, c); QApplication::sendEvent(focusWidget, &press); QApplication::sendEvent(focusWidget, &release); } // DEBUG << "got key press" << c; qApp->processEvents(); } void WidgetEventHandler::handleTextUpdate(json_object* request) { json_object* idObject = json_object_object_get(request, "id"); const int id = json_object_get_int(idObject); json_object* tmp = json_object_object_get(request, "text"); QByteArray text = json_object_get_string(tmp); QLineEdit *lineEdit = ((QLineEdit *)(id)); disableUpdates.insert(lineEdit); if (text == "\n") { QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier); qApp->sendEvent(lineEdit, &keyEvent); //((QLineEditAccess *)(id))->emitTextEdited(text); } else { lineEdit->setText(text); ((QLineEditAccess *)(id))->emitTextEdited(text); } disableUpdates.remove(lineEdit); } void WidgetEventHandler::handlePositionUpdate(json_object *request) { const int id = json_object_get_int(json_object_object_get(request, "id")); const int x = json_object_get_int(json_object_object_get(request, "x")); const int y = json_object_get_int(json_object_object_get(request, "y")); QWidget *widget = ((QWidget *)(id)); disableUpdates.insert(widget); widget->move(x, y); disableUpdates.remove(widget); // DEBUG() << "position update" << id << x << y; } void WidgetEventHandler::widgetPaint(QWidget *widget, const QRect &updateRect) { QImage image(widget->size(), QImage::Format_ARGB32_Premultiplied); DEBUG << "widget->size" << widget->size(); image.fill(QColor(0,0,0,0).rgba()); // fill with transparent pixels grabbing = true; // prevent recusion //DEBUG << "render"; // grab widget only, no background or children widget->render(&image, updateRect.topLeft(), QRegion(updateRect), QWidget::RenderFlags(0)); //DEBUG << "render done"; grabbing = false; // image.save(QString::number(int(widget)) + ".png"); // DEBUG << "update" << widget << (int)widget; events.addUpdateEvent((int)widget, image, updateRect); // DEBUG << "geometry" << widget << (int)widget << globalGeometry(widget); events.addGeometryEvent((int)widget, globalGeometry(widget)); } QRect WidgetEventHandler::globalGeometry(QWidget *widget) { // QRect gemetry = widget->rect(); QRect geometry; if (widget->isWindow()) geometry = QRect(QPoint(0,0), widget->size()); else geometry = QRect(widget->mapToParent(QPoint(0,0)), widget->size()); // DEBUG << "geometry for" << widget << geometry; return geometry; } void WidgetEventHandler::recursivelyAddHide(QWidget *widget) { events.addEvent((int)widget, EventEntry::Hide); if (shouldProcessChildWidgets(widget) == false) return; foreach (QObject *child, widget->children()) { if (QWidget *childWidget = qobject_cast(child)) recursivelyAddHide(childWidget); } } void WidgetEventHandler::recursivelyAddShow(QWidget *root) { if (shouldProcessChildWidgets(root) == false) return; foreach (QObject *child, root->children()) { if (QWidget *childWidget = qobject_cast(child)) { if (childWidget->isVisible() == false) continue; if (events.images.contains((int)childWidget)) { addShowEvent(childWidget); } // Add pending paint update addPendingUpdate(childWidget, childWidget->rect()); } } } void WidgetEventHandler::recursivelyAddUpdate(QWidget *widget) { if (widget->isVisible() == false) return; addShowEvent(widget); events.addGeometryEvent((int)widget, globalGeometry(widget)); events.addParentChangeEvent((int) widget); if (QLineEdit *lineEdit = qobject_cast(widget)) { events.addEvent((int)widget, EventEntry::TextUpdate); } if (QTextEdit *textEdit = qobject_cast(widget)) { events.addEvent((int)widget, EventEntry::TextUpdate); } else if (QLabel *label = qobject_cast(widget)) { events.addEvent((int)widget, EventEntry::TextUpdate); } // Add pending paint update addPendingUpdate(widget, widget->rect()); if (shouldProcessChildWidgets(widget) == false) return; foreach (QObject *child, widget->children()) { if (QWidget *childWidget = qobject_cast(child)) recursivelyAddUpdate(childWidget); } } void WidgetEventHandler::addShowEvent(QWidget *widget) { DEBUG << "add show event" << widget; events.addEvent((int)widget, EventEntry::Show); } void WidgetEventHandler::textChange() { if (disableUpdates.contains(sender()) == false) events.addEvent((int)sender(), EventEntry::TextUpdate); }