diff -r 7516d6d86cf5 -r ed14f46c0e55 src/hbcore/inputfw/hbinputmodecache.cpp --- a/src/hbcore/inputfw/hbinputmodecache.cpp Mon Oct 04 17:49:30 2010 +0300 +++ b/src/hbcore/inputfw/hbinputmodecache.cpp Mon Oct 18 18:23:13 2010 +0300 @@ -23,6 +23,7 @@ ** ****************************************************************************/ #include "hbinputmodecache_p.h" +#include "hbinputmodecache_p_p.h" #include #include @@ -30,6 +31,9 @@ #include #include #include +#include +#include +#include #include "hbinpututils.h" #include "hbinputmethod.h" @@ -42,50 +46,217 @@ /*! @alpha -@hbcore \class HbInputModeCache \brief Input framework's internal input mode resolver class. */ /// @cond -class HbInputMethodListItem +void HbInputMethodListItem::setValues(QInputContextPlugin *plugin, const QString &key) +{ + if (plugin) { + descriptor.setKey(key); + descriptor.setDisplayName(plugin->displayName(key)); + + HbInputContextPlugin *extension = qobject_cast(plugin); + if (extension) { + descriptor.setDisplayNames(extension->displayNames(key)); + descriptor.setIcon(extension->icon(key)); + descriptor.setIcons(extension->icons(key)); + } + } +} + +QDataStream &operator<<(QDataStream &out, const HbInputMethodListItem &item) +{ + out << item.descriptor; + out << item.languages; + return out; +} + +QDataStream &operator>>(QDataStream &in, HbInputMethodListItem &item) +{ + in >> item.descriptor; + in >> item.languages; + item.cached = 0; + item.toBeRemoved = false; + return in; +} + +HbInputModeCachePrivate::HbInputModeCachePrivate() + : mSharedMethodList(0), + mMethodListModTime(0), + mMethodListLastUpdate(0), + mMethodsFetchedFromDisk(false), + mShuttingDown(false) { -public: - HbInputMethodListItem() : cached(0), toBeRemoved(false) {} - bool operator==(const HbInputMethodListItem &item) const { - return (descriptor.pluginNameAndPath() == item.descriptor.pluginNameAndPath() && - descriptor.key() == item.descriptor.key()); + mSharedMethodList = new QSharedMemory(HbInputMethodListKey); + // sharedMethodList is only attached when the list is updated + mMethodListModTime = new QSharedMemory(HbInputMethodListModTimeKey); + mMethodListModTime->attach(); +} + +HbInputModeCachePrivate::~HbInputModeCachePrivate() +{ + delete mSharedMethodList; + delete mMethodListModTime; +} + +void HbInputModeCachePrivate::refresh() +{ + // Shared memory data is used if available (checked every time we refresh) + // Otherwise the methods are read from disk, but just once during modecache lifetime + if (!readInputMethodDataFromSharedMemory() && !mMethodsFetchedFromDisk) { + readInputMethodDataFromDisk(&mMethods); + pruneRemovedMethods(); + mMethodsFetchedFromDisk = true; + } +} + +void HbInputModeCachePrivate::readInputMethodDataFromDisk(QList* methodList, const QDir &readPath) +{ + bool readFromSinglePath = (readPath != QDir()); + // First go through all the previously found entries and + // tag them not refreshed. In case a directory is defined, only marks entries + // in that directory + for (int i = 0; i < methodList->size(); ++i) { + if (readFromSinglePath) { + if (methodList->at(i).descriptor.pluginNameAndPath().left(methodList->at(i).descriptor.pluginNameAndPath().lastIndexOf(QDir::separator())) + == readPath.absolutePath()) { + (*methodList)[i].toBeRemoved = true; + } + } else { + (*methodList)[i].toBeRemoved = true; + } } -public: - HbInputMethodDescriptor descriptor; - QStringList languages; - HbInputMethod *cached; - bool toBeRemoved; -}; + // Query plugin paths and scan the folders. + QStringList folders = HbInputSettingProxy::instance()->inputMethodPluginPaths(); + foreach(const QString &folder, folders) { + QDir dir(folder); + if (!readFromSinglePath || readPath == dir) { + 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]); -class HbInputModeCachePrivate + // For each found plugin, check if there is already a list item for it. + // If not, then add one. + QStringList contextKeys = inputContextPlugin->keys(); + foreach(const QString &key, contextKeys) { + listItem.setValues(inputContextPlugin, key); + + int index = methodList->indexOf(listItem); + if (index >= 0) { + // The method is already in the list, the situation hasn't changed. + // just tag it not to be removed. + (*methodList)[index].toBeRemoved = false; + } else { + listItem.languages = inputContextPlugin->languages(key); + methodList->append(listItem); + } + } + } + } + } + } +} + +bool HbInputModeCachePrivate::readInputMethodDataFromSharedMemory() { -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(); - bool isMappedLanguage(const HbInputLanguage &language) const; + // Check if the shared list has been modified + if (!mMethodListModTime->isAttached()) { + // Shared memory is not attached + // Revert to in-process handling + return false; + } + // No locking, since in case the value is corrupt we just need to read the list again + // on the next run + uint *newModTime = static_cast(mMethodListModTime->data()); + if (*newModTime == mMethodListLastUpdate) { + // The internal list is still in sync with the one in shared memory + return true; + } + // Modifications done since last update, try to attach the method list + // Revert to in-process handling if not successful + if (!mSharedMethodList->attach()) { + return false; + } + // Attached successfully, update the modification time + mMethodListLastUpdate = *newModTime; + + // To start updating the list, first mark all methods for removal + for (int k = 0; k < mMethods.count(); k++) { + mMethods[k].toBeRemoved = true; + } + + // Get a copy of the list from shared memory + mSharedMethodList->lock(); + QByteArray array(static_cast(mSharedMethodList->data())+sizeof(int), *static_cast(mSharedMethodList->data())); + // array now has a copy of the data, so we can unlock and detach + mSharedMethodList->unlock(); + mSharedMethodList->detach(); + + // Next read the entries from the array to a temporary list + QDataStream in(&array, QIODevice::ReadOnly); + QList newMethodList; + in >> newMethodList; -public: - QFileSystemWatcher *mWatcher; - QList mMethods; - bool mShuttingDown; -}; + // Go through the temporary list and append new methods to internal list + // Duplicates are marked as not to be removed, the rest will be removed by pruneRemovedMethods + foreach (const HbInputMethodListItem& item, newMethodList) { + int index = mMethods.indexOf(item); + if (index >= 0) { + mMethods[index].toBeRemoved = false; + } else { + mMethods.append(item); + } + } + pruneRemovedMethods(); + return true; +} -QInputContextPlugin *HbInputModeCachePrivate::pluginInstance(const QString &pluginFileName) const +void HbInputModeCachePrivate::pruneRemovedMethods() +{ + // 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.at(i).toBeRemoved) { + if (mMethods.at(i).cached && mMethods.at(i).cached->isActiveMethod()) { + // If the item to be removed happens to be the active one, + // try to deal with the situation. + mMethods.at(i).cached->forceUnfocus(); + // The active custom method is being removed. + // Clear out related setting proxy values. + if (mMethods.at(i).descriptor.pluginNameAndPath() == HbInputSettingProxy::instance()->preferredInputMethod(Qt::Horizontal).pluginNameAndPath()) { + HbInputSettingProxy::instance()->setPreferredInputMethod(Qt::Horizontal, HbInputMethodDescriptor()); + } + if (mMethods.at(i).descriptor.pluginNameAndPath() == HbInputSettingProxy::instance()->preferredInputMethod(Qt::Vertical).pluginNameAndPath()) { + HbInputSettingProxy::instance()->setPreferredInputMethod(Qt::Vertical, HbInputMethodDescriptor()); + } + + // Replace it with null input context. + HbInputMethod *master = HbInputMethodNull::Instance(); + master->d_ptr->mIsActive = true; + QInputContext *proxy = master->d_ptr->proxy(); + if (proxy != qApp->inputContext()) { + qApp->setInputContext(proxy); + } + } + delete mMethods[i].cached; + mMethods.removeAt(i); + i--; + } + } +} + +QInputContextPlugin *HbInputModeCachePrivate::pluginInstance(const QString &pluginFileName) { if (QLibrary::isLibrary(pluginFileName)) { QPluginLoader loader(pluginFileName); @@ -108,7 +279,7 @@ QStringList languages = plugin->languages(key); QList modeList; foreach(const QString &language, languages) { - modeList.append(propertiesFromString(language)); + modeList.append(HbInputModeProperties::fromString(language)); } result->d_ptr->mInputModes = modeList; } @@ -118,122 +289,6 @@ 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(const 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(const QString &key, contextKeys) { - listItem.descriptor.setKey(key); - listItem.descriptor.setDisplayName(inputContextPlugin->displayName(key)); - - HbInputContextPlugin *extension = qobject_cast(inputContextPlugin); - if (extension) { - listItem.descriptor.setDisplayNames(extension->displayNames(key)); - listItem.descriptor.setIcon(extension->icon(key)); - listItem.descriptor.setIcons(extension->icons(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(); - // The active custom method is being removed. - // Clear out related setting proxy values. - if (mMethods[i].descriptor.pluginNameAndPath() == HbInputSettingProxy::instance()->preferredInputMethod(Qt::Horizontal).pluginNameAndPath()) { - HbInputSettingProxy::instance()->setPreferredInputMethod(Qt::Horizontal, HbInputMethodDescriptor()); - } - if (mMethods[i].descriptor.pluginNameAndPath() == HbInputSettingProxy::instance()->preferredInputMethod(Qt::Vertical).pluginNameAndPath()) { - HbInputSettingProxy::instance()->setPreferredInputMethod(Qt::Vertical, HbInputMethodDescriptor()); - } - - // Replace it with null input context. - HbInputMethod *master = HbInputMethodNull::Instance(); - master->d_ptr->mIsActive = true; - QInputContext *proxy = master->d_ptr->proxy(); - if (proxy != qApp->inputContext()) { - 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) { @@ -243,36 +298,9 @@ return item.cached; } -void HbInputModeCachePrivate::updateMonitoredPaths() -{ - QStringList watchedDirs = mWatcher->directories(); - if (!watchedDirs.isEmpty()) { - mWatcher->removePaths(watchedDirs); - } - - QStringList paths = HbInputSettingProxy::instance()->inputMethodPluginPaths(); - foreach(const QString &path, paths) { - QDir dir(path); - if (!dir.exists() && path.left(1) == "f") { - mWatcher->addPath(QString("f:") + QDir::separator()); - } else { - mWatcher->addPath(path); - } - } -} - bool HbInputModeCachePrivate::isMappedLanguage(const HbInputLanguage &language) const { - if (language.defined()) { - QList languages = HbKeymapFactory::instance()->availableLanguages(); - foreach(const HbInputLanguage &mappedLanguage, languages) { - if (mappedLanguage == language) { - return true; - } - } - } - - return false; + return (HbKeymapFactory::instance()->keymap(language) != 0); } /// @endcond @@ -295,10 +323,6 @@ { 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(); } @@ -313,22 +337,6 @@ /*! \internal -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); - } -} - -/*! -\internal 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 @@ -344,8 +352,6 @@ method.cached = 0; } d->mMethods.clear(); - delete d->mWatcher; - d->mWatcher = 0; } /*! @@ -355,11 +361,12 @@ HbInputMethod *HbInputModeCache::loadInputMethod(const HbInputMethodDescriptor &inputMethod) { Q_D(HbInputModeCache); + d->refresh(); 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) { + if (d->mMethods.at(i).descriptor.pluginNameAndPath() == inputMethod.pluginNameAndPath() && + d->mMethods.at(i).descriptor.key() == inputMethod.key()) { + if (!d->mMethods.at(i).cached) { d->mMethods[i].cached = d->methodInstance(inputMethod.pluginNameAndPath(), inputMethod.key()); } @@ -377,12 +384,13 @@ QList HbInputModeCache::listCustomInputMethods() { Q_D(HbInputModeCache); + d->refresh(); QList result; foreach(const HbInputMethodListItem &item, d->mMethods) { foreach(const QString &language, item.languages) { - HbInputModeProperties properties = d->propertiesFromString(language); + HbInputModeProperties properties = HbInputModeProperties::fromString(language); if (properties.inputMode() == HbInputModeCustom) { result.append(item.descriptor); break; @@ -400,12 +408,13 @@ QList HbInputModeCache::listCustomInputMethods(Qt::Orientation orientation, const HbInputLanguage &language) { Q_D(HbInputModeCache); + d->refresh(); QList result; - foreach (const HbInputMethodListItem &item, d->mMethods) { - foreach (const QString &lang, item.languages) { - HbInputModeProperties properties = d->propertiesFromString(lang); + for (int i = 0; i < d->mMethods.count(); i++) { + foreach (const QString &lang, d->mMethods.at(i).languages) { + HbInputModeProperties properties = HbInputModeProperties::fromString(lang); // Find custom methods that supports given language or any language and // supports given orientation @@ -413,7 +422,8 @@ (properties.language() == language || properties.language() == HbInputLanguage()) && ((orientation == Qt::Vertical && properties.keyboard() == HbKeyboardTouchPortrait) || (orientation == Qt::Horizontal && properties.keyboard() == HbKeyboardTouchLandscape))) { - result.append(item.descriptor); + + result.append(d->mMethods[i].descriptor); break; } } @@ -429,23 +439,40 @@ HbInputMethodDescriptor HbInputModeCache::defaultInputMethod(Qt::Orientation orientation) { Q_D(HbInputModeCache); + d->refresh(); - HbInputMethodDescriptor result; - foreach (const HbInputMethodListItem &item, d->mMethods) { - foreach (const QString &language, item.languages) { - HbInputModeProperties properties = d->propertiesFromString(language); + HbInputLanguage currentLanguage = HbInputSettingProxy::instance()->globalInputLanguage(); + bool mapped = d->isMappedLanguage(currentLanguage); + + for (int i = 0; i < d->mMethods.count(); i++) { + foreach (const QString &language, d->mMethods[i].languages) { + HbInputModeProperties properties = HbInputModeProperties::fromString(language); + + if (properties.language().undefined()) { + // The input method reports language range but current language is not mapped + // language. Skip this one. + if (!mapped) { + continue; + } + } else { + // The input method reports support for specific language but it is not an exact + // match to current language. Skip this one + if (properties.language() != currentLanguage) { + // It is not direct match either. + continue; + } + } // Find default method that supports given orientation if (properties.inputMode() == HbInputModeDefault && ((orientation == Qt::Vertical && properties.keyboard() == HbKeyboardTouchPortrait) || (orientation == Qt::Horizontal && properties.keyboard() == HbKeyboardTouchLandscape))) { - result = item.descriptor; - break; + return d->mMethods[i].descriptor; } } } - return result; + return HbInputMethodDescriptor(); } /*! @@ -455,15 +482,16 @@ HbInputMethod *HbInputModeCache::findStateHandler(const HbInputState &state) { Q_D(HbInputModeCache); + d->refresh(); - HbInputModeProperties stateProperties = d->propertiesFromState(state); + HbInputModeProperties stateProperties(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(const QString &language, d->mMethods[i].languages) { - HbInputModeProperties properties = d->propertiesFromString(language); + HbInputModeProperties properties = HbInputModeProperties::fromString(language); if (properties.language().undefined() && properties.keyboard() == stateProperties.keyboard() && properties.inputMode() == stateProperties.inputMode()) { @@ -507,9 +535,10 @@ \sa HbInputMethod */ -HbInputMethod *HbInputModeCache::activeMethod() const +HbInputMethod *HbInputModeCache::activeMethod() { - Q_D(const HbInputModeCache); + Q_D(HbInputModeCache); + d->refresh(); foreach(const HbInputMethodListItem &item, d->mMethods) { if (item.cached && item.cached->isActiveMethod()) { @@ -524,15 +553,16 @@ \internal Lists available input languages. */ -QList HbInputModeCache::listInputLanguages() const +QList HbInputModeCache::listInputLanguages() { - Q_D(const HbInputModeCache); + Q_D(HbInputModeCache); + d->refresh(); QList result; foreach(const HbInputMethodListItem &item, d->mMethods) { foreach(const QString &language, item.languages) { - HbInputModeProperties mode = d->propertiesFromString(language); + HbInputModeProperties mode = HbInputModeProperties::fromString(language); if (mode.inputMode() != HbInputModeCustom) { if (mode.language().undefined()) { // This is language range. Let's add everything @@ -559,14 +589,15 @@ \internal Returns true if given input method is able to handle given input state. */ -bool HbInputModeCache::acceptsState(const HbInputMethod *inputMethod, const HbInputState &state) const +bool HbInputModeCache::acceptsState(const HbInputMethod *inputMethod, const HbInputState &state) { - Q_D(const HbInputModeCache); + Q_D(HbInputModeCache); + d->refresh(); foreach(const HbInputMethodListItem &item, d->mMethods) { if (item.cached == inputMethod) { foreach(const QString &language, item.languages) { - HbInputModeProperties mode = d->propertiesFromString(language); + HbInputModeProperties mode = HbInputModeProperties::fromString(language); // Check if keyboard type matches. if (mode.keyboard() == state.keyboard()) { // Check if input mode matches or it is a custom input method but @@ -594,9 +625,10 @@ Returns input method descriptor for given input method. Returns empty descriptor if the framework doesn't recognize given input method. */ -HbInputMethodDescriptor HbInputModeCache::descriptor(const HbInputMethod *inputMethod) const +HbInputMethodDescriptor HbInputModeCache::descriptor(const HbInputMethod *inputMethod) { - Q_D(const HbInputModeCache); + Q_D(HbInputModeCache); + d->refresh(); foreach(const HbInputMethodListItem &item, d->mMethods) { if (item.cached == inputMethod) {