6
6
* Contact: maliit-discuss@lists.maliit.org
8
* Copyright (C) 2012 Openismus GmbH
8
* Copyright (C) 2012 Canonical Ltd
10
10
* This library is free software; you can redistribute it and/or
11
11
* modify it under the terms of the GNU Lesser General Public
17
17
#include "mimpluginmanager.h"
18
18
#include "mimpluginmanager_p.h"
19
19
#include <maliit/plugins/inputmethodplugin.h>
20
#include <maliit/plugins/abstractpluginfactory.h>
21
20
#include "mattributeextensionmanager.h"
22
21
#include "msharedattributeextensionmanager.h"
23
22
#include <maliit/plugins/abstractinputmethod.h>
27
26
#include "mimsubviewoverride.h"
28
27
#include "maliit/namespaceinternal.h"
29
28
#include <maliit/settingdata.h>
29
#include "windowgroup.h"
31
#include <quick/inputmethodquickplugin.h>
32
34
#include <QPluginLoader>
33
35
#include <QSignalMapper>
34
#include <QGraphicsLinearLayout>
35
#include <QStandardItemModel>
36
36
#include <QWeakPointer>
44
43
const QString DefaultPluginLocation(MALIIT_PLUGINS_DIR);
45
const QString DefaultFactoryPluginLocation(MALIIT_FACTORY_PLUGINS_DIR);
47
45
const char * const VisualizationAttribute = "visualizationPriority";
48
46
const char * const FocusStateAttribute = "focusState";
50
48
const QString ConfigRoot = MALIIT_CONFIG_ROOT;
51
49
const QString MImPluginPaths = ConfigRoot + "paths";
52
50
const QString MImPluginDisabled = ConfigRoot + "disabledpluginfiles";
53
const QString MImPluginFactories = ConfigRoot + "factories";
55
52
const QString PluginRoot = MALIIT_CONFIG_ROOT"plugins";
56
53
const QString PluginSettings = MALIIT_CONFIG_ROOT"pluginsettings";
57
54
const QString MImAccesoryEnabled = MALIIT_CONFIG_ROOT"accessoryenabled";
59
const int MaxPluginHideTransitionTime(2*1000);
61
56
const char * const InputMethodItem = "inputMethod";
62
57
const char * const LoadAll = "loadAll";
64
// this function is used to detect the file suffix used to associate with specific factory
65
static QString getFileMimeType(const QString& fileName)
67
QFileInfo fi(fileName);
72
60
MIMPluginManagerPrivate::MIMPluginManagerPrivate(const QSharedPointer<MInputContextConnection> &connection,
73
const QSharedPointer<AbstractSurfaceGroupFactory> &surfaceGroupFactory,
61
const QSharedPointer<Maliit::AbstractPlatform> &platform,
74
62
MIMPluginManager *p)
76
64
mICConnection(connection),
77
65
imAccessoryEnabledConf(0),
79
acceptRegionUpdates(false),
83
mSurfaceGroupFactory(surfaceGroupFactory),
84
69
lastOrientation(0),
85
70
attributeExtensionManager(new MAttributeExtensionManager),
86
sharedAttributeExtensionManager(new MSharedAttributeExtensionManager)
71
sharedAttributeExtensionManager(new MSharedAttributeExtensionManager),
88
74
inputSourceToNameMap[Maliit::Hardware] = "hardware";
89
75
inputSourceToNameMap[Maliit::Accessory] = "accessory";
91
ensureEmptyRegionWhenHiddenTimer.setSingleShot(true);
92
ensureEmptyRegionWhenHiddenTimer.setInterval(MaxPluginHideTransitionTime);
93
QObject::connect(&ensureEmptyRegionWhenHiddenTimer, SIGNAL(timeout()),
94
parent, SLOT(_q_ensureEmptyRegionWhenHidden()));
100
81
qDeleteAll(handlerToPluginConfs);
103
void MIMPluginManagerPrivate::autoDetectEnabledSubViews(const QString &plugin)
105
QList<MImOnScreenPlugins::SubView> to_enable;
107
// Try to auto-detect subviews for the selected plugin by looking for
108
// subviews that coincide with the languages selected for use on the
110
// FIXME: This works for the keyboard plugin, but won't work everywhere.
111
// The methodology for auto-configuring subviews should be somehow
113
QStringList langs = QLocale::system().uiLanguages();
114
Q_FOREACH (QString lang, langs) {
115
// Convert to lower case, remove any .utf8 suffix, and use _ as
116
// the separator between language and country.
117
lang = lang.split('.')[0].toLower().replace("-", "_");
119
MImOnScreenPlugins::SubView subView(plugin, lang);
121
// First try the language code as-is
122
if (onScreenPlugins.isSubViewAvailable(subView) && !to_enable.contains(subView)) {
123
to_enable << subView;
127
// See if we get a match if we expand "de" to "de_de"
128
if (!lang.contains('_')) {
129
subView.id = lang + "_" + lang;
130
if (onScreenPlugins.isSubViewAvailable(subView) && !to_enable.contains(subView)) {
131
to_enable << subView;
136
// See if we get a match if we trim "de_at" to "de"
137
subView.id = lang.split("_").first();
138
if (onScreenPlugins.isSubViewAvailable(subView) && !to_enable.contains(subView)) {
139
to_enable << subView;
143
if (!to_enable.isEmpty()) {
144
onScreenPlugins.setAutoEnabledSubViews(to_enable);
148
84
void MIMPluginManagerPrivate::loadPlugins()
150
86
Q_Q(MIMPluginManager);
152
88
MImOnScreenPlugins::SubView activeSubView = onScreenPlugins.activeSubView();
155
const QDir &dir(DefaultFactoryPluginLocation);
156
Q_FOREACH (QString factoryName, dir.entryList(QDir::Files))
157
loadFactoryPlugin(dir, factoryName);
159
90
// Load active plugin first
160
91
Q_FOREACH (QString path, paths) {
161
92
const QDir &dir(path);
185
116
const QList<MImOnScreenPlugins::SubView> &availableSubViews = availablePluginsAndSubViews();
186
117
onScreenPlugins.updateAvailableSubViews(availableSubViews);
188
// If no subviews are enabled by the configuration, try to auto-detect
190
if (onScreenPlugins.enabledSubViews().empty()) {
191
autoDetectEnabledSubViews(activeSubView.plugin);
194
// If we still don't have an enabled subview, enable the first available
196
if (onScreenPlugins.enabledSubViews().empty()) {
197
MImOnScreenPlugins::SubView subView = availableSubViews.first();
198
onScreenPlugins.setAutoEnabledSubViews(QList<MImOnScreenPlugins::SubView>() << subView);
201
// If we have an active subview in the configuration, check that it is
202
// enabled. If it's not, drop the active subview.
203
if (!activeSubView.id.isEmpty() && !onScreenPlugins.isSubViewEnabled(activeSubView)) {
204
activeSubView.id = "";
207
// If we don't have an active subview, auto-activate the first enabled
209
if (activeSubView.id.isEmpty()) {
210
MImOnScreenPlugins::SubView subView = onScreenPlugins.enabledSubViews().first();
211
onScreenPlugins.setAutoActiveSubView(subView);
214
119
Q_EMIT q->pluginsChanged();
217
bool MIMPluginManagerPrivate::loadFactoryPlugin(const QDir &dir, const QString &fileName)
219
if (blacklist.contains(fileName)) {
220
qWarning() << __PRETTY_FUNCTION__ << fileName << "is on the blacklist, skipped.";
224
// TODO: skip already loaded plugin ids (fileName)
225
QPluginLoader load(dir.absoluteFilePath(fileName));
227
QObject *pluginInstance = load.instance();
228
if (!pluginInstance) {
229
qWarning() << __PRETTY_FUNCTION__
230
<< "Error loading factory plugin from" << dir.absoluteFilePath(fileName) << load.errorString();
234
// check if the plugin is a factory
235
MImAbstractPluginFactory *factory = qobject_cast<MImAbstractPluginFactory *>(pluginInstance);
237
qWarning() << __PRETTY_FUNCTION__
238
<< "Could not cast" << pluginInstance->metaObject()->className() << "into MImAbstractPluginFactory.";
241
factories.insert(factory->fileExtension(), factory);
245
122
bool MIMPluginManagerPrivate::loadPlugin(const QDir &dir, const QString &fileName)
247
124
Q_Q(MIMPluginManager);
254
131
Maliit::Plugins::InputMethodPlugin *plugin = 0;
256
QSharedPointer<AbstractSurfaceGroup> surfaceGroup(mSurfaceGroupFactory->createSurfaceGroup());
258
// Check if we have a specific factory for this plugin
259
QString mimeType = getFileMimeType(fileName);
260
if (factories.contains(mimeType)) {
261
plugin = factories[mimeType]->create(dir.filePath(fileName));
133
if (QFileInfo(fileName).suffix() == "qml") {
134
plugin = new Maliit::InputMethodQuickPlugin(dir.filePath(fileName), m_platform);
263
136
qWarning() << __PRETTY_FUNCTION__
264
137
<< "Could not create a plugin for: " << fileName;
291
MInputMethodHost *host = new MInputMethodHost(mICConnection, q, indicatorService, surfaceGroup->factory(),
164
QSharedPointer<Maliit::WindowGroup> windowGroup(new Maliit::WindowGroup(m_platform));
165
MInputMethodHost *host = new MInputMethodHost(mICConnection, q, windowGroup,
292
166
fileName, plugin->name());
294
168
MAbstractInputMethod *im = plugin->createInputMethod(host);
306
180
PluginDescription desc = { im, host, PluginState(),
307
Maliit::SwitchUndefined, fileName, surfaceGroup };
181
Maliit::SwitchUndefined, fileName, windowGroup };
309
183
// Connect surface group signals
310
QObject::connect(surfaceGroup.data(), SIGNAL(inputMethodAreaChanged(QRegion)),
184
QObject::connect(windowGroup.data(), SIGNAL(inputMethodAreaChanged(QRegion)),
311
185
mICConnection.data(), SLOT(updateInputMethodArea(QRegion)));
313
187
plugins.insert(plugin, desc);
1097
void MIMPluginManagerPrivate::_q_ensureEmptyRegionWhenHidden()
1099
Q_Q(MIMPluginManager);
1100
// Do not accept region updates from hidden plugins. Emit an empty region
1101
// update, because we cannot trust that a plugin sends a region update
1102
// after it's hidden.
1103
acceptRegionUpdates = false;
1104
Q_EMIT q->regionUpdated(QRegion());
1107
970
void MIMPluginManagerPrivate::showActivePlugins()
1109
ensureEmptyRegionWhenHiddenTimer.stop();
1110
acceptRegionUpdates = true;
1113
973
ensureActivePluginsVisible(ShowInputMethod);
1118
978
visible = false;
1119
979
Q_FOREACH (Maliit::Plugins::InputMethodPlugin *plugin, activePlugins) {
1120
980
plugins.value(plugin).inputMethod->hide();
1121
plugins.value(plugin).surfaceGroup->deactivate();
981
plugins.value(plugin).windowGroup->deactivate(Maliit::WindowGroup::HideDelayed);
1124
ensureEmptyRegionWhenHiddenTimer.start();
1127
985
void MIMPluginManagerPrivate::ensureActivePluginsVisible(ShowInputMethodRequest request)
1131
989
for (; iterator != plugins.end(); ++iterator) {
1132
990
if (activePlugins.contains(iterator.key())) {
1133
iterator.value().surfaceGroup->activate();
991
iterator.value().windowGroup->activate();
1134
992
if (request == ShowInputMethod) {
1135
993
iterator.value().inputMethod->show();
1138
iterator.value().surfaceGroup->deactivate();
996
iterator.value().windowGroup->deactivate(Maliit::WindowGroup::HideImmediate);
1232
1090
// actual class
1234
1092
MIMPluginManager::MIMPluginManager(const QSharedPointer<MInputContextConnection>& icConnection,
1235
const QSharedPointer<AbstractSurfaceGroupFactory>& surfacesFactory)
1093
const QSharedPointer<Maliit::AbstractPlatform> &platform)
1237
d_ptr(new MIMPluginManagerPrivate(icConnection, surfacesFactory, this))
1095
d_ptr(new MIMPluginManagerPrivate(icConnection, platform, this))
1239
1097
Q_D(MIMPluginManager);
1240
1098
d->q_ptr = this;
1313
1171
connect(d->mICConnection.data(), SIGNAL(pluginSettingsRequested(int,QString)),
1314
1172
this, SLOT(pluginSettingsRequested(int,QString)));
1174
connect(d->mICConnection.data(), SIGNAL(focusChanged(WId)),
1175
this, SLOT(handleAppFocusChanged(WId)));
1316
1177
// Connect from MAttributeExtensionManager to our handlers
1317
1178
connect(d->attributeExtensionManager.data(), SIGNAL(attributeExtensionIdChanged(const MAttributeExtensionId &)),
1318
1179
this, SLOT(setToolbar(const MAttributeExtensionId &)));
1460
1321
d->onScreenPlugins.setAllSubViewsEnabled(enable);
1463
void MIMPluginManager::updateRegion(const QRegion ®ion)
1465
Q_D(MIMPluginManager);
1467
// Record input method object's region.
1468
d->activeImRegion = region;
1470
// Don't update region when no region updates from the plugin side are
1472
if (d->acceptRegionUpdates) {
1473
Q_EMIT regionUpdated(region);
1477
1324
void MIMPluginManager::setToolbar(const MAttributeExtensionId &id)
1479
1326
Q_D(MIMPluginManager);
1427
void MIMPluginManager::handleAppFocusChanged(WId id)
1429
Q_D(MIMPluginManager);
1431
MIMPluginManagerPrivate::Plugins::iterator i = d->plugins.begin();
1432
while (i != d->plugins.end()) {
1433
i.value().windowGroup.data()->setApplicationWindow(id);
1580
1438
void MIMPluginManager::handleClientChange()
1582
1440
// notify plugins