src/hbservers/themeindexer/main.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 HbServers 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 
       
    26 #include <hbiconsource_p.h>
       
    27 #include <hbthemeindex_p.h>
       
    28 #include <QtGui>
       
    29 #include <assert.h>
       
    30 #include <iostream>
       
    31 
       
    32 #define RESOURCE_LIB_NAME "HbCore"
       
    33 #define WIN32_DEBUG_SUFFIX "d"
       
    34 #define MAC_DEBUG_SUFFIX "_debug"
       
    35 
       
    36 // For being able to sort the index items based on the iconname
       
    37 class IndexItemInfo
       
    38 {
       
    39 public:
       
    40     QString iconname;
       
    41     HbThemeIndexItem item;
       
    42 };
       
    43 
       
    44 bool operator<(const IndexItemInfo &left, const IndexItemInfo &right)
       
    45 {
       
    46     return left.iconname < right.iconname;
       
    47 }
       
    48 
       
    49 // Global variables
       
    50 
       
    51 static int counter = 0;
       
    52 static bool verboseOn = false;
       
    53 
       
    54 static int version = 1; // Current theme index format version
       
    55 
       
    56 QList<IndexItemInfo> IndexItems;
       
    57 
       
    58 QMap<QString, int> Strings;
       
    59 QByteArray StringBuffer;
       
    60 
       
    61 // ------
       
    62 
       
    63 QSize getDefaultSize(const QString &filename)
       
    64 {
       
    65     HbIconSource source(filename);
       
    66     return source.defaultSize().toSize();
       
    67 }
       
    68 
       
    69 int getStringOffset(const QString &string)
       
    70 {
       
    71     int offset = Strings.value(string, -1);
       
    72     if (offset < 0) {
       
    73         // Allocate new string in the string buffer
       
    74         offset = StringBuffer.size();
       
    75         StringBuffer.append(string.toLatin1());
       
    76         StringBuffer.append('\0');
       
    77         // Add offset to the target paths list
       
    78         Strings.insert(string, offset);
       
    79     }
       
    80     return offset;
       
    81 }
       
    82 
       
    83 void processFile(const QFileInfo &info, const QString &themename)
       
    84 {    
       
    85     QString fullFilename = info.absoluteFilePath();
       
    86     QString filename = info.fileName();
       
    87 
       
    88     if (filename.endsWith(".svg") ||
       
    89         filename.endsWith(".png") ||
       
    90         filename.endsWith(".mng") ||
       
    91         filename.endsWith(".gif") ||
       
    92         filename.endsWith(".xpm") ||
       
    93         filename.endsWith(".jpg") ||
       
    94         filename.endsWith(".nvg") ||
       
    95         filename.endsWith(".svgz") ||
       
    96         filename.endsWith(".qpic")) {
       
    97 
       
    98         IndexItemInfo itemInfo;
       
    99 
       
   100         QString targetPath;
       
   101 
       
   102         // If not "hbdefault", which is in resource file, resolve target path for the icon
       
   103         if (!fullFilename.startsWith(':') && 
       
   104             !fullFilename.contains("icons/hbdefault") &&
       
   105             !fullFilename.contains("icons\\hbdefault")) {
       
   106 
       
   107             if (fullFilename.contains("scalable")) {
       
   108                 targetPath = "/resource/hb/themes/icons/" + themename + "/scalable/";
       
   109             } else {
       
   110                 targetPath = "/resource/hb/themes/icons/" + themename + "/pixmap/";
       
   111             }
       
   112         } else {
       
   113             // Resource file target path, used with "hbdefault" theme
       
   114             if (fullFilename.contains("scalable")) {
       
   115                 targetPath = ":/themes/icons/hbdefault/scalable/";
       
   116             } else {
       
   117                 targetPath = ":/themes/icons/hbdefault/pixmap/";
       
   118             }
       
   119         }
       
   120 
       
   121         itemInfo.item.folderOffset = getStringOffset(targetPath);
       
   122         itemInfo.item.extOffset = getStringOffset(filename.mid(filename.lastIndexOf('.')));
       
   123 
       
   124         // Define iconname (remove file extension)
       
   125         QString iconname;
       
   126 
       
   127         int extIndex = filename.lastIndexOf('.');
       
   128         if (extIndex > 0) {
       
   129             iconname = filename.left(extIndex);
       
   130         } else {
       
   131             iconname = filename;
       
   132         }
       
   133 
       
   134         itemInfo.item.iconnameOffset = getStringOffset(iconname);
       
   135         itemInfo.iconname = iconname;
       
   136 
       
   137         // Define default size
       
   138         itemInfo.item.defaultSize = getDefaultSize(fullFilename);
       
   139 
       
   140         QString mirroredFilepath = fullFilename;
       
   141         
       
   142         // Define mirrored filename if there is a separate mirrored version of the
       
   143         // icon in 'mirrored' folder and in that case get also its default size
       
   144 
       
   145         int index1 = mirroredFilepath.lastIndexOf('/');
       
   146         int index2 = mirroredFilepath.lastIndexOf('\\');
       
   147 
       
   148         int index = index1 < index2 ? index2 : index1;
       
   149 
       
   150         if (index>0) {
       
   151             mirroredFilepath = mirroredFilepath.left(index);
       
   152             mirroredFilepath.append(QString("/mirrored/"));
       
   153 
       
   154             QStringList extList;
       
   155             extList << ".svg" << ".png" << ".mng" << ".gif" << ".xpm" << ".jpg" << ".nvg" << ".svgz" << ".qpic";
       
   156 
       
   157             foreach(QString ext, extList) {
       
   158                 QString mirroredFilenameCandidate = mirroredFilepath + iconname + ext;
       
   159 
       
   160                 if (QFile::exists(mirroredFilenameCandidate)) {
       
   161                     itemInfo.item.mirroredExtOffset = getStringOffset(ext);
       
   162                     itemInfo.item.mirroredDefaultSize = getDefaultSize(mirroredFilenameCandidate);
       
   163                     break;
       
   164                 }
       
   165             }
       
   166         }
       
   167 
       
   168         bool alreadyExists = false;
       
   169 
       
   170         // Check if there is already an item with the same iconname in the index
       
   171         foreach(const IndexItemInfo &info, IndexItems) {
       
   172             if (info.iconname == itemInfo.iconname) {
       
   173                 alreadyExists = true;
       
   174                 break;
       
   175             }
       
   176         }
       
   177 
       
   178         if (!alreadyExists) {
       
   179             IndexItems.append(itemInfo);
       
   180 
       
   181             if (verboseOn) {
       
   182             std::cout << "----------------------------------------------------------------\n";
       
   183             std::cout << "Added item" << counter << "\n";
       
   184             std::cout << "Iconname:" << &StringBuffer.data()[itemInfo.item.iconnameOffset] << "\n";
       
   185             std::cout << "Folder:" << &StringBuffer.data()[itemInfo.item.folderOffset] << "\n";
       
   186             std::cout << "Extension:" << &StringBuffer.data()[itemInfo.item.extOffset] << "\n";
       
   187             std::cout << "Default size: width: " << itemInfo.item.defaultSize.width() << " height: " << itemInfo.item.defaultSize.height() << "\n";
       
   188                 if (itemInfo.item.mirroredExtOffset >= 0) {
       
   189                 std::cout << "Mirrored extension:" << &StringBuffer.data()[itemInfo.item.mirroredExtOffset] << "\n";
       
   190                 } else {
       
   191                 std::cout << "Mirrored extension: <empty>\n";
       
   192                 }
       
   193             std::cout << "Mirrored default size: width:" << itemInfo.item.mirroredDefaultSize.width() << " height: " << itemInfo.item.mirroredDefaultSize.height() << "\n";
       
   194             }
       
   195             counter++;
       
   196         } else { // Icon already added in index with some other extension, do not add duplicates
       
   197             if (verboseOn) {
       
   198                 std::cout << "----------------------------------------------------------------\n";
       
   199                 std::cout << "WARNING! Skipped already existing icon:" << fullFilename.toStdString() << "\n";
       
   200             }
       
   201         }
       
   202     }
       
   203 }
       
   204 
       
   205 void adjustOffsets() {
       
   206     int adjustment = sizeof(HbThemeIndexHeaderV1) + IndexItems.count() * sizeof(HbThemeIndexItem);
       
   207 
       
   208     for (int i = 0; i<IndexItems.count(); ++i) {
       
   209         IndexItems[i].item.iconnameOffset += adjustment;
       
   210         IndexItems[i].item.folderOffset += adjustment;        
       
   211         IndexItems[i].item.extOffset += adjustment;
       
   212 
       
   213         if (IndexItems[i].item.mirroredExtOffset >= 0) {
       
   214             IndexItems[i].item.mirroredExtOffset += adjustment;
       
   215         }
       
   216     }
       
   217 }
       
   218 
       
   219 void processDir(const QDir &dir, const QString &themename, const QString targetName, bool subDir = false)
       
   220 {
       
   221     if (!subDir) {
       
   222         IndexItems.clear();
       
   223         Strings.clear();
       
   224         StringBuffer.clear();
       
   225     }
       
   226 
       
   227     QFileInfoList entries = dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot);
       
   228     for (int i=0; i<entries.count(); i++) {
       
   229         QFileInfo info = entries.at(i);
       
   230         QString file = info.absoluteFilePath();
       
   231         if (info.isDir()) {
       
   232             // Process subdirs recursively
       
   233             QDir subDir(file);
       
   234             processDir(subDir, themename, targetName, true);
       
   235         }
       
   236         // Process file
       
   237         processFile(info, themename);
       
   238     }
       
   239 
       
   240     if (!subDir) {
       
   241         QDir targetDir(targetName);
       
   242         if (!targetDir.exists()) {
       
   243             targetDir.mkpath(targetName);
       
   244         }
       
   245         QString filename = targetName + themename + ".themeindex";
       
   246 
       
   247         QFile::remove(filename);
       
   248         QFile indexFile(filename);
       
   249         if (!indexFile.open(QIODevice::ReadWrite)) {
       
   250             std::cout << "ERROR: could not open index file!\n";
       
   251             return;
       
   252         }
       
   253         
       
   254         // Write the header in the beginning of the file
       
   255         HbThemeIndexHeaderV1 header;
       
   256         header.version = version;
       
   257         header.count = IndexItems.count();
       
   258         
       
   259         qint64 ret = indexFile.write(reinterpret_cast<const char *>(&header), sizeof(HbThemeIndexHeaderV1));
       
   260         assert(ret == sizeof(HbThemeIndexHeaderV1));
       
   261 
       
   262         // Sort the list
       
   263         qSort(IndexItems);
       
   264 
       
   265         // Fix offsets in the items to be based on the beginning of the theme index instead of
       
   266         // the beginning of the string buffer area.
       
   267         adjustOffsets();
       
   268 
       
   269         // Write the items in the file stream
       
   270         foreach(const IndexItemInfo &itemInfo, IndexItems) {
       
   271             ret = indexFile.write(reinterpret_cast<const char *>(&itemInfo.item), sizeof(HbThemeIndexItem));
       
   272             assert(ret == sizeof(HbThemeIndexItem));
       
   273         }
       
   274 
       
   275         // Write the string buffer in the stream
       
   276         ret = indexFile.write(StringBuffer.constData(), StringBuffer.size());
       
   277         assert(ret == StringBuffer.size());
       
   278         indexFile.close();    
       
   279     }
       
   280 }
       
   281 
       
   282 void showHelp() {
       
   283     std::cout << "Themeindexer.exe usage:\n\n";
       
   284     std::cout << "themeindexer [-v] -f filename OR -n themename -s theme icons source directory -t theme index file target directory\n\n";
       
   285 
       
   286     std::cout << "-n \t\tname of index file (\"<themename>.themeindex\").\n";
       
   287     std::cout << "-s \t\ticons source directory is scanned recursively and all the";
       
   288     std::cout << "\t\t\trecognized icon files are aded in the theme index.\n";
       
   289     std::cout << "-t \t\ttarget directory for the index file.\n";
       
   290 
       
   291     std::cout << "-f <filename>\tfile which contains multiple themes to be indexed. Each in its own row.\n";
       
   292     std::cout << "-v \t\tverbose output\n\n";
       
   293 
       
   294     std::cout << "Example 1:\n";
       
   295     std::cout << "Themeindexer.exe -n theme1 -s c:/themes/icons/theme1/ -t c:/temp/\n\n";
       
   296     std::cout << "Example 2:\n";
       
   297     std::cout << "Themeindexer.exe -f c:/mythemes/themes.txt\n\n";
       
   298 }
       
   299 
       
   300 void loadHbResource()
       
   301 {
       
   302     bool loadSuccess;
       
   303     // To load resources embedded in hb library
       
   304     QString resourceLibName(RESOURCE_LIB_NAME);
       
   305     QLibrary hbLib(resourceLibName);
       
   306     loadSuccess = hbLib.load();
       
   307     
       
   308     if ( !loadSuccess ) {
       
   309         // Library may not be loaded, if it was built in debug mode and the name in debug mode is
       
   310         // different, change the name to debug version in that scenario
       
   311 #ifdef Q_OS_WIN32
       
   312         resourceLibName += WIN32_DEBUG_SUFFIX;
       
   313 #elif defined(Q_OS_MAC)
       
   314         resourceLibName += MAC_DEBUG_SUFFIX;
       
   315 #endif
       
   316         // On symbian library name in debug mode is same as that in release mode,
       
   317         // so no need to do anything for that
       
   318         hbLib.setFileName(resourceLibName);
       
   319         loadSuccess = hbLib.load();
       
   320     }
       
   321 }
       
   322 
       
   323 int main(int argc, char *argv[])
       
   324 {
       
   325     QApplication app(argc, argv);
       
   326 
       
   327     if (argc <= 2) {
       
   328         showHelp();
       
   329     } else {
       
   330         // Load HbCore resource to be able to index hbdefault theme
       
   331         loadHbResource();
       
   332 
       
   333         QString filename;
       
   334         QString themename;
       
   335         QDir basedir;
       
   336         QString targetname;
       
   337         QStringList args(app.arguments());
       
   338 
       
   339         for (int n = 0; n < args.count(); n++) {
       
   340             if (args[n].toLower() == "-n") {
       
   341                 themename = args[n+1];
       
   342                 n++;
       
   343             } else if (args[n].toLower() == "-s") {
       
   344                 basedir = QDir(args[n+1]);
       
   345                 n++;
       
   346             } else if (args[n].toLower() == "-t") {
       
   347                 targetname = args[n+1];
       
   348                 n++;
       
   349             } else if (args[n].toLower() == "-v") {
       
   350                 verboseOn = true;
       
   351             } else if (args[n].toLower() == "-f") {
       
   352                 filename = args[n+1];
       
   353             }
       
   354         }
       
   355 
       
   356 
       
   357         if (filename.length() > 0) {
       
   358             if (!QFile::exists(filename)) {
       
   359                 std::cout << "Error: file " << filename.toStdString() << " does not exist.\n";
       
   360             } else {
       
   361                 // Open file and parse lines. Each line should have three value separated with:
       
   362                 QFile themesToBeIndexed(filename);
       
   363                 if (themesToBeIndexed.open(QIODevice::ReadOnly | QIODevice::Text)) {
       
   364                     QTextStream in(&themesToBeIndexed);
       
   365 
       
   366                     while(!in.atEnd()) {
       
   367                         QString line = in.readLine();
       
   368 
       
   369                         QStringList values = line.split(' ');
       
   370                         if (values.count() == 3) {
       
   371                             themename = values[0];
       
   372                             basedir = values[1];
       
   373                             targetname = values[2];
       
   374 
       
   375                             targetname.replace('\\', '/');
       
   376                             // Check that targetname has / at the end
       
   377                             if (!targetname.endsWith('/')) {
       
   378                                 targetname.append('/');
       
   379                             }
       
   380                             processDir(basedir, themename, targetname);
       
   381                         }
       
   382                     }
       
   383 
       
   384                     themesToBeIndexed.close();
       
   385 
       
   386                     // Loop through themes string list and call processDir
       
   387                 } else {
       
   388                     std::cout << "Error: file " << filename.toStdString() << " could not be opened.\n";
       
   389                 }
       
   390             }
       
   391         } else {
       
   392             // Index only given theme
       
   393 
       
   394             targetname.replace('\\', '/');
       
   395             // Check that targetname has / at the end
       
   396             if (!targetname.endsWith('/')) {
       
   397                 targetname.append('/');
       
   398             }
       
   399 
       
   400             processDir(basedir, themename, targetname);
       
   401 
       
   402         }
       
   403     }
       
   404 
       
   405     return 0;
       
   406 }
       
   407