1
/* * This file is part of Maliit framework *
3
* Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
6
* Contact: maliit-discuss@lists.maliit.org
8
* This library is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU Lesser General Public
10
* License version 2.1 as published by the Free Software Foundation
11
* and appearing in the file LICENSE.LGPL included in the packaging
15
#include "minputmethodquick.h"
17
#include "mkeyoverridequick.h"
18
#include "minputmethodquickplugin.h"
20
#include <maliit/plugins/abstractinputmethodhost.h>
21
#include <maliit/plugins/abstractsurfacefactory.h>
22
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
23
#include <maliit/plugins/quickviewsurface.h>
26
#include <qpa/qplatformnativeinterface.h>
28
#include <xcb/xfixes.h>
30
#include <maliit/plugins/abstractwidgetssurface.h>
32
#include <maliit/plugins/keyoverride.h>
35
#include <QApplication>
36
#include <QDesktopWidget>
43
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
44
#include <QQmlComponent>
45
#include <QQmlContext>
48
#include <QDeclarativeComponent>
49
#include <QDeclarativeContext>
50
#include <QDeclarativeEngine>
52
#include <QGraphicsTextItem>
53
#include <QGraphicsScene>
54
#include <QGraphicsObject>
58
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
62
#include <X11/Xatom.h>
63
#include <X11/Xutil.h>
64
#include <X11/extensions/Xfixes.h>
65
#include <X11/extensions/shape.h>
69
//this hack is needed because KeyPress definded by xlib conflicts with QEvent::KeyPress, breaking build
74
const char * const actionKeyName = "actionKey";
76
const QRect &computeDisplayRect(QWidget *w = 0)
78
static const QRect displayRect(w ? qApp->desktop()->screenGeometry(w)
79
: qApp->desktop()->screenGeometry());
85
// TODO: Remove typedefs for Maliit 1.0 and only use the Qt5 types instead.
86
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
87
typedef QQmlEngine MaliitQmlEngine;
88
typedef QQmlComponent MaliitQmlComponent;
90
typedef QDeclarativeEngine MaliitQmlEngine;
91
typedef QDeclarativeComponent MaliitQmlComponent;
94
//! Helper class to load QML files and set up the declarative view accordingly.
95
class MInputMethodQuickLoader
98
QGraphicsScene *const m_scene;
99
MaliitQmlEngine *const m_engine; //!< managed by controller
100
std::auto_ptr<MaliitQmlComponent> m_component;
102
QGraphicsObject *m_content; //!< managed by scene
103
MInputMethodQuick *const m_controller;
106
MInputMethodQuickLoader(QGraphicsScene *newScene,
107
MInputMethodQuick *newController)
109
, m_engine(new MaliitQmlEngine(newController))
111
, m_controller(newController)
114
Q_ASSERT(m_controller);
116
m_engine->rootContext()->setContextProperty("MInputMethodQuick", m_controller);
117
m_engine->addImportPath(MALIIT_PLUGINS_DATA_DIR);
119
Q_FOREACH (const QString &path, MInputMethodQuickPlugin::qmlImportPaths()) {
120
m_engine->addImportPath(path);
123
// Assuming that plugin B loads after plugin A, we need to make sure
124
// that plugin B does not use the customized import paths of plugin A:
125
MInputMethodQuickPlugin::setQmlImportPaths(QStringList());
128
virtual ~MInputMethodQuickLoader()
131
// TODO: rename to showContent?
135
qWarning() << __PRETTY_FUNCTION__
136
<< "Content or controller missing: Cannot show UI.";
140
m_controller->setActive(true);
149
m_controller->setActive(false);
152
void loadQmlFile(const QString &qmlFileName)
155
qWarning() << "Qml file already loaded";
159
m_component.reset(new MaliitQmlComponent(m_engine, QUrl(qmlFileName)));
161
if (not m_component->errors().isEmpty()) {
162
qWarning() << "QML errors while loading " << qmlFileName << "\n"
163
<< m_component->errors();
166
m_content = qobject_cast<QGraphicsObject *>(m_component->create());
169
m_content = new QGraphicsTextItem("Error loading QML");
173
m_scene->addItem(m_content);
176
QGraphicsObject *content() const
182
class MInputMethodQuickPrivate
185
MInputMethodQuick *const q_ptr;
186
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
187
QSharedPointer<Maliit::Plugins::QuickViewSurface> surface;
189
QSharedPointer<Maliit::Plugins::AbstractGraphicsViewSurface> surface;
190
QGraphicsScene *const scene;
191
QGraphicsView *const view;
192
MInputMethodQuickLoader *const loader;
194
QRect inputMethodArea;
198
//! current active state
199
Maliit::HandlerState activeState;
201
//! In practice show() and hide() correspond to application SIP (close)
202
//! requests. We track the current shown/SIP requested state using these variables.
205
QSharedPointer<MKeyOverrideQuick> actionKeyOverride;
206
QSharedPointer<MKeyOverride> sentActionKeyOverride;
209
Q_DECLARE_PUBLIC(MInputMethodQuick)
211
MInputMethodQuickPrivate(MAbstractInputMethodHost *host,
212
MInputMethodQuick *im)
214
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
215
, surface(qSharedPointerDynamicCast<Maliit::Plugins::QuickViewSurface>(host->surfaceFactory()->create(Maliit::Plugins::AbstractSurface::PositionCenterBottom | Maliit::Plugins::AbstractSurface::TypeQuick2)))
217
, surface(qSharedPointerDynamicCast<Maliit::Plugins::AbstractGraphicsViewSurface>(host->surfaceFactory()->create(Maliit::Plugins::AbstractSurface::PositionCenterBottom | Maliit::Plugins::AbstractSurface::TypeGraphicsView)))
218
, scene(surface->scene())
219
, view(surface->view())
220
, loader(new MInputMethodQuickLoader(scene, im))
224
, activeState(Maliit::OnScreen)
225
, sipRequested(false)
226
, sipIsInhibited(false)
227
, actionKeyOverride(new MKeyOverrideQuick())
228
, sentActionKeyOverride()
233
updateActionKey(MKeyOverride::All);
234
// Set surface size to fullscreen
235
surface->setSize(QApplication::desktop()->screenGeometry().size());
236
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
237
surface->view()->engine()->addImportPath(MALIIT_PLUGINS_DATA_DIR);
238
surface->view()->engine()->rootContext()->setContextProperty("MInputMethodQuick", im);
242
~MInputMethodQuickPrivate()
244
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
249
void handleInputMethodAreaUpdate(MAbstractInputMethodHost *host,
250
const QRegion ®ion)
256
host->setInputMethodArea(region);
259
void updateActionKey (const MKeyOverride::KeyOverrideAttributes changedAttributes)
261
actionKeyOverride->applyOverride(sentActionKeyOverride, changedAttributes);
264
void syncInputMask ()
266
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
267
#if defined(Q_WS_X11)
268
if (!view->effectiveWinId())
273
XRectangle * const rects = new XRectangle[size];
275
quint32 customRegion[size * 4]; // custom region is pack of x, y, w, h
276
rects[0].x = inputMethodArea.x();
277
rects[0].y = inputMethodArea.y();
278
rects[0].width = inputMethodArea.width();
279
rects[0].height = inputMethodArea.height();
280
customRegion[0] = inputMethodArea.x();
281
customRegion[1] = inputMethodArea.y();
282
customRegion[2] = inputMethodArea.width();
283
customRegion[3] = inputMethodArea.height();
286
const XserverRegion shapeRegion = XFixesCreateRegion(QX11Info::display(), rects, size);
287
XFixesSetWindowShapeRegion(QX11Info::display(), view->effectiveWinId(), ShapeBounding, 0, 0, 0);
288
XFixesSetWindowShapeRegion(QX11Info::display(), view->effectiveWinId(), ShapeInput, 0, 0, shapeRegion);
290
XFixesDestroyRegion(QX11Info::display(), shapeRegion);
292
XChangeProperty(QX11Info::display(), view->effectiveWinId(),
293
XInternAtom(QX11Info::display(), "_MEEGOTOUCH_CUSTOM_REGION", False),
294
XA_CARDINAL, 32, PropModeReplace,
295
(unsigned char *) customRegion, size * 4);
300
if (QGuiApplication::platformName() != "xcb")
303
QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
304
xcb_connection_t *connection = static_cast<xcb_connection_t *>(nativeInterface->nativeResourceForWindow("connection", surface->view()));
306
xcb_rectangle_t rect;
307
rect.x = inputMethodArea.x();
308
rect.y = inputMethodArea.y();
309
rect.width = inputMethodArea.width();
310
rect.height = inputMethodArea.height();
312
xcb_xfixes_region_t region = xcb_generate_id(connection);
313
xcb_xfixes_create_region(connection, region, 1, &rect);
315
xcb_window_t window = surface->view()->winId();
316
xcb_xfixes_set_window_shape_region(connection, window, XCB_SHAPE_SK_BOUNDING, 0, 0, 0);
317
xcb_xfixes_set_window_shape_region(connection, window, XCB_SHAPE_SK_INPUT, 0, 0, region);
319
xcb_xfixes_destroy_region(connection, region);
324
MInputMethodQuick::MInputMethodQuick(MAbstractInputMethodHost *host,
325
const QString &qmlFileName)
326
: MAbstractInputMethod(host)
327
, d_ptr(new MInputMethodQuickPrivate(host, this))
329
Q_D(MInputMethodQuick);
331
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
332
d->surface->view()->setSource(QUrl::fromLocalFile(qmlFileName));
334
d->loader->loadQmlFile(qmlFileName);
337
propagateScreenSize();
340
MInputMethodQuick::~MInputMethodQuick()
343
void MInputMethodQuick::handleFocusChange(bool focusIn)
345
Q_D(MInputMethodQuick);
346
d->haveFocus = focusIn;
349
void MInputMethodQuick::show()
351
Q_D(MInputMethodQuick);
352
d->sipRequested = true;
353
if (d->sipIsInhibited) {
357
handleAppOrientationChanged(d->appOrientation);
359
if (d->activeState == Maliit::OnScreen) {
361
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
370
void MInputMethodQuick::hide()
372
Q_D(MInputMethodQuick);
373
if (!d->sipRequested) {
376
d->sipRequested = false;
377
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
384
d->handleInputMethodAreaUpdate(inputMethodHost(), r);
387
void MInputMethodQuick::handleAppOrientationChanged(int angle)
389
Q_D(MInputMethodQuick);
391
MAbstractInputMethod::handleAppOrientationChanged(angle);
393
if (d->appOrientation != angle) {
395
d->appOrientation = angle;
396
// When emitted, QML Plugin will realice a state
397
// change and update InputMethodArea. Don't propagate those changes except if
398
// VkB is currently showed
399
Q_EMIT appOrientationChanged(d->appOrientation);
400
if (d->sipRequested && !d->sipIsInhibited) {
401
d->handleInputMethodAreaUpdate(inputMethodHost(), inputMethodArea().toRect());
406
void MInputMethodQuick::setState(const QSet<Maliit::HandlerState> &state)
408
Q_D(MInputMethodQuick);
410
if (state.isEmpty()) {
414
if (state.contains(Maliit::OnScreen)) {
415
d->activeState = Maliit::OnScreen;
416
if (d->sipRequested && !d->sipIsInhibited) {
417
show(); // Force reparent of client widgets.
420
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
425
// Allow client to make use of InputMethodArea
427
d->handleInputMethodAreaUpdate(inputMethodHost(), r);
428
d->activeState = *state.begin();
432
void MInputMethodQuick::handleClientChange()
434
Q_D(MInputMethodQuick);
436
if (d->sipRequested) {
437
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
445
void MInputMethodQuick::handleVisualizationPriorityChange(bool inhibitShow)
447
Q_D(MInputMethodQuick);
449
if (d->sipIsInhibited == inhibitShow) {
452
d->sipIsInhibited = inhibitShow;
454
if (d->sipRequested) {
456
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
462
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
472
void MInputMethodQuick::propagateScreenSize()
474
Q_EMIT screenWidthChanged(computeDisplayRect().width());
475
Q_EMIT screenHeightChanged(computeDisplayRect().height());
478
int MInputMethodQuick::screenHeight() const
480
return computeDisplayRect().height();
483
int MInputMethodQuick::screenWidth() const
485
return computeDisplayRect().width();
488
int MInputMethodQuick::appOrientation() const
490
Q_D(const MInputMethodQuick);
491
return d->appOrientation;
494
QRectF MInputMethodQuick::inputMethodArea() const
496
Q_D(const MInputMethodQuick);
497
return d->inputMethodArea;
500
void MInputMethodQuick::setInputMethodArea(const QRectF &area)
502
Q_D(MInputMethodQuick);
504
if (d->inputMethodArea != area.toRect()) {
505
d->inputMethodArea = area.toRect();
506
d->handleInputMethodAreaUpdate(inputMethodHost(), d->inputMethodArea);
508
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
509
qDebug() << __PRETTY_FUNCTION__ << "QWidget::effectiveWinId(): " << d_ptr->view->effectiveWinId();
512
Q_EMIT inputMethodAreaChanged(d->inputMethodArea);
517
void MInputMethodQuick::setScreenRegion(const QRect ®ion)
519
inputMethodHost()->setScreenRegion(region);
522
void MInputMethodQuick::sendPreedit(const QString &text)
524
QList<Maliit::PreeditTextFormat> lst;
525
inputMethodHost()->sendPreeditString(text, lst, text.length());
528
void MInputMethodQuick::sendKey(int key, int modifiers, const QString &text)
530
QKeyEvent event(QEvent::KeyPress, key, (~(Qt::KeyboardModifiers(Qt::NoModifier))) & modifiers, text);
531
inputMethodHost()->sendKeyEvent(event);
534
void MInputMethodQuick::sendCommit(const QString &text)
537
QKeyEvent event(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier);
538
inputMethodHost()->sendKeyEvent(event);
540
if ((text == "\r\n") || (text == "\n") || (text == "\r")) {
541
QKeyEvent event(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier);
542
inputMethodHost()->sendKeyEvent(event);
544
inputMethodHost()->sendCommitString(text);
548
void MInputMethodQuick::pluginSwitchRequired(int switchDirection)
550
inputMethodHost()->switchPlugin(
551
static_cast<Maliit::SwitchDirection>(switchDirection));
554
void MInputMethodQuick::userHide()
557
inputMethodHost()->notifyImInitiatedHiding();
560
void MInputMethodQuick::setKeyOverrides(const QMap<QString, QSharedPointer<MKeyOverride> > &overrides)
562
Q_D(MInputMethodQuick);
563
const QMap<QString, QSharedPointer<MKeyOverride> >::const_iterator iter(overrides.find(actionKeyName));
565
if (d->sentActionKeyOverride) {
566
disconnect(d->sentActionKeyOverride.data(), SIGNAL(keyAttributesChanged(const QString &, const MKeyOverride::KeyOverrideAttributes)),
567
this, SLOT(onSentActionKeyAttributesChanged(const QString &, const MKeyOverride::KeyOverrideAttributes)));
568
d->sentActionKeyOverride.clear();
571
if (iter != overrides.end()) {
572
QSharedPointer<MKeyOverride> sentActionKeyOverride(*iter);
574
if (sentActionKeyOverride) {
575
d->sentActionKeyOverride = sentActionKeyOverride;
576
connect(d->sentActionKeyOverride.data(), SIGNAL(keyAttributesChanged(const QString &, const MKeyOverride::KeyOverrideAttributes)),
577
this, SLOT(onSentActionKeyAttributesChanged(const QString &, const MKeyOverride::KeyOverrideAttributes)));
580
d->updateActionKey(MKeyOverride::All);
583
QList<MAbstractInputMethod::MInputMethodSubView> MInputMethodQuick::subViews(Maliit::HandlerState state) const
586
MAbstractInputMethod::MInputMethodSubView sub_view;
587
sub_view.subViewId = "";
588
sub_view.subViewTitle = "";
589
QList<MAbstractInputMethod::MInputMethodSubView> sub_views;
590
sub_views << sub_view;
594
void MInputMethodQuick::onSentActionKeyAttributesChanged(const QString &, const MKeyOverride::KeyOverrideAttributes changedAttributes)
596
Q_D(MInputMethodQuick);
598
d->updateActionKey(changedAttributes);
601
MKeyOverrideQuick* MInputMethodQuick::actionKeyOverride() const
603
Q_D(const MInputMethodQuick);
605
return d->actionKeyOverride.data();
608
void MInputMethodQuick::activateActionKey()
613
bool MInputMethodQuick::isActive() const
615
Q_D(const MInputMethodQuick);
619
void MInputMethodQuick::setActive(bool enable)
621
Q_D(MInputMethodQuick);
622
if (d->active != enable) {
624
Q_EMIT activeChanged();