src/hbcore/theme/hbthemeutils_p.cpp
changeset 1 f7ac710697a9
parent 0 16d8024aca5e
child 2 06ff229162e9
--- a/src/hbcore/theme/hbthemeutils_p.cpp	Mon Apr 19 14:02:13 2010 +0300
+++ b/src/hbcore/theme/hbthemeutils_p.cpp	Mon May 03 12:48:33 2010 +0300
@@ -31,35 +31,55 @@
 #include <QtDebug>
 #include <QDir>
 #include <QMap>
+#include <QVariant>
 
 #include <hbapplication.h>
-
+#include <hbtheme.h>
 #include "hbstandarddirs_p.h"
 #include "hbiniparser_p.h"
 #include "hblayeredstyleloader_p.h"
 #include "hbthemecommon_p.h"
 
-static const QString iconsResourceFolder("icons");
-static const QString effectsResourceFolder("effects");
-static const QString styleResourceFolder("style");
-static const QString themeResourceFolder("theme");
+#ifdef Q_OS_SYMBIAN
+#include "hbthemecommon_symbian_p.h"
+#include <e32std.h>
+#endif
+
+// Standard folder names
 
-static const QString &getResourceFolderName(Hb::ResourceType resType)
+const char *HbThemeUtils::iconsResourceFolder = "icons";
+const char *HbThemeUtils::effectsResourceFolder = "effects";
+const char *HbThemeUtils::styleResourceFolder = "style";
+const char *HbThemeUtils::themeResourceFolder = "theme";
+const char *HbThemeUtils::operatorHierarchy = "operatortheme";
+const char *HbThemeUtils::appHierarchy = "apptheme";
+const char *HbThemeUtils::platformHierarchy = "themes";
+
+const char *operatorBasePathKey = "OperatorBasePath";
+static const char *themeSettingFile = "theme.theme";
+static const char *baseThemeVariable = "BaseTheme";
+static const char *defaultThemeVariable = "DefaultActiveTheme";
+
+// These are the used setting names corresponding to HbThemeUtils::Setting enumeration.
+// Value 0 is not used to be able to change the implementation to use Symbian's Cenrep if needed.
+static const QString settingNames[6] = {"", "currenttheme", "defaulttheme", "defaultthemedir", "basetheme", "operatorbasepath"};
+
+static const char *getResourceFolderName(Hb::ResourceType resType)
 {
     switch(resType) {
     case Hb::IconResource:
-        return iconsResourceFolder;
+        return HbThemeUtils::iconsResourceFolder;
     case Hb::EffectResource:
-        return effectsResourceFolder;
+        return HbThemeUtils::effectsResourceFolder;
     case Hb::ThemeResource:
-        return themeResourceFolder;
+        return HbThemeUtils::themeResourceFolder;
     case Hb::StyleSheetResource:
-        return styleResourceFolder;
+        return HbThemeUtils::styleResourceFolder;
     default:
         break;
     }
     // This just to avoid warning
-    return iconsResourceFolder;
+    return HbThemeUtils::iconsResourceFolder;
 }
 
 /*!
@@ -78,23 +98,82 @@
 class HbThemeUtilsPrivate
 {
 public:
-    HbThemeUtilsPrivate()
+    HbThemeUtilsPrivate() : settingsRetrieved(false)
     {
          // add the operator level, app level and platform level hierarchies in the hierarchy list.
-        hierarchy<<HbThemeUtils::operatorHierarchy()
-                <<HbThemeUtils::appHierarchy()
-                <<HbThemeUtils::platformHierarchy();
+        hierarchies << HbHierarchy(HbThemeUtils::operatorHierarchy, HbLayeredStyleLoader::Priority_Operator)
+#ifdef USE_APPTHEMES
+                    << HbHierarchy(HbThemeUtils::appHierarchy, HbLayeredStyleLoader::Priority_Application)
+#endif
+                    << HbHierarchy(HbThemeUtils::platformHierarchy, HbLayeredStyleLoader::Priority_Theme);
         // @todo: The operator name has been hard-coded here. Will be removed once it is decided on how to
         // get the operator name.
         operatorName = "myoperator";
     }
+    QString constructOperatorPath(const QString &basePath, const QString &resourcePath, const QString &fileName) const
+    {
+        return basePath + resourcePath + '/' + operatorName + '/' + fileName;
+    }
+    void initSettings();
 
+    void readSettings();
+
+public: // data
     QString operatorName;
-    QStringList hierarchy;
+    QVector<HbHierarchy> hierarchies;
+
+    bool settingsRetrieved;
+    // Setting values are stored here to avoid overhead of reading from QSettings every time.
+    QString currentTheme;
+    QString defaultTheme;
+    QString defaultThemeRootDir;
+    QString baseTheme;
+    QString operatorBasePath;
 };
 
+void HbThemeUtilsPrivate::initSettings()
+{
+    //server gets and stores the operator path to settings, clients only read it.
+    if (HbMemoryUtils::getCleanAppName()== THEME_SERVER_NAME) {
+        QStringList operatorPath;
+        operatorPath << QLatin1String(HbThemeUtils::operatorHierarchy) + '/';
+        operatorPath = HbStandardDirs::findExistingFolderList(operatorPath, QString(), Hb::IconResource);
+        if (operatorPath.size() > 0) {
+            operatorBasePath = operatorPath.at(0);
+        }
+        HbThemeUtils::setThemeSetting(HbThemeUtils::OperatorBasePathSetting, operatorBasePath);
+    } else {
+        operatorBasePath = HbThemeUtils::getThemeSetting(HbThemeUtils::OperatorBasePathSetting).trimmed();
+    }
+}
+
+void HbThemeUtilsPrivate::readSettings()
+{
+    // Read settings from QSettings and store them in member variables to
+    // avoid slow instantiating of QSettings in advance.
+
+    // The only changing setting is currentThemeSetting and its value is updated in theme change event.
+
+    if (!settingsRetrieved) {
+        QSettings settings(QLatin1String(ORGANIZATION), QLatin1String(THEME_COMPONENT));
+
+        currentTheme = settings.value(settingNames[HbThemeUtils::CurrentThemeSetting]).toString();
+        defaultTheme = settings.value(settingNames[HbThemeUtils::DefaultThemeSetting]).toString();
+        defaultThemeRootDir = settings.value(settingNames[HbThemeUtils::DefaultThemeRootDirSetting]).toString();
+        baseTheme = settings.value(settingNames[HbThemeUtils::BaseThemeSetting]).toString();
+        operatorBasePath = settings.value(settingNames[HbThemeUtils::OperatorBasePathSetting]).toString();
+
+        settingsRetrieved = true;
+    }
+}
+
 static HbThemeUtilsPrivate d;
 
+void HbThemeUtils::initSettings()
+{
+    d.initSettings();
+}
+
 /* Adds a new hierarchy level to be used for attribute look-up
  * 
  * @param newHierrachy the name of the new hierrachy
@@ -104,23 +183,26 @@
  *
  * @return the positon in the new hierarchy in the hierarchy list. -1 if the new hierarchy is not added.
  */
+
 int HbThemeUtils::addHierarchy(const QString &newHierarchy, int priorityOrder)
 {    
     int retValue = -1;
     if (priorityOrder >= 0) {
         // check that the hierarchy to be added is neither of opertor level,app level and platform level.
-        if(newHierarchy != HbThemeUtils::operatorHierarchy() 
-           && newHierarchy != HbThemeUtils::appHierarchy() 
-           && newHierarchy != HbThemeUtils::platformHierarchy()){
+        if(newHierarchy != HbThemeUtils::operatorHierarchy
+            && newHierarchy != HbThemeUtils::appHierarchy
+            && newHierarchy != HbThemeUtils::platformHierarchy){
+
             // if priority given is more than the number of hierarchies already existing, append the new
             // hierarchy at end.
-            if (priorityOrder > d.hierarchy.count()) {
-                d.hierarchy.append(newHierarchy);
-                retValue = d.hierarchy.count() - 1;
+            HbHierarchy add(newHierarchy, HbLayeredStyleLoader::Priority_Theme);
+            if (priorityOrder > d.hierarchies.count()) {
+                d.hierarchies.append(add);
+                retValue = d.hierarchies.count() - 1;
             }
             // else insert it at the correct position
             else {
-                d.hierarchy.insert(priorityOrder, newHierarchy);
+                d.hierarchies.insert(priorityOrder,add);
                 retValue = priorityOrder;
             }
         }
@@ -128,30 +210,40 @@
     return retValue;
 }
 
-
 /* Removes a hierarchy level from the hierarchy list
  *
  * @param newHierrachy the name of the hierrachy to be removed.
  *
  * @ret true if the hierarchy has been removed, else false.
  */
-bool HbThemeUtils::removeHierarchy(const QString &hierarchy)
+bool HbThemeUtils::removeHierarchy(const QString &hierarchyName)
 {
     bool retValue = false;
     // check whether an attempt is made to remove operator level, app level or platform level hierarchy
-    if (hierarchy != HbThemeUtils::operatorHierarchy() 
-        && hierarchy != HbThemeUtils::appHierarchy() 
-        && hierarchy != HbThemeUtils::platformHierarchy()) {
-        retValue = d.hierarchy.removeOne(hierarchy);
+    if (hierarchyName != HbThemeUtils::operatorHierarchy
+        && hierarchyName != HbThemeUtils::appHierarchy
+        && hierarchyName != HbThemeUtils::platformHierarchy) {
+        QVector<HbHierarchy>::iterator end = d.hierarchies.end();
+        for (QVector<HbHierarchy>::iterator i = d.hierarchies.begin(); i != end; ++i) {
+            if (i->name == hierarchyName) {
+                d.hierarchies.erase(i);
+                retValue = true;
+                break;
+            }
+        }
     }
     return retValue;
 }
 
+QString HbThemeUtils::operatorBasePath()
+{
+    return d.operatorBasePath;
+}
 /* @ret hierarchy of themes in priority.
  */
-QStringList HbThemeUtils::hierarchy()
+QVector<HbHierarchy> HbThemeUtils::hierarchies()
 {
-   return d.hierarchy; 
+   return d.hierarchies;
 }
 
 /* It constructs the hierarchy list with complete path info using the existing hierarchy list.
@@ -166,96 +258,188 @@
                                                                    const QString &currentTheme,
                                                                    const Hb::ResourceType resType)
 {
-    Q_UNUSED(currentTheme);
     QMap<int,QString> hierarchyListWithPathInfo;
 
     // Map the resource enum to string here
-    const QString &resourceFolder = getResourceFolderName(resType);
-    
-    foreach (const QString &hierarchy, HbThemeUtils::hierarchy()) {
-        if (hierarchy == HbThemeUtils::operatorHierarchy()) {
-            hierarchyListWithPathInfo.insert(HbLayeredStyleLoader::Priority_Operator, (hierarchy + '/' + resourceFolder + '/' + d.operatorName + '/' + fileName));
-        }
-        else if (hierarchy == HbThemeUtils::appHierarchy()) {
-            QString exebasename = QFileInfo(QCoreApplication::applicationFilePath()).baseName();
-            hierarchyListWithPathInfo.insert(HbLayeredStyleLoader::Priority_Application, (hierarchy + '/' + exebasename + '/' + resourceFolder + '/' + currentTheme + '/' + fileName));
-        }
-        else if(hierarchy == HbThemeUtils::platformHierarchy()) {
-            hierarchyListWithPathInfo.insert(HbLayeredStyleLoader::Priority_Theme, (hierarchy + '/' + resourceFolder + '/' + currentTheme + '/' + fileName));
-        }
-        else {
+    const QString &resourcePath = getResourceFolderName(resType);
+
+    foreach (const HbHierarchy &hierarchy, d.hierarchies) {
+        switch(hierarchy.layerPriority) {
+        case HbLayeredStyleLoader::Priority_Operator:
+            if (!d.operatorBasePath.isEmpty()) {
+                hierarchyListWithPathInfo.insert(HbLayeredStyleLoader::Priority_Operator,
+                                                 d.constructOperatorPath(d.operatorBasePath, resourcePath, fileName));
+            }
+            break;
+        case HbLayeredStyleLoader::Priority_Application:
+            hierarchyListWithPathInfo.insert(HbLayeredStyleLoader::Priority_Application, 
+                    (hierarchy.name + '/' + HbMemoryUtils::getCleanAppName() + '/' + resourcePath + '/' + currentTheme + '/' + fileName));
+            break;
+        case HbLayeredStyleLoader::Priority_Theme:
+            // Add platform theme folder only if it is different from base theme
+            // Base theme is anyway added at the core priority
+            if (currentTheme != baseTheme().name) {
+                hierarchyListWithPathInfo.insert(HbLayeredStyleLoader::Priority_Theme, 
+                        (hierarchy.name + '/' + resourcePath + '/' + currentTheme + '/' + fileName));
+            }
+            break;
+        default:
             // this is for a new hierarchy level and for the time being HbLayeredStyleLoader::Priority_Theme prirority is used,since there is no enum defined in hblayeredstyleloader_p.h
             // priority should be replaced with respective enum.
-            hierarchyListWithPathInfo.insert(HbLayeredStyleLoader::Priority_Theme, (hierarchy + '/' + resourceFolder + '/' + currentTheme + '/' + fileName));
+            hierarchyListWithPathInfo.insert(HbLayeredStyleLoader::Priority_Theme, 
+                    (hierarchy.name + '/' + resourcePath + '/' + currentTheme + '/' + fileName));
         }
     }
     
-    if (resType == Hb::StyleSheetResource) {
-        // lets add default CSS path too in this list for now
-        // This comes last in fallback hierarchy
-        hierarchyListWithPathInfo.insert(HbLayeredStyleLoader::Priority_Core, ("themes/" + resourceFolder + '/' + defaultTheme() + '/' + fileName));
+    if (resType == Hb::StyleSheetResource || resType == Hb::EffectResource) {
+        // lets add base CSS path too in this list for now
+        // This comes last in base hierarchy
+        hierarchyListWithPathInfo.insert(HbLayeredStyleLoader::Priority_Core, 
+                (QLatin1String("themes/") + resourcePath + '/' + baseTheme().name + '/' + fileName));
     }
 
     return hierarchyListWithPathInfo;
 }
 
-
+/* returns information of base theme
+ */
+const HbThemeInfo &HbThemeUtils::baseTheme()
+{
+    static HbThemeInfo baseThemeInfo;
+   
+    if (baseThemeInfo.name.isEmpty()) {
+        // basetheme is empty, means it was not yet filled with appropriate values      
+        // Check if its value is stored in settings.
+        baseThemeInfo.name = getThemeSetting(BaseThemeSetting).trimmed();
+        if ( baseThemeInfo.name.isEmpty() ) {
+            // Settings not yet initialized
+            // Check if Base theme in rom set
+            baseThemeInfo = getBaseThemeFromFile(HbStandardDirs::themesDir());
+            if (baseThemeInfo.name.isEmpty()) {
+                // Base theme does not exists in rom
+                // Get the base theme info from core resources
+                baseThemeInfo = getBaseThemeFromFile(coreResourcesRootDir);
+            }
+        } else {
+            // So settings are initialized, it will have other value as well
+            baseThemeInfo.rootDir = getThemeSetting(DefaultThemeRootDirSetting).trimmed();            
+        }
+    }
+    
+    return baseThemeInfo;
+}
 
 /* returns name of default theme
  */
-
-QString HbThemeUtils::defaultTheme()
+HbThemeInfo HbThemeUtils::defaultTheme()
 {
-    static QString defaultThemeName;
-    
-    // defaultThemeName is empty, means it was not yet filled with appropriate value
-    if (defaultThemeName.isEmpty()) {
-        HbIniParser iniParser;
+    // getting base theme makes sure that default theme was added in
+    // QSettings, if it was not already done
+    const HbThemeInfo &themeInfo = baseTheme(); 
+
+    // Assuming the path of default theme and base theme are same
+    return HbThemeInfo(getThemeSetting(DefaultThemeSetting), themeInfo.rootDir);
+}
+
+QString HbThemeUtils::getThemeSetting(Setting setting)
+{
+    // Make sure settings are read from QSettings.
+    d.readSettings();
 
-        // First check whether it is already stored in QSettings
-        QSettings&  settings = getThemeSettings();
-        defaultThemeName = settings.value("defaulttheme").toString();
+    switch (setting) {
+        case CurrentThemeSetting:
+            return d.currentTheme;
+        case DefaultThemeSetting:
+            return d.defaultTheme;
+        case DefaultThemeRootDirSetting:
+            return d.defaultThemeRootDir;
+        case BaseThemeSetting:
+            return d.baseTheme;
+        case OperatorBasePathSetting:
+            return d.operatorBasePath;
+        default:
+            return QString();
+    }
+}
 
-        // if not in QSettings, read from theme.theme file
-        if (defaultThemeName.isEmpty()) {
-            // Find theme.theme file
-            QString dir = "themes";
-            QString masterThemeFile = HbStandardDirs::findResource( dir + '/' + "themes" +
-                '/' + "theme.theme", Hb::ThemeResource);
+void HbThemeUtils::setThemeSetting(Setting setting, const QString &value)
+{
+    QSettings settings(QLatin1String(ORGANIZATION), QLatin1String(THEME_COMPONENT));
+    settings.setValue(settingNames[setting], QVariant(value));
+    // Destructor of QSettings flushes the changed setting in the INI file.
+}   
 
-            // Try to read file and get parameters
-            QFile themeFile(masterThemeFile);
-            if (!themeFile.open(QIODevice::ReadOnly) || !iniParser.read(&themeFile)){
-                qDebug() << "Can't access file";
-                return false;
-            } 
+/**
+* Updates the setting's value in stored member variables.
+* Normally the settings are loaded from QSettings when method getThemeSetting() is called for the first time.
+* When there is a change in settings, this method can be used to sync the setting value stored in HbThemeUtilsPrivate.
+* E.g. theme change event updates the current theme setting, currently no other settings are changing their values.
+*/
+void HbThemeUtils::updateThemeSetting(Setting setting, const QString &value)
+{
+    switch (setting) {
+        case CurrentThemeSetting:
+            d.currentTheme = value;
+            break;
+        case DefaultThemeSetting:
+            d.defaultTheme = value;
+            break;
+        case DefaultThemeRootDirSetting:
+            d.defaultThemeRootDir = value;
+            break;
+        case BaseThemeSetting:
+            d.baseTheme = value;
+            break;
+        case OperatorBasePathSetting:
+            d.operatorBasePath = value;
+            break;
+        default:
+            break;
+    }
+}   
+ 
+/* reads the theme name from theme.theme file, stores the same in theme settings,
+   returns the pair of theme name and its root directory
+ */
+HbThemeInfo HbThemeUtils::getBaseThemeFromFile(const QString &rootDir)
+{
+    QFile themeSetting(rootDir + '/' + platformHierarchy + '/' + themeSettingFile);
+    HbThemeInfo themeInfo;
+    HbIniParser iniParser;
 
-            //Find default theme index.theme file and clean it
-            defaultThemeName = iniParser.value("Default Theme", "Name").trimmed();
-            //Save Default theme
-            settings.setValue("defaulttheme", defaultThemeName);        
-        }
-        else {
+    if (themeSetting.open(QIODevice::ReadOnly) && iniParser.read(&themeSetting)){
+        themeInfo.name = iniParser.value("Default", baseThemeVariable).trimmed();
         
-            QString cleanDefThemeName = defaultThemeName.trimmed();         
-            // if stored default theme name is not clean, store the cleaned theme name
-            // (stored theme name may not be clean in case old implementaion which did not
-            //  handle dirty theme name, was run on the same device earlier.)
-            if (cleanDefThemeName != defaultThemeName) {
-                defaultThemeName = cleanDefThemeName;
-                settings.setValue("defaulttheme", defaultThemeName);
+        QString defaultTheme = iniParser.value("Default", defaultThemeVariable).trimmed();
+
+        // default theme name may not exist, in which case using base theme as default theme
+        if (defaultTheme.isEmpty()) {
+            defaultTheme = themeInfo.name;
+        }
+
+        // If there is any base theme
+        if (!themeInfo.name.isEmpty() && isThemeValid(HbThemeInfo(themeInfo.name,rootDir))) {
+            // Save these theme names in settings
+            setThemeSetting(BaseThemeSetting, themeInfo.name);
+            setThemeSetting(DefaultThemeRootDirSetting, rootDir);
+
+            // Store default theme also in settings, only if it is valid
+            if (themeInfo.name == defaultTheme || isThemeValid(HbThemeInfo(defaultTheme, rootDir))) {
+                setThemeSetting(DefaultThemeSetting, defaultTheme);
             }
+            themeInfo.rootDir = rootDir;
+            d.settingsRetrieved = false;
         }
     }
-    
-    return defaultThemeName;
+    return themeInfo;
 }
 
-/* returns settings for the theme
+/* checks whether the theme is valid
  */
-QSettings& HbThemeUtils::getThemeSettings()
-    {
-     static QSettings settings(QLatin1String(ORGANIZATION), QLatin1String(THEME_COMPONENT));
-     return settings;
-    }
-   
+bool HbThemeUtils::isThemeValid(const HbThemeInfo &themeInfo)
+{
+    // If the theme contains index.theme in icons resources
+    // it will be assumed valid
+    QFile themeIndexFile(themeInfo.rootDir + '/' + platformHierarchy + '/' + iconsResourceFolder + "/" + themeInfo.name + "/index.theme");
+    return themeIndexFile.open(QIODevice::ReadOnly);
+}