diff -r 000000000000 -r 16d8024aca5e src/hbcore/inputfw/hbinputmodecache.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hbcore/inputfw/hbinputmodecache.cpp Mon Apr 19 14:02:13 2010 +0300 @@ -0,0 +1,463 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (developer.feedback@nokia.com) +** +** This file is part of the HbCore module of the UI Extensions for Mobile. +** +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at developer.feedback@nokia.com. +** +****************************************************************************/ +#include +#include +#include +#include +#include +#include + +#include "hbinputmodecache_p.h" +#include "hbinpututils.h" +#include "hbinputmethod.h" +#include "hbinputsettingproxy.h" +#include "hbinputmodeproperties.h" +#include "hbinputkeymapfactory.h" +#include "hbinputmethod_p.h" +#include "hbinputmethodnull_p.h" + +/*! +@alpha +@hbcore +\class HbInputModeCache +\brief Input framework's internal input mode resolver class. +*/ + +/// @cond + +class HbInputMethodListItem +{ +public: + HbInputMethodListItem() : cached(0), toBeRemoved(false) {} + bool operator==(const HbInputMethodListItem &item) const { + return (descriptor.pluginNameAndPath() == item.descriptor.pluginNameAndPath() && + descriptor.key() == item.descriptor.key()); + } + +public: + HbInputMethodDescriptor descriptor; + QStringList languages; + HbInputMethod *cached; + bool toBeRemoved; +}; + +class HbInputModeCachePrivate +{ +public: + HbInputModeCachePrivate() : mWatcher(new QFileSystemWatcher()), mShuttingDown(false) {} + ~HbInputModeCachePrivate() {} + void refresh(const QString &directory = QString()); + QInputContextPlugin *pluginInstance(const QString& pluginFileName) const; + HbInputMethod *methodInstance(const QString &pluginFileName, const QString &key) const; + HbInputModeProperties propertiesFromString(const QString &entry) const; + HbInputModeProperties propertiesFromState(const HbInputState &state) const; + HbInputMethod *cachedMethod(HbInputMethodListItem &item); + void updateMonitoredPaths(); + +public: + QFileSystemWatcher *mWatcher; + QList mMethods; + bool mShuttingDown; +}; + +QInputContextPlugin* HbInputModeCachePrivate::pluginInstance(const QString& pluginFileName) const +{ + if (QLibrary::isLibrary(pluginFileName)) { + QPluginLoader loader(pluginFileName); + QObject* plugin = loader.instance(); + if (plugin) { + return qobject_cast(plugin); + } + } + + return 0; +} + +HbInputMethod *HbInputModeCachePrivate::methodInstance(const QString &pluginFileName, const QString &key) const +{ + QInputContextPlugin *plugin = pluginInstance(pluginFileName); + if (plugin) { + QInputContext *instance = plugin->create(key); + HbInputMethod *result = qobject_cast(instance); + if (result) { + QStringList languages = plugin->languages(key); + QList modeList; + foreach (QString language, languages) { + modeList.append(propertiesFromString(language)); + } + result->d_ptr->mInputModes = modeList; + } + return result; + } + + return 0; +} + +void HbInputModeCachePrivate::refresh(const QString &directory) +{ + Q_UNUSED(directory); + // optimize later so that if the directory is given, only changes concerning + // it are taken into account. + + // First go through all the previously found entries and + // tag them not refreshed. + for (int k = 0; k < mMethods.count(); k++) { + mMethods[k].toBeRemoved = true; + } + + // Query plugin paths and scan the folders. + QStringList folders = HbInputSettingProxy::instance()->inputMethodPluginPaths(); + foreach (QString folder, folders) { + QDir dir(folder); + for (unsigned int i = 0; i < dir.count(); i++) { + QString path = QString(dir.absolutePath()); + if (path.right(1) != "\\" && path.right(1) != "/") { + path += QDir::separator(); + } + path += dir[i]; + QInputContextPlugin* inputContextPlugin = pluginInstance(path); + if (inputContextPlugin) { + HbInputMethodListItem listItem; + listItem.descriptor.setPluginNameAndPath(dir.absolutePath() + QDir::separator() + dir[i]); + + // For each found plugin, check if there is already a list item for it. + // If not, then add one. + QStringList contextKeys = inputContextPlugin->keys(); + foreach (QString key, contextKeys) { + listItem.descriptor.setKey(key); + listItem.descriptor.setDisplayName(inputContextPlugin->displayName(key)); + + int index = mMethods.indexOf(listItem); + if (index >= 0) { + // The method is already in the list, the situation hasn't changed. + // just tag it not to be removed. + mMethods[index].toBeRemoved = false; + } else { + listItem.languages = inputContextPlugin->languages(key); + mMethods.append(listItem); + } + } + } + } + } + + // Go through the cache list and find out if some of the previous items need to be + // removed after the refresh. + for (int i = 0; i < mMethods.count(); i++) { + if (mMethods[i].toBeRemoved) { + if (mMethods[i].cached && mMethods[i].cached->isActiveMethod()) { + // If the item to be removed happens to be the active one, + // try to deal with the situation. + mMethods[i].cached->forceUnfocus(); + if (mMethods[i].descriptor.pluginNameAndPath() == HbInputSettingProxy::instance()->activeCustomInputMethod().pluginNameAndPath()) { + // The active custom method is being removed. + // Clear out related setting proxy values. + HbInputSettingProxy::instance()->setActiveCustomInputMethod(HbInputMethodDescriptor()); + } + + // Replace it with null input context. + HbInputMethod *master = HbInputMethodNull::Instance(); + master->d_ptr->mIsActive = true; + QInputContext* proxy = master->d_ptr->newProxy(); + qApp->setInputContext(proxy); + } + delete mMethods[i].cached; + mMethods.removeAt(i); + i--; + } + } +} + +HbInputModeProperties HbInputModeCachePrivate::propertiesFromString(const QString &entry) const +{ + HbInputModeProperties result; + + QStringList parts = entry.split(" "); + if (parts.count() == 4) { + // See HbInputModeProperties::toString() for details, + QString languageStr = parts[0] + QString(" ") + parts[1]; + HbInputLanguage language; + language.fromString(languageStr); + result.setLanguage(language); + result.setInputMode((HbInputModeType)parts[2].toLong()); + result.setKeyboard((HbKeyboardType)parts[3].toLong()); + } + + return result; +} + +HbInputModeProperties HbInputModeCachePrivate::propertiesFromState(const HbInputState &state) const +{ + HbInputModeProperties result; + + result.setLanguage(state.language()); + result.setKeyboard(state.keyboard()); + result.setInputMode(state.inputMode()); + + return HbInputModeProperties(result); +} + +HbInputMethod *HbInputModeCachePrivate::cachedMethod(HbInputMethodListItem &item) +{ + if (!item.cached) { + item.cached = methodInstance(item.descriptor.pluginNameAndPath(), item.descriptor.key()); + } + + return item.cached; +} + +void HbInputModeCachePrivate::updateMonitoredPaths() +{ + QStringList watchedDirs = mWatcher->directories(); + if (!watchedDirs.isEmpty()) { + mWatcher->removePaths(watchedDirs); + } + + QStringList paths = HbInputSettingProxy::instance()->inputMethodPluginPaths(); + foreach (QString path, paths) { + QDir dir(path); + if (!dir.exists() && path.left(1) == "f") { + mWatcher->addPath(QString("f:") + QDir::separator()); + } else { + mWatcher->addPath(path); + } + } +} +/// @endcond + +/*! +Returns the singleton instance. +*/ +HbInputModeCache* HbInputModeCache::instance() +{ + static HbInputModeCache theCache; + return &theCache; +} + +/*! +Construct the object. +*/ +HbInputModeCache::HbInputModeCache() : d_ptr(new HbInputModeCachePrivate()) +{ + Q_D(HbInputModeCache); + + // Start to monitor file system for changes. + d->updateMonitoredPaths(); + connect(d->mWatcher, SIGNAL(directoryChanged(const QString &)), this, SLOT(directoryChanged(const QString &))); + + d->refresh(); +} + +/*! +Destruct the object. +*/ +HbInputModeCache::~HbInputModeCache() +{ + delete d_ptr; +} + +/*! +This slot is called whenever a change in input method plugin file system is detected and +the list needs to be refreshed. +*/ +void HbInputModeCache::directoryChanged(const QString &directory) +{ + Q_D(HbInputModeCache); + + d->updateMonitoredPaths(); + + if (!d->mShuttingDown) { + d->refresh(directory); + } +} + +/*! +Shuts down the object safely. This is needed mainly for singleton object. There has been a lot +of problems related to randown singleton desctruction order and additional shutdown step is +needed to guarantee that it will be done safely. The slot is connected to +QCoreApplication::aboutToQuit when the framework is initialized. +*/ +void HbInputModeCache::shutdown() +{ + Q_D(HbInputModeCache); + d->mShuttingDown = true; + + foreach (HbInputMethodListItem method, d->mMethods) { + delete method.cached; + method.cached = 0; + } + d->mMethods.clear(); + delete d->mWatcher; + d->mWatcher = 0; +} + +/*! +Loads given input method and caches it. +*/ +HbInputMethod* HbInputModeCache::loadInputMethod(const HbInputMethodDescriptor &inputMethod) +{ + Q_D(HbInputModeCache); + + for (int i = 0; i < d->mMethods.count(); i++) { + if (d->mMethods[i].descriptor.pluginNameAndPath() == inputMethod.pluginNameAndPath() && + d->mMethods[i].descriptor.key() == inputMethod.key()) { + if (!d->mMethods[i].cached) { + d->mMethods[i].cached = d->methodInstance(inputMethod.pluginNameAndPath(), inputMethod.key()); + } + + return d->mMethods[i].cached; + } + } + + return 0; +} + +/*! +Lists custom input methods. +*/ +QList HbInputModeCache::listCustomInputMethods() +{ + Q_D(HbInputModeCache); + + QList result; + + foreach (HbInputMethodListItem item, d->mMethods) { + foreach (QString language, item.languages) { + HbInputModeProperties properties = d->propertiesFromString(language); + if (properties.inputMode() == HbInputModeCustom) { + result.append(item.descriptor); + break; + } + } + } + + return result; +} + +/*! + Find correct handler for given input state. + */ +HbInputMethod* HbInputModeCache::findStateHandler(const HbInputState& state) +{ + Q_D(HbInputModeCache); + + HbInputModeProperties stateProperties = d->propertiesFromState(state); + int languageRangeIndex = -1; + + // First check if there is a method that matches excatly (ie. also specifies + // the language). + for (int i = 0; i < d->mMethods.count(); i++) { + foreach (QString language, d->mMethods[i].languages) { + HbInputModeProperties properties = d->propertiesFromString(language); + if (properties.language().undefined() && + properties.keyboard() == stateProperties.keyboard() && + properties.inputMode() == stateProperties.inputMode()) { + // Remember the index, we'll need this in the next phase if no exact + // match is found. + languageRangeIndex = i; + } + + if (properties.inputMode() != HbInputModeCustom) { + if (properties == stateProperties) { + return d->cachedMethod(d->mMethods[i]); + } + } + } + } + + // No exact match found. Then see if there was a method that matches to language + // range, meaning that the language is left unspecified in which case we'll + // use key mapping factory for matching. + if (languageRangeIndex >= 0) { + QList languages = HbKeymapFactory::instance()->availableLanguages(); + + foreach(HbInputLanguage language, languages) { + // exact match is returned If the country variant is specified in state language, + // otherwise a method that matches to only language range is returned. + bool exactMatchFound = (stateProperties.language().variant() != QLocale::AnyCountry) ? + (language == stateProperties.language()) : + (language.language() == stateProperties.language().language()); + if (exactMatchFound) { + return d->cachedMethod(d->mMethods[languageRangeIndex]); + } + } + } + + return 0; +} + +/*! + Returns the active input method. + + \sa HbInputMethod + */ +HbInputMethod* HbInputModeCache::activeMethod() const +{ + Q_D(const HbInputModeCache); + + foreach (HbInputMethodListItem item, d->mMethods) { + if (item.cached && item.cached->isActiveMethod()) { + return item.cached; + } + } + + return 0; +} + +/*! +Lists available input languages. +*/ +QList HbInputModeCache::listInputLanguages() const +{ + Q_D(const HbInputModeCache); + + QList result; + + foreach (HbInputMethodListItem item, d->mMethods) { + foreach (QString language, item.languages) { + HbInputModeProperties mode = d->propertiesFromString(language); + if (mode.inputMode() != HbInputModeCustom) { + if (mode.language().undefined()) { + // This is language range. Let's add everything + // we have key mappings for. + QList languages = HbKeymapFactory::instance()->availableLanguages(); + foreach (HbInputLanguage mappedLanguage, languages) { + if (!result.contains(mappedLanguage)) { + result.append(mappedLanguage); + } + } + } else { + if (!result.contains(mode.language())) { + result.append(mode.language()); + } + } + } + } + } + + return result; +} + +// End of file