src/hbcore/inputfw/hbinputmodecache.cpp
changeset 0 16d8024aca5e
child 1 f7ac710697a9
equal deleted inserted replaced
-1:000000000000 0:16d8024aca5e
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (developer.feedback@nokia.com)
       
     6 **
       
     7 ** This file is part of the HbCore module of the UI Extensions for Mobile.
       
     8 **
       
     9 ** GNU Lesser General Public License Usage
       
    10 ** This file may be used under the terms of the GNU Lesser General Public
       
    11 ** License version 2.1 as published by the Free Software Foundation and
       
    12 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
       
    13 ** Please review the following information to ensure the GNU Lesser General
       
    14 ** Public License version 2.1 requirements will be met:
       
    15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    16 **
       
    17 ** In addition, as a special exception, Nokia gives you certain additional
       
    18 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    20 **
       
    21 ** If you have questions regarding the use of this file, please contact
       
    22 ** Nokia at developer.feedback@nokia.com.
       
    23 **
       
    24 ****************************************************************************/
       
    25 #include <QInputContextPlugin>
       
    26 #include <QLocale>
       
    27 #include <QFileSystemWatcher>
       
    28 #include <QLibrary>
       
    29 #include <QPluginLoader>
       
    30 #include <QDir>
       
    31 
       
    32 #include "hbinputmodecache_p.h"
       
    33 #include "hbinpututils.h"
       
    34 #include "hbinputmethod.h"
       
    35 #include "hbinputsettingproxy.h"
       
    36 #include "hbinputmodeproperties.h"
       
    37 #include "hbinputkeymapfactory.h"
       
    38 #include "hbinputmethod_p.h"
       
    39 #include "hbinputmethodnull_p.h"
       
    40 
       
    41 /*!
       
    42 @alpha
       
    43 @hbcore
       
    44 \class HbInputModeCache
       
    45 \brief Input framework's internal input mode resolver class.
       
    46 */
       
    47 
       
    48 /// @cond
       
    49 
       
    50 class HbInputMethodListItem
       
    51 {
       
    52 public:
       
    53     HbInputMethodListItem() : cached(0), toBeRemoved(false) {}
       
    54     bool operator==(const HbInputMethodListItem &item) const {
       
    55         return (descriptor.pluginNameAndPath() == item.descriptor.pluginNameAndPath() &&
       
    56                 descriptor.key() == item.descriptor.key());
       
    57     }
       
    58 
       
    59 public:
       
    60     HbInputMethodDescriptor descriptor;
       
    61     QStringList languages;
       
    62     HbInputMethod *cached;
       
    63     bool toBeRemoved;
       
    64 };
       
    65 
       
    66 class HbInputModeCachePrivate
       
    67 {
       
    68 public:
       
    69     HbInputModeCachePrivate() : mWatcher(new QFileSystemWatcher()), mShuttingDown(false) {}
       
    70     ~HbInputModeCachePrivate() {}
       
    71     void refresh(const QString &directory = QString());
       
    72     QInputContextPlugin *pluginInstance(const QString& pluginFileName) const;
       
    73     HbInputMethod *methodInstance(const QString &pluginFileName, const QString &key) const;
       
    74     HbInputModeProperties propertiesFromString(const QString &entry) const;
       
    75     HbInputModeProperties propertiesFromState(const HbInputState &state) const;
       
    76     HbInputMethod *cachedMethod(HbInputMethodListItem &item);
       
    77     void updateMonitoredPaths();
       
    78 
       
    79 public:
       
    80     QFileSystemWatcher *mWatcher;
       
    81     QList<HbInputMethodListItem> mMethods;
       
    82     bool mShuttingDown;
       
    83 };
       
    84 
       
    85 QInputContextPlugin* HbInputModeCachePrivate::pluginInstance(const QString& pluginFileName) const
       
    86 {
       
    87     if (QLibrary::isLibrary(pluginFileName)) {
       
    88         QPluginLoader loader(pluginFileName);
       
    89         QObject* plugin = loader.instance();
       
    90         if (plugin) {
       
    91             return qobject_cast<QInputContextPlugin*>(plugin);
       
    92         }
       
    93     }
       
    94 
       
    95     return 0;
       
    96 }
       
    97 
       
    98 HbInputMethod *HbInputModeCachePrivate::methodInstance(const QString &pluginFileName, const QString &key) const
       
    99 {
       
   100     QInputContextPlugin *plugin = pluginInstance(pluginFileName);
       
   101     if (plugin) {
       
   102         QInputContext *instance = plugin->create(key);
       
   103         HbInputMethod *result = qobject_cast<HbInputMethod*>(instance);
       
   104         if (result) {
       
   105             QStringList languages = plugin->languages(key);
       
   106             QList<HbInputModeProperties> modeList;
       
   107             foreach (QString language, languages) {
       
   108                   modeList.append(propertiesFromString(language));
       
   109             }
       
   110             result->d_ptr->mInputModes = modeList;
       
   111         }
       
   112         return result;
       
   113     }
       
   114 
       
   115     return 0;
       
   116 }
       
   117 
       
   118 void HbInputModeCachePrivate::refresh(const QString &directory)
       
   119 {
       
   120     Q_UNUSED(directory);
       
   121     // optimize later so that if the directory is given, only changes concerning
       
   122     // it are taken into account.
       
   123 
       
   124     // First go through all the previously found entries and
       
   125     // tag them not refreshed.
       
   126     for (int k = 0; k < mMethods.count(); k++) {
       
   127         mMethods[k].toBeRemoved = true;
       
   128     }
       
   129 
       
   130     // Query plugin paths and scan the folders.
       
   131     QStringList folders = HbInputSettingProxy::instance()->inputMethodPluginPaths();
       
   132     foreach (QString folder, folders) {
       
   133         QDir dir(folder);
       
   134         for (unsigned int i = 0; i < dir.count(); i++) {
       
   135             QString path = QString(dir.absolutePath());
       
   136             if (path.right(1) != "\\" && path.right(1) != "/") {
       
   137                 path += QDir::separator();
       
   138             }
       
   139             path += dir[i];
       
   140             QInputContextPlugin* inputContextPlugin = pluginInstance(path);
       
   141             if (inputContextPlugin) {
       
   142                 HbInputMethodListItem listItem;
       
   143                 listItem.descriptor.setPluginNameAndPath(dir.absolutePath() + QDir::separator() + dir[i]);
       
   144 
       
   145                 // For each found plugin, check if there is already a list item for it.
       
   146                 // If not, then add one.
       
   147                 QStringList contextKeys = inputContextPlugin->keys();
       
   148                 foreach (QString key, contextKeys) {
       
   149                     listItem.descriptor.setKey(key);
       
   150                     listItem.descriptor.setDisplayName(inputContextPlugin->displayName(key));
       
   151 
       
   152                     int index = mMethods.indexOf(listItem);
       
   153                     if (index >= 0) {
       
   154                         // The method is already in the list, the situation hasn't changed.
       
   155                         // just tag it not to be removed.
       
   156                         mMethods[index].toBeRemoved = false;
       
   157                     } else {
       
   158                         listItem.languages = inputContextPlugin->languages(key);
       
   159                         mMethods.append(listItem);
       
   160                     }
       
   161                 }
       
   162             }
       
   163         }
       
   164     }
       
   165 
       
   166     // Go through the cache list and find out if some of the previous items need to be
       
   167     // removed after the refresh.
       
   168     for (int i = 0; i < mMethods.count(); i++) {
       
   169         if (mMethods[i].toBeRemoved) {
       
   170             if (mMethods[i].cached && mMethods[i].cached->isActiveMethod()) {
       
   171                 // If the item to be removed happens to be the active one,
       
   172                 // try to deal with the situation.
       
   173                 mMethods[i].cached->forceUnfocus();
       
   174                 if (mMethods[i].descriptor.pluginNameAndPath() == HbInputSettingProxy::instance()->activeCustomInputMethod().pluginNameAndPath()) {
       
   175                     // The active custom method is being removed.
       
   176                     // Clear out related setting proxy values.
       
   177                     HbInputSettingProxy::instance()->setActiveCustomInputMethod(HbInputMethodDescriptor());
       
   178                 }
       
   179 
       
   180                 // Replace it with null input context.
       
   181                 HbInputMethod *master = HbInputMethodNull::Instance();
       
   182                 master->d_ptr->mIsActive = true;
       
   183                 QInputContext* proxy = master->d_ptr->newProxy();
       
   184                 qApp->setInputContext(proxy);
       
   185             }
       
   186             delete mMethods[i].cached;
       
   187             mMethods.removeAt(i);
       
   188             i--;
       
   189         }
       
   190     }
       
   191 }
       
   192 
       
   193 HbInputModeProperties HbInputModeCachePrivate::propertiesFromString(const QString &entry) const
       
   194 {
       
   195     HbInputModeProperties result;
       
   196 
       
   197     QStringList parts = entry.split(" ");
       
   198     if (parts.count() == 4) {
       
   199         // See HbInputModeProperties::toString() for details,
       
   200         QString languageStr = parts[0] + QString(" ") + parts[1];
       
   201         HbInputLanguage language;
       
   202         language.fromString(languageStr);
       
   203         result.setLanguage(language);
       
   204         result.setInputMode((HbInputModeType)parts[2].toLong());
       
   205         result.setKeyboard((HbKeyboardType)parts[3].toLong());
       
   206     }
       
   207 
       
   208     return result;
       
   209 }
       
   210 
       
   211 HbInputModeProperties HbInputModeCachePrivate::propertiesFromState(const HbInputState &state) const
       
   212 {
       
   213     HbInputModeProperties result;
       
   214 
       
   215     result.setLanguage(state.language());
       
   216     result.setKeyboard(state.keyboard());
       
   217     result.setInputMode(state.inputMode());
       
   218 
       
   219     return HbInputModeProperties(result);
       
   220 }
       
   221 
       
   222 HbInputMethod *HbInputModeCachePrivate::cachedMethod(HbInputMethodListItem &item)
       
   223 {
       
   224     if (!item.cached) {
       
   225         item.cached = methodInstance(item.descriptor.pluginNameAndPath(), item.descriptor.key());
       
   226     }
       
   227 
       
   228     return item.cached;
       
   229 }
       
   230 
       
   231 void HbInputModeCachePrivate::updateMonitoredPaths()
       
   232 {
       
   233     QStringList watchedDirs = mWatcher->directories();
       
   234     if (!watchedDirs.isEmpty()) {
       
   235         mWatcher->removePaths(watchedDirs);
       
   236     }
       
   237 
       
   238     QStringList paths = HbInputSettingProxy::instance()->inputMethodPluginPaths();
       
   239     foreach (QString path, paths) {
       
   240         QDir dir(path);
       
   241         if (!dir.exists() && path.left(1) == "f") {
       
   242             mWatcher->addPath(QString("f:") + QDir::separator());
       
   243         } else {
       
   244             mWatcher->addPath(path);
       
   245         }
       
   246     }
       
   247 }
       
   248 /// @endcond
       
   249 
       
   250 /*!
       
   251 Returns the singleton instance.
       
   252 */
       
   253 HbInputModeCache* HbInputModeCache::instance()
       
   254 {
       
   255     static HbInputModeCache theCache;
       
   256     return &theCache;
       
   257 }
       
   258 
       
   259 /*!
       
   260 Construct the object.
       
   261 */
       
   262 HbInputModeCache::HbInputModeCache() : d_ptr(new HbInputModeCachePrivate())
       
   263 {
       
   264     Q_D(HbInputModeCache);
       
   265 
       
   266     // Start to monitor file system for changes.
       
   267     d->updateMonitoredPaths();
       
   268     connect(d->mWatcher, SIGNAL(directoryChanged(const QString &)), this, SLOT(directoryChanged(const QString &)));
       
   269 
       
   270     d->refresh();
       
   271 }
       
   272 
       
   273 /*!
       
   274 Destruct the object.
       
   275 */
       
   276 HbInputModeCache::~HbInputModeCache()
       
   277 {
       
   278     delete d_ptr;
       
   279 }
       
   280 
       
   281 /*!
       
   282 This slot is called whenever a change in input method plugin file system is detected and
       
   283 the list needs to be refreshed.
       
   284 */
       
   285 void HbInputModeCache::directoryChanged(const QString &directory)
       
   286 {
       
   287     Q_D(HbInputModeCache);
       
   288 
       
   289     d->updateMonitoredPaths();
       
   290 
       
   291     if (!d->mShuttingDown) {
       
   292         d->refresh(directory);
       
   293     }
       
   294 }
       
   295 
       
   296 /*!
       
   297 Shuts down the object safely. This is needed mainly for singleton object. There has been a lot
       
   298 of problems related to randown singleton desctruction order and additional shutdown step is
       
   299 needed to guarantee that it will be done safely. The slot is connected to
       
   300 QCoreApplication::aboutToQuit when the framework is initialized.
       
   301 */
       
   302 void HbInputModeCache::shutdown()
       
   303 {
       
   304     Q_D(HbInputModeCache);
       
   305     d->mShuttingDown = true;
       
   306 
       
   307     foreach (HbInputMethodListItem method, d->mMethods) {
       
   308         delete method.cached;
       
   309         method.cached = 0;
       
   310     }
       
   311     d->mMethods.clear();
       
   312     delete d->mWatcher;
       
   313     d->mWatcher = 0;
       
   314 }
       
   315 
       
   316 /*!
       
   317 Loads given input method and caches it.
       
   318 */
       
   319 HbInputMethod* HbInputModeCache::loadInputMethod(const HbInputMethodDescriptor &inputMethod)
       
   320 {
       
   321     Q_D(HbInputModeCache);
       
   322 
       
   323     for (int i = 0; i < d->mMethods.count(); i++) {
       
   324         if (d->mMethods[i].descriptor.pluginNameAndPath() == inputMethod.pluginNameAndPath() &&
       
   325             d->mMethods[i].descriptor.key() == inputMethod.key()) {
       
   326             if (!d->mMethods[i].cached) {
       
   327                 d->mMethods[i].cached = d->methodInstance(inputMethod.pluginNameAndPath(), inputMethod.key());
       
   328             }
       
   329 
       
   330             return d->mMethods[i].cached;
       
   331         }
       
   332     }
       
   333 
       
   334     return 0;
       
   335 }
       
   336 
       
   337 /*!
       
   338 Lists custom input methods.
       
   339 */
       
   340 QList<HbInputMethodDescriptor> HbInputModeCache::listCustomInputMethods()
       
   341 {
       
   342     Q_D(HbInputModeCache);
       
   343 
       
   344     QList<HbInputMethodDescriptor> result;
       
   345 
       
   346     foreach (HbInputMethodListItem item, d->mMethods) {
       
   347         foreach (QString language, item.languages) {
       
   348             HbInputModeProperties properties = d->propertiesFromString(language);
       
   349             if (properties.inputMode() == HbInputModeCustom) {
       
   350                 result.append(item.descriptor);
       
   351                 break;
       
   352             }
       
   353         }
       
   354     }
       
   355 
       
   356     return result;
       
   357 }
       
   358 
       
   359 /*!
       
   360  Find correct handler for given input state.
       
   361  */
       
   362 HbInputMethod* HbInputModeCache::findStateHandler(const HbInputState& state)
       
   363 {
       
   364     Q_D(HbInputModeCache);
       
   365 
       
   366     HbInputModeProperties stateProperties = d->propertiesFromState(state);
       
   367     int languageRangeIndex = -1;
       
   368 
       
   369     // First check if there is a method that matches excatly (ie. also specifies
       
   370     // the language).
       
   371     for (int i = 0; i < d->mMethods.count(); i++) {
       
   372         foreach (QString language, d->mMethods[i].languages) {
       
   373             HbInputModeProperties properties = d->propertiesFromString(language);
       
   374             if (properties.language().undefined() &&
       
   375                 properties.keyboard() == stateProperties.keyboard() &&
       
   376                 properties.inputMode() == stateProperties.inputMode()) {
       
   377                 // Remember the index, we'll need this in the next phase if no exact
       
   378                 // match is found.
       
   379                 languageRangeIndex = i;
       
   380             }
       
   381 
       
   382             if (properties.inputMode() != HbInputModeCustom) {
       
   383                 if (properties == stateProperties) {
       
   384                         return d->cachedMethod(d->mMethods[i]);
       
   385                 }
       
   386             }
       
   387         }
       
   388     }
       
   389 
       
   390     // No exact match found. Then see if there was a method that matches to language
       
   391     // range, meaning that the language is left unspecified in which case we'll
       
   392     // use key mapping factory for matching.
       
   393     if (languageRangeIndex >= 0) {
       
   394         QList<HbInputLanguage> languages = HbKeymapFactory::instance()->availableLanguages();
       
   395 
       
   396         foreach(HbInputLanguage language, languages) {
       
   397             // exact match is returned If the country variant is specified in state language,
       
   398             // otherwise a method that matches to only language range is returned.
       
   399             bool exactMatchFound = (stateProperties.language().variant() != QLocale::AnyCountry) ?
       
   400                 (language == stateProperties.language()) :
       
   401                 (language.language() == stateProperties.language().language());
       
   402             if (exactMatchFound) {
       
   403                 return d->cachedMethod(d->mMethods[languageRangeIndex]);
       
   404             }
       
   405         }
       
   406     }
       
   407 
       
   408     return 0;
       
   409 }
       
   410 
       
   411 /*!
       
   412  Returns the active input method.
       
   413 
       
   414  \sa HbInputMethod
       
   415  */
       
   416 HbInputMethod* HbInputModeCache::activeMethod() const
       
   417 {
       
   418     Q_D(const HbInputModeCache);
       
   419 
       
   420     foreach (HbInputMethodListItem item, d->mMethods) {
       
   421         if (item.cached && item.cached->isActiveMethod()) {
       
   422             return item.cached;
       
   423         }
       
   424     }
       
   425 
       
   426     return 0;
       
   427 }
       
   428 
       
   429 /*!
       
   430 Lists available input languages.
       
   431 */
       
   432 QList<HbInputLanguage> HbInputModeCache::listInputLanguages() const
       
   433 {
       
   434     Q_D(const HbInputModeCache);
       
   435 
       
   436     QList<HbInputLanguage> result;
       
   437 
       
   438     foreach (HbInputMethodListItem item, d->mMethods) {
       
   439         foreach (QString language, item.languages) {
       
   440             HbInputModeProperties mode = d->propertiesFromString(language);
       
   441             if (mode.inputMode() != HbInputModeCustom) {
       
   442                 if (mode.language().undefined()) {
       
   443                     // This is language range. Let's add everything
       
   444                     // we have key mappings for.
       
   445                     QList<HbInputLanguage> languages = HbKeymapFactory::instance()->availableLanguages();
       
   446                     foreach (HbInputLanguage mappedLanguage, languages) {
       
   447                         if (!result.contains(mappedLanguage)) {
       
   448                             result.append(mappedLanguage);
       
   449                         }
       
   450                     }
       
   451                 } else {
       
   452                     if (!result.contains(mode.language())) {
       
   453                         result.append(mode.language());
       
   454                     }
       
   455                 }
       
   456             }
       
   457         }
       
   458     }
       
   459 
       
   460     return result;
       
   461 }
       
   462 
       
   463 // End of file