Bug 798413 - Tests for export signature from MAR files. r=bsmith
[gecko.git] / widget / qt / mozqwidget.cpp
blob268e4e8d0ef733b8743b2ab73e2513f67b26b214
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=4 et sw=4 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include <QGraphicsSceneHoverEvent>
8 #include <QGraphicsSceneMouseEvent>
9 #if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
10 #include <QInputContext>
11 #endif
12 #include <QtCore/QTimer>
13 // Solve conflict of qgl.h and GLDefs.h
14 #define GLdouble_defined 1
15 #include "mozqwidget.h"
16 #include "nsWindow.h"
18 #include "nsIObserverService.h"
19 #include "mozilla/Services.h"
22 #ifdef MOZ_ENABLE_QTMOBILITY
23 #include "mozqorientationsensorfilter.h"
24 #ifdef MOZ_X11
25 #include <QX11Info>
26 #include <X11/Xlib.h>
27 #include <X11/Xatom.h>
28 # undef KeyPress
29 # undef KeyRelease
30 # undef CursorShape
31 #endif //MOZ_X11
32 #endif //MOZ_ENABLE_QTMOBILITY
35 Pure Qt is lacking a clear API to get the current state of the VKB (opened
36 or closed).
38 static bool gKeyboardOpen = false;
41 In case we could not open the keyboard, we will try again when the focus
42 event is sent. This can happen if the keyboard is asked for before the
43 window is focused. This global is used to track that case.
45 static bool gFailedOpenKeyboard = false;
48 For websites that focus editable elements during other operations for a very
49 short time, we add some decoupling to prevent the VKB from appearing and
50 reappearing for a very short time. This global is set when the keyboard should
51 be opened and if it is still set when a timer runs out, the VKB is really
52 shown.
54 static bool gPendingVKBOpen = false;
57 Contains the last preedit String, this is needed in order to generate KeyEvents
59 static QString gLastPreeditString;
61 MozQWidget::MozQWidget(nsWindow* aReceiver, QGraphicsItem* aParent)
62 : mReceiver(aReceiver)
64 setParentItem(aParent);
65 #if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0))
66 setFlag(QGraphicsItem::ItemAcceptsInputMethod);
67 setAcceptTouchEvents(true);
68 #endif
69 setAcceptHoverEvents(true);
72 MozQWidget::~MozQWidget()
74 if (mReceiver)
75 mReceiver->QWidgetDestroyed();
78 void MozQWidget::paint(QPainter* aPainter, const QStyleOptionGraphicsItem* aOption, QWidget* aWidget /*= 0*/)
80 mReceiver->DoPaint(aPainter, aOption, aWidget);
83 void MozQWidget::activate()
85 // ensure that the keyboard is hidden when we activate the window
86 hideVKB();
87 mReceiver->DispatchActivateEventOnTopLevelWindow();
90 void MozQWidget::deactivate()
92 // ensure that the keyboard is hidden when we deactivate the window
93 hideVKB();
94 mReceiver->DispatchDeactivateEventOnTopLevelWindow();
97 void MozQWidget::resizeEvent(QGraphicsSceneResizeEvent* aEvent)
99 mReceiver->OnResizeEvent(aEvent);
102 void MozQWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent* aEvent)
104 mReceiver->contextMenuEvent(aEvent);
107 void MozQWidget::dragEnterEvent(QGraphicsSceneDragDropEvent* aEvent)
109 mReceiver->OnDragEnter(aEvent);
112 void MozQWidget::dragLeaveEvent(QGraphicsSceneDragDropEvent* aEvent)
114 mReceiver->OnDragLeaveEvent(aEvent);
117 void MozQWidget::dragMoveEvent(QGraphicsSceneDragDropEvent* aEvent)
119 mReceiver->OnDragMotionEvent(aEvent);
122 void MozQWidget::dropEvent(QGraphicsSceneDragDropEvent* aEvent)
124 mReceiver->OnDragDropEvent(aEvent);
127 void MozQWidget::focusInEvent(QFocusEvent* aEvent)
129 mReceiver->OnFocusInEvent(aEvent);
131 // The application requested the VKB during startup but did not manage
132 // to open it, because there was no focused window yet so we do it now by
133 // requesting the VKB without any timeout.
134 if (gFailedOpenKeyboard)
135 requestVKB(0, this);
138 #ifdef MOZ_ENABLE_QTMOBILITY
139 void MozQWidget::orientationChanged()
141 if (!scene() || !scene()->views().size()) {
142 return;
145 NS_ASSERTION(scene()->views().size() == 1, "Not exactly one view for our scene!");
146 QTransform& transform = MozQOrientationSensorFilter::GetRotationTransform();
147 QRect scrTrRect = transform.mapRect(scene()->views()[0]->rect());
149 setTransformOriginPoint(scene()->views()[0]->size().width() / 2, scene()->views()[0]->size().height() / 2);
150 scene()->views()[0]->setTransform(transform);
151 int orientation = MozQOrientationSensorFilter::GetWindowRotationAngle();
152 if (orientation == 0 || orientation == 180) {
153 setPos(0,0);
154 } else {
155 setPos(-(scrTrRect.size().width() - scrTrRect.size().height()) / 2,
156 (scrTrRect.size().width() - scrTrRect.size().height()) / 2);
158 resize(scrTrRect.size());
159 scene()->setSceneRect(QRectF(QPointF(0, 0), scrTrRect.size()));
160 #ifdef MOZ_X11
161 Display* display = QX11Info::display();
162 if (!display) {
163 return;
166 Atom orientationAngleAtom = XInternAtom(display, "_MEEGOTOUCH_ORIENTATION_ANGLE", False);
167 XChangeProperty(display, scene()->views()[0]->effectiveWinId(),
168 orientationAngleAtom, XA_CARDINAL, 32,
169 PropModeReplace, (unsigned char*)&orientation, 1);
170 #endif
172 #endif
174 void MozQWidget::focusOutEvent(QFocusEvent* aEvent)
176 mReceiver->OnFocusOutEvent(aEvent);
177 //OtherFocusReason most like means VKB was closed manual (done button)
178 if (aEvent->reason() == Qt::OtherFocusReason && gKeyboardOpen) {
179 hideVKB();
183 void MozQWidget::hoverEnterEvent(QGraphicsSceneHoverEvent* aEvent)
185 mReceiver->OnEnterNotifyEvent(aEvent);
188 void MozQWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent* aEvent)
190 mReceiver->OnLeaveNotifyEvent(aEvent);
193 void MozQWidget::hoverMoveEvent(QGraphicsSceneHoverEvent* aEvent)
195 mReceiver->OnMotionNotifyEvent(aEvent->pos(), aEvent->modifiers());
198 void MozQWidget::keyPressEvent(QKeyEvent* aEvent)
200 #if (MOZ_PLATFORM_MAEMO == 6)
201 if (!gKeyboardOpen ||
202 //those might get sended as KeyEvents, even in 'NormalMode'
203 aEvent->key() == Qt::Key_Space ||
204 aEvent->key() == Qt::Key_Return ||
205 aEvent->key() == Qt::Key_Backspace) {
206 mReceiver->OnKeyPressEvent(aEvent);
208 #elif (MOZ_PLATFORM_MAEMO == 5)
209 // Below removed to prevent invertion of upper and lower case
210 // See bug 561234
211 // mReceiver->OnKeyPressEvent(aEvent);
212 #else
213 mReceiver->OnKeyPressEvent(aEvent);
214 #endif
217 void MozQWidget::keyReleaseEvent(QKeyEvent* aEvent)
219 #if (MOZ_PLATFORM_MAEMO == 6)
220 if (!gKeyboardOpen ||
221 //those might get sended as KeyEvents, even in 'NormalMode'
222 aEvent->key() == Qt::Key_Space ||
223 aEvent->key() == Qt::Key_Return ||
224 aEvent->key() == Qt::Key_Backspace) {
225 mReceiver->OnKeyReleaseEvent(aEvent);
227 return;
228 #elif (MOZ_PLATFORM_MAEMO == 5)
229 // Below line should be removed when bug 561234 is fixed
230 mReceiver->OnKeyPressEvent(aEvent);
231 #endif
232 mReceiver->OnKeyReleaseEvent(aEvent);
235 void MozQWidget::inputMethodEvent(QInputMethodEvent* aEvent)
237 QString currentPreeditString = aEvent->preeditString();
238 QString currentCommitString = aEvent->commitString();
240 //first check for some controllkeys send as text...
241 if (currentCommitString == " ") {
242 sendPressReleaseKeyEvent(Qt::Key_Space, currentCommitString.unicode());
243 } else if (currentCommitString == "\n") {
244 sendPressReleaseKeyEvent(Qt::Key_Return, currentCommitString.unicode());
245 } else if (currentCommitString.isEmpty()) {
246 //if its no controllkey than check if current Commit is empty
247 //if yes than we have some preedit text here
248 if (currentPreeditString.length() == 1 && gLastPreeditString.isEmpty()) {
249 //Preedit text can change its entire look'a'like
250 //check if length of new compared to the old is 1,
251 //means that its a new startup
252 sendPressReleaseKeyEvent(0, currentPreeditString.unicode());
253 } else if (currentPreeditString.startsWith(gLastPreeditString)) {
254 //Length was not 1 or not a new startup
255 //check if the current preedit starts with the last one,
256 //if so: Add new letters (note: this can be more then one new letter)
257 const QChar * text = currentPreeditString.unicode();
258 for (int i = gLastPreeditString.length(); i < currentPreeditString.length(); i++) {
259 sendPressReleaseKeyEvent(0, &text[i]);
261 } else {
262 //last possible case, we had a PreeditString which was now completely changed.
263 //first, check if just one letter was removed (normal Backspace case!)
264 //if so: just send the backspace
265 QString tempLastPre = gLastPreeditString;
266 tempLastPre.truncate(gLastPreeditString.length()-1);
267 if (currentPreeditString == tempLastPre) {
268 sendPressReleaseKeyEvent(Qt::Key_Backspace);
269 } else if (currentPreeditString != tempLastPre) {
270 //more than one character changed, so just renew everything
271 //delete all preedit
272 for (int i = 0; i < gLastPreeditString.length(); i++) {
273 sendPressReleaseKeyEvent(Qt::Key_Backspace);
275 //send new Preedit
276 const QChar * text = currentPreeditString.unicode();
277 for (int i = 0; i < currentPreeditString.length(); i++) {
278 sendPressReleaseKeyEvent(0, &text[i]);
282 } else if (gLastPreeditString != currentCommitString) {
283 //User commited something
284 if (currentCommitString.length() == 1 && gLastPreeditString.isEmpty()) {
285 //if commit string ist one and there is no Preedit String
286 //case i.e. when no error correction is enabled in the system (default meego.com)
287 sendPressReleaseKeyEvent(0, currentCommitString.unicode());
288 } else {
289 //There is a Preedit, first remove it
290 for (int i = 0; i < gLastPreeditString.length(); i++) {
291 sendPressReleaseKeyEvent(Qt::Key_Backspace);
293 //Now push commited String into
294 const QChar * text = currentCommitString.unicode();
295 for (int i = 0; i < currentCommitString.length(); i++) {
296 sendPressReleaseKeyEvent(0, &text[i]);
301 //save preedit for next round.
302 gLastPreeditString = currentPreeditString;
304 //pre edit is continues string of new chars pressed by the user.
305 //if pre edit is changing rapidly without commit string first then user choose some overed text
306 //if commitstring comes directly after, forget about it
307 QGraphicsWidget::inputMethodEvent(aEvent);
310 void MozQWidget::sendPressReleaseKeyEvent(int key,
311 const QChar* letter,
312 bool autorep,
313 ushort count)
315 Qt::KeyboardModifiers modifiers = Qt::NoModifier;
316 if (letter && letter->isUpper()) {
317 modifiers = Qt::ShiftModifier;
320 if (letter) {
321 // Handle as TextEvent
322 nsCompositionEvent start(true, NS_COMPOSITION_START, mReceiver);
323 mReceiver->DispatchEvent(&start);
325 nsTextEvent text(true, NS_TEXT_TEXT, mReceiver);
326 QString commitString = QString(*letter);
327 text.theText.Assign(commitString.utf16());
328 mReceiver->DispatchEvent(&text);
330 nsCompositionEvent end(true, NS_COMPOSITION_END, mReceiver);
331 mReceiver->DispatchEvent(&end);
332 return;
335 QKeyEvent press(QEvent::KeyPress, key, modifiers, QString(), autorep, count);
336 mReceiver->OnKeyPressEvent(&press);
337 QKeyEvent release(QEvent::KeyRelease, key, modifiers, QString(), autorep, count);
338 mReceiver->OnKeyReleaseEvent(&release);
341 void MozQWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* aEvent)
343 // Qt sends double click event, but not second press event.
344 mReceiver->OnButtonPressEvent(aEvent);
345 mReceiver->OnMouseDoubleClickEvent(aEvent);
348 void MozQWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* aEvent)
350 mReceiver->OnMotionNotifyEvent(aEvent->pos(), aEvent->modifiers());
353 void MozQWidget::mousePressEvent(QGraphicsSceneMouseEvent* aEvent)
355 mReceiver->OnButtonPressEvent(aEvent);
358 void MozQWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent* aEvent)
360 mReceiver->OnButtonReleaseEvent(aEvent);
363 bool MozQWidget::event ( QEvent * event )
365 // check receiver, since due to deleteLater() call it's possible, that
366 // events pass loop after receiver's destroy and while widget is still alive
367 if (!mReceiver)
368 return QGraphicsWidget::event(event);
370 #if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0))
371 switch (event->type())
373 case QEvent::TouchBegin:
374 case QEvent::TouchEnd:
375 case QEvent::TouchUpdate:
377 // Do not send this event to other handlers, this is needed
378 // to be able to receive the gesture events
379 bool handled = false;
380 mReceiver->OnTouchEvent(static_cast<QTouchEvent *>(event),handled);
381 return handled;
383 case (QEvent::Gesture):
385 bool handled = false;
386 mReceiver->OnGestureEvent(static_cast<QGestureEvent*>(event),handled);
387 return handled;
389 default:
390 break;
392 #endif
393 return QGraphicsWidget::event(event);
396 void MozQWidget::wheelEvent(QGraphicsSceneWheelEvent* aEvent)
398 mReceiver->OnScrollEvent(aEvent);
401 void MozQWidget::closeEvent(QCloseEvent* aEvent)
403 mReceiver->OnCloseEvent(aEvent);
406 void MozQWidget::hideEvent(QHideEvent* aEvent)
408 mReceiver->hideEvent(aEvent);
409 QGraphicsWidget::hideEvent(aEvent);
412 void MozQWidget::showEvent(QShowEvent* aEvent)
414 mReceiver->showEvent(aEvent);
415 QGraphicsWidget::showEvent(aEvent);
418 void MozQWidget::SetCursor(nsCursor aCursor)
420 Qt::CursorShape cursor = Qt::ArrowCursor;
421 switch(aCursor) {
422 case eCursor_standard:
423 cursor = Qt::ArrowCursor;
424 break;
425 case eCursor_wait:
426 cursor = Qt::WaitCursor;
427 break;
428 case eCursor_select:
429 cursor = Qt::IBeamCursor;
430 break;
431 case eCursor_hyperlink:
432 cursor = Qt::PointingHandCursor;
433 break;
434 case eCursor_ew_resize:
435 cursor = Qt::SplitHCursor;
436 break;
437 case eCursor_ns_resize:
438 cursor = Qt::SplitVCursor;
439 break;
440 case eCursor_nw_resize:
441 case eCursor_se_resize:
442 cursor = Qt::SizeBDiagCursor;
443 break;
444 case eCursor_ne_resize:
445 case eCursor_sw_resize:
446 cursor = Qt::SizeFDiagCursor;
447 break;
448 case eCursor_crosshair:
449 case eCursor_move:
450 cursor = Qt::SizeAllCursor;
451 break;
452 case eCursor_help:
453 cursor = Qt::WhatsThisCursor;
454 break;
455 case eCursor_copy:
456 case eCursor_alias:
457 break;
458 case eCursor_context_menu:
459 case eCursor_cell:
460 case eCursor_grab:
461 case eCursor_grabbing:
462 case eCursor_spinning:
463 case eCursor_zoom_in:
464 case eCursor_zoom_out:
466 default:
467 break;
470 setCursor(cursor);
473 void MozQWidget::SetCursor(const QPixmap& aCursor, int aHotX, int aHotY)
475 QCursor bitmapCursor(aCursor, aHotX, aHotY);
476 setCursor(bitmapCursor);
479 void MozQWidget::setModal(bool modal)
481 #if QT_VERSION >= 0x040600
482 setPanelModality(modal ? QGraphicsItem::SceneModal : QGraphicsItem::NonModal);
483 #else
484 LOG(("Modal QGraphicsWidgets not supported in Qt < 4.6\n"));
485 #endif
488 QVariant MozQWidget::inputMethodQuery(Qt::InputMethodQuery aQuery) const
490 // Additional MeeGo Touch queries, which do not depend on actually
491 // having a focused input field.
492 switch ((int) aQuery) {
493 case 10001: // VisualizationPriorityQuery.
494 // Tells if input method widget wants to have high priority
495 // for visualization. Input methods should honor this and stay
496 // out of widgets space.
497 // Return false, eg. the input method can overlap QGraphicsWKView.
498 return QVariant(false);
499 case 10003: // ImCorrectionEnabledQuery.
500 // Explicit correction enabling for text entries.
501 return QVariant(false);
502 case 10004: // ImModeQuery.
503 // Retrieval mode: normal (0), direct [minics hardware keyboard] (1) or proxy (2)
504 return QVariant::fromValue(0);
505 case 10006: // InputMethodToolbarQuery.
506 // Custom toolbar file name for text entry.
507 return QVariant();
510 // Standard Qt queries dependent on having a focused web text input.
511 switch (aQuery) {
512 case Qt::ImFont:
513 return QVariant(QFont());
514 case Qt::ImMaximumTextLength:
515 return QVariant(); // Means no limit.
518 // Additional MeeGo Touch queries dependent on having a focused web text input
519 switch ((int) aQuery) {
520 case 10002: // PreeditRectangleQuery.
521 // Retrieve bounding rectangle for current preedit text.
522 return QVariant(QRect());
525 return QVariant();
529 Request the VKB and starts a timer with the given timeout in milliseconds.
530 If the request is not canceled when the timer runs out, the VKB is actually
531 shown.
533 void MozQWidget::requestVKB(int aTimeout, QObject* aWidget)
535 if (!gPendingVKBOpen) {
536 gPendingVKBOpen = true;
538 if (aTimeout == 0 || !aWidget) {
539 showVKB();
540 } else {
541 QTimer::singleShot(aTimeout, aWidget, SLOT(showVKB()));
546 void MozQWidget::showVKB()
548 // skip showing of keyboard if not pending
549 if (!gPendingVKBOpen) {
550 return;
553 gPendingVKBOpen = false;
555 #if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) && (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
556 QWidget* focusWidget = qApp->focusWidget();
558 if (focusWidget) {
559 QInputContext *inputContext = qApp->inputContext();
560 if (!inputContext) {
561 NS_WARNING("Requesting SIP: but no input context");
562 return;
565 QEvent request(QEvent::RequestSoftwareInputPanel);
566 inputContext->filterEvent(&request);
567 focusWidget->setAttribute(Qt::WA_InputMethodEnabled, true);
568 inputContext->setFocusWidget(focusWidget);
569 gKeyboardOpen = true;
570 gFailedOpenKeyboard = false;
572 else
574 // No focused widget yet, so we have to open the VKB later on.
575 gFailedOpenKeyboard = true;
577 #else
578 LOG(("VKB not supported in Qt < 4.6\n"));
579 #endif
582 void MozQWidget::hideVKB()
584 if (gPendingVKBOpen) {
585 // do not really open
586 gPendingVKBOpen = false;
589 if (!gKeyboardOpen) {
590 return;
593 #if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)) && (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
594 QInputContext *inputContext = qApp->inputContext();
595 if (!inputContext) {
596 NS_WARNING("Closing SIP: but no input context");
597 return;
600 QEvent request(QEvent::CloseSoftwareInputPanel);
601 inputContext->filterEvent(&request);
602 inputContext->reset();
603 gKeyboardOpen = false;
604 #else
605 LOG(("VKB not supported in Qt < 4.6\n"));
606 #endif
609 bool MozQWidget::isVKBOpen()
611 return gKeyboardOpen;
614 void
615 MozQWidget::NotifyVKB(const QRect& rect)
617 QRegion region(scene()->views()[0]->rect());
618 region -= rect;
619 QRectF bounds = mapRectFromScene(region.boundingRect());
620 nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
621 if (observerService) {
622 QString rect = QString("{\"left\": %1, \"top\": %2, \"right\": %3, \"bottom\": %4}")
623 .arg(bounds.x()).arg(bounds.y()).arg(bounds.width()).arg(bounds.height());
624 observerService->NotifyObservers(nullptr, "softkb-change", rect.utf16());
628 void MozQWidget::SwitchToForeground()
630 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
631 if (!os)
632 return;
633 os->NotifyObservers(nullptr, "application-foreground", nullptr);
636 void MozQWidget::SwitchToBackground()
638 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
639 if (!os)
640 return;
641 os->NotifyObservers(nullptr, "application-background", nullptr);