--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hbtools/hbthemeindexer/main.cpp Fri May 14 16:09:54 2010 +0300
@@ -0,0 +1,506 @@
+/****************************************************************************
+**
+** 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 HbTools 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 <hbiconsource_p.h>
+#include <hbthemeindex_p.h>
+#include <assert.h>
+#include <iostream>
+#include <QApplication>
+#include <QStringList>
+#include <QTextStream>
+#include <QFileInfo>
+#include <QLibrary>
+#include <QString>
+#include <QFile>
+#include <QMap>
+#include <QDir>
+
+#define RESOURCE_LIB_NAME "HbCore"
+#define WIN32_DEBUG_SUFFIX "d"
+#define MAC_DEBUG_SUFFIX "_debug"
+
+
+// Global variables
+static bool verboseOn = false;
+static quint32 version = 1; // Current theme index format version
+
+QList<HbThemeIndexItemData> IndexItems;
+QMap<quint32, QString> AddedItems;
+
+// Lists that hold icons to be automatically mirrored or icons that are locked
+QStringList MirroredList;
+QStringList LockedList;
+
+
+// ------
+
+
+void createMirroredList(const QString &fullThemePath)
+{
+ std::cout << "Parsing mirrored list for theme " << fullThemePath.toStdString() << "\n";
+ // Find mirrored.txt file
+ QString filename = fullThemePath + "/mirrored.txt";
+ // Try to read file
+ QFile file(filename);
+ if (file.open(QIODevice::ReadOnly)) {
+ QString line;
+
+ while(!file.atEnd()) {
+ QByteArray dirtyLine = file.readLine();
+ line = QString(dirtyLine).trimmed();
+ // Skip empty lines and comment lines
+ if (line.isEmpty() || line.at(0) == '#') {
+ continue;
+ }
+ MirroredList.append(line);
+ }
+ }
+}
+
+void createLockedList(const QString &fullThemePath)
+{
+ std::cout << "Parsing locked list for theme " << fullThemePath.toStdString() << "\n";
+ // Find locked.txt file
+ QString filename = fullThemePath + "/locked.txt";
+ // Try to read file
+ QFile file(filename);
+ if (file.open(QIODevice::ReadOnly)) {
+ QString line;
+
+ while(!file.atEnd()) {
+ QByteArray dirtyLine = file.readLine();
+ line = QString(dirtyLine).trimmed();
+ // Skip empty lines and comment lines
+ if (line.isEmpty() || line.at(0) == '#') {
+ continue;
+ }
+ LockedList.append(line);
+ }
+ }
+}
+
+QSize getDefaultSize(const QString &filename)
+{
+ HbIconSource source(filename);
+ return source.defaultSize().toSize();
+}
+
+void appendItem(HbThemeIndexItemData &itemData, const QString &itemName)
+{
+ bool alreadyExists = false;
+
+ if (AddedItems.contains(itemData.itemNameHash)) {
+ // If there already is item with same hash, it means either:
+ // - same icon is in both scalable and pixmap folder and then we won't append it again
+ // - hashing has failed, it generated same hash for 2 different strings -> TODO: do we care? We could e.g. save hash seed in index...
+ if (AddedItems.value(itemData.itemNameHash) == itemName) {
+ alreadyExists = true;
+ } else {
+ // Two different strings have same hash!!!
+ std::cout << "ERROR: Two different strings (" << AddedItems.value(itemData.itemNameHash).toStdString() << ", " << itemName.toStdString() << ") have same hash\n";
+ alreadyExists = true;
+ }
+ }
+
+ if (!alreadyExists) {
+ IndexItems.append(itemData);
+ AddedItems.insert(itemData.itemNameHash, itemName);
+ }
+
+ if (verboseOn) {
+ if (!alreadyExists) {
+ std::cout << "----------------------------------------------------------------\n";
+ std::cout << "Added item" << IndexItems.count() << "\n";
+ std::cout << "Item name: " << itemName.toStdString() << " hash: " << itemData.itemNameHash << "\n";
+ std::cout << "Item type: " << itemData.itemType << "\n";
+ if (itemData.itemType == HbThemeIndexItemData::SvgItem ||
+ itemData.itemType == HbThemeIndexItemData::PngItem ||
+ itemData.itemType == HbThemeIndexItemData::MngItem ||
+ itemData.itemType == HbThemeIndexItemData::GifItem ||
+ itemData.itemType == HbThemeIndexItemData::XpmItem ||
+ itemData.itemType == HbThemeIndexItemData::JpgItem ||
+ itemData.itemType == HbThemeIndexItemData::NvgItem ||
+ itemData.itemType == HbThemeIndexItemData::SvgzItem ||
+ itemData.itemType == HbThemeIndexItemData::QpicItem) {
+
+ std::cout << "Default size: width: " << itemData.defaultWidth << " height: " << itemData.defaultHeight << "\n";
+
+ if (itemData.mirroredItemType != HbThemeIndexItemData::NotDefined) {
+ std::cout << "Mirrored default size: width:" << itemData.mirroredWidth << " height: " << itemData.mirroredHeight << "\n";
+ }
+
+ if (itemData.flags & HbThemeIndexItemData::Mirrorable) {
+ std::cout << "Icon is automatically mirrored\n";
+ }
+ }
+ if (itemData.flags & HbThemeIndexItemData::Locked) {
+ std::cout << "Item is locked\n";
+ }
+ std::cout << "----------------------------------------------------------------\n\n";
+ } else { // Item already added in index with some other extension, do not add duplicates
+ std::cout << "----------------------------------------------------------------\n";
+ std::cout << "WARNING! Skipped already existing item:" << itemName.toStdString() << "\n";
+ }
+ }
+}
+
+HbThemeIndexItemData::Type getItemType(const QString &itemName)
+{
+ if (itemName.endsWith(".svg")) {
+ return HbThemeIndexItemData::SvgItem;
+ } else if (itemName.endsWith(".png")) {
+ return HbThemeIndexItemData::PngItem;
+ } else if (itemName.endsWith(".mng")) {
+ return HbThemeIndexItemData::MngItem;
+ } else if (itemName.endsWith(".gif")) {
+ return HbThemeIndexItemData::GifItem;
+ } else if (itemName.endsWith(".xpm")) {
+ return HbThemeIndexItemData::XpmItem;
+ } else if (itemName.endsWith(".jpg")) {
+ return HbThemeIndexItemData::JpgItem;
+ } else if (itemName.endsWith(".nvg")) {
+ return HbThemeIndexItemData::NvgItem;
+ } else if (itemName.endsWith(".svgz")) {
+ return HbThemeIndexItemData::SvgzItem;
+ } else if (itemName.endsWith(".qpic")) {
+ return HbThemeIndexItemData::QpicItem;
+ } else if (itemName.endsWith(".fxml")) {
+ return HbThemeIndexItemData::FxmlItem;
+ } else if (itemName.endsWith(".axml")) {
+ return HbThemeIndexItemData::AxmlItem;
+ }
+
+ return HbThemeIndexItemData::NotDefined;
+}
+
+void processFile(const QFileInfo &info) //, const QString &themename)
+{
+ QString fullFilename = info.absoluteFilePath();
+ QString filename = info.fileName();
+
+ HbThemeIndexItemData itemData;
+
+ // First get correct item type
+ itemData.itemType = getItemType(filename);
+
+ switch (itemData.itemType) {
+ case HbThemeIndexItemData::SvgItem: // fallback all icon types
+ case HbThemeIndexItemData::PngItem:
+ case HbThemeIndexItemData::MngItem:
+ case HbThemeIndexItemData::GifItem:
+ case HbThemeIndexItemData::XpmItem:
+ case HbThemeIndexItemData::JpgItem:
+ case HbThemeIndexItemData::NvgItem:
+ case HbThemeIndexItemData::SvgzItem:
+ case HbThemeIndexItemData::QpicItem:
+ {
+ // Define fileName (remove file extension)
+ QString iconname;
+ // If we come here, the filename must end with .* (e.g. .svg)
+ iconname = filename.left(filename.lastIndexOf('.'));
+
+ itemData.itemNameHash = HbThemeIndex::hash(iconname);
+
+ // Define default size
+ QSize defaultSize = getDefaultSize(fullFilename);
+ itemData.defaultWidth = static_cast<quint32>(defaultSize.width());
+ itemData.defaultHeight = static_cast<quint32>(defaultSize.height());
+
+ QString mirroredFilepath = fullFilename;
+
+ // Define mirrored filename if there is a separate mirrored version of the
+ // icon in 'mirrored' folder and in that case get also its default size
+
+ int index1 = mirroredFilepath.lastIndexOf('/');
+ int index2 = mirroredFilepath.lastIndexOf('\\');
+
+ int index = index1 < index2 ? index2 : index1;
+
+ if (index>0) {
+ mirroredFilepath = mirroredFilepath.left(index);
+ mirroredFilepath.append(QString("/mirrored/"));
+
+ QStringList extList;
+ extList << ".svg" << ".png" << ".mng" << ".gif" << ".xpm" << ".jpg" << ".nvg" << ".svgz" << ".qpic";
+
+ foreach(QString ext, extList) {
+ QString mirroredFilenameCandidate = mirroredFilepath + iconname + ext;
+
+ if (QFile::exists(mirroredFilenameCandidate)) {
+ itemData.mirroredItemType = getItemType(mirroredFilenameCandidate);
+ // Define mirrored icon size
+ QSize mirroredSize = getDefaultSize(mirroredFilenameCandidate);
+ itemData.mirroredWidth = static_cast<quint32>(mirroredSize.width());
+ itemData.mirroredHeight = static_cast<quint32>(mirroredSize.height());
+ break;
+ }
+ }
+ }
+
+ if (MirroredList.contains(iconname)) {
+ itemData.flags |= HbThemeIndexItemData::Mirrorable;
+ // Remove all found items from the list so that in the end we can handle with missing items.
+ MirroredList.removeOne(iconname);
+ }
+
+ if (LockedList.contains(iconname)) {
+ itemData.flags |= HbThemeIndexItemData::Locked;
+ // Remove all found items from the list so that in the end we can handle with missing items.
+ LockedList.removeOne(iconname);
+ }
+ appendItem(itemData, iconname);
+ break;
+ }
+
+ case HbThemeIndexItemData::AxmlItem: // fallback, these are handled same way
+ case HbThemeIndexItemData::FxmlItem:
+ {
+ // Define fileName (file extension not removed)
+ itemData.itemNameHash = HbThemeIndex::hash(filename);
+
+ if (LockedList.contains(filename)) {
+ itemData.flags |= HbThemeIndexItemData::Locked;
+ // Remove all found items from the list so that in the end we can handle with missing items.
+ LockedList.removeOne(filename);
+ }
+ appendItem(itemData, filename);
+ break;
+ }
+
+ default:
+ {
+ // Don't append unknown items to the index.
+ break;
+ }
+ } // end switch
+}
+
+bool themeIndexItemDataLessThan(const HbThemeIndexItemData &d1, const HbThemeIndexItemData &d2)
+{
+ return d1.itemNameHash < d2.itemNameHash;
+}
+
+void processDir(const QDir &dir, const QString &themename, const QString targetName, bool subDir = false)
+{
+ if (!subDir) {
+ IndexItems.clear();
+ AddedItems.clear();
+ MirroredList.clear();
+ LockedList.clear();
+ createMirroredList(dir.absolutePath()+"/icons/"+themename);
+ createLockedList(dir.absolutePath()+"/icons/"+themename);
+ }
+
+ QFileInfoList entries = dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot);
+ for (int i=0; i<entries.count(); i++) {
+ QFileInfo info = entries.at(i);
+ QString file = info.absoluteFilePath();
+ if (info.isDir()) {
+ // Process subdirs recursively
+ QDir subDir(file);
+ processDir(subDir, themename, targetName, true);
+ }
+ // Process file
+ if (file.contains("/" + themename + "/", Qt::CaseInsensitive)) {
+ processFile(info);
+ }
+ }
+
+ if (!subDir) {
+ // There might still be items in mirrored list (e.g. frames are not actual items, but they are still mirrored)
+ // So just create empty items for the rest of mirrored items in list
+ foreach (QString mirrored, MirroredList) {
+ HbThemeIndexItemData itemData;
+ itemData.itemNameHash = HbThemeIndex::hash(mirrored);
+ itemData.flags |= HbThemeIndexItemData::Mirrorable;
+ appendItem(itemData, mirrored);
+ }
+ QDir targetDir(targetName);
+ if (!targetDir.exists()) {
+ targetDir.mkpath(targetName);
+ }
+ QString filename = targetName + themename + ".themeindex";
+
+ QFile::remove(filename);
+ QFile indexFile(filename);
+ if (!indexFile.open(QIODevice::ReadWrite)) {
+ std::cout << "ERROR: could not open index file!\n";
+ return;
+ }
+
+ // Write the header in the beginning of the file
+ HbThemeIndexHeaderV1 header;
+ header.version = version;
+ header.itemCount = IndexItems.count();
+
+ if (verboseOn) {
+ std::cout << "============================TOTALS==============================\n";
+ std::cout << "Added " << header.itemCount << " items.\n";
+ std::cout << "================================================================\n";
+ }
+
+ // Sort the list
+ qStableSort(IndexItems.begin(), IndexItems.end(), themeIndexItemDataLessThan);
+
+ // Write header info into the file stream
+ qint64 ret = indexFile.write(reinterpret_cast<const char *>(&header), sizeof(HbThemeIndexHeaderV1));
+ assert(ret == sizeof(HbThemeIndexHeaderV1));
+
+ // Write items into the file stream
+ foreach(const HbThemeIndexItemData &itemData, IndexItems) {
+ ret = indexFile.write(reinterpret_cast<const char *>(&itemData), sizeof(HbThemeIndexItemData));
+ assert(ret == sizeof(HbThemeIndexItemData));
+ }
+
+ indexFile.close();
+ }
+}
+
+void showHelp() {
+ std::cout << "Themeindexer.exe usage:\n\n";
+ std::cout << "hbthemeindexer [-v] -f filename OR -n themename -s themes source directory -t theme index file target directory\n\n";
+
+ std::cout << "-n \t\ttheme to be indexed (\"<themename>.themeindex\").\n";
+ std::cout << "-s \t\tthemes source directory is scanned recursively and all the";
+ std::cout << "\t\t\trecognized resource files for given theme are aded in the theme index.\n";
+ std::cout << "-t \t\ttarget directory for the index file.\n";
+
+ std::cout << "-f <filename>\tfile which contains multiple themes to be indexed. Each in its own row.\n";
+ std::cout << "-v \t\tverbose output\n\n";
+
+ std::cout << "Example 1:\n";
+ std::cout << "Themeindexer.exe -n mytheme -s c:/mythemes -t c:/mythemes\n";
+ std::cout << "Example 2:\n";
+ std::cout << "Themeindexer.exe -f c:/mythemes/themes.txt\n\n";
+}
+
+void loadHbResource()
+{
+ bool loadSuccess;
+ // To load resources embedded in hb library
+ QString resourceLibName(RESOURCE_LIB_NAME);
+ QLibrary hbLib(resourceLibName);
+ loadSuccess = hbLib.load();
+
+ if ( !loadSuccess ) {
+ // Library may not be loaded, if it was built in debug mode and the name in debug mode is
+ // different, change the name to debug version in that scenario
+#ifdef Q_OS_WIN32
+ resourceLibName += WIN32_DEBUG_SUFFIX;
+#elif defined(Q_OS_MAC)
+ resourceLibName += MAC_DEBUG_SUFFIX;
+#endif
+ // On symbian library name in debug mode is same as that in release mode,
+ // so no need to do anything for that
+ hbLib.setFileName(resourceLibName);
+ loadSuccess = hbLib.load();
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv, false); // GUIenabled=false
+
+ if (argc <= 2) {
+ showHelp();
+ } else {
+ // Load HbCore resource to be able to index hbdefault theme
+ loadHbResource();
+
+ QString filename;
+ QString themename;
+ QDir basedir;
+ QString targetname;
+ QStringList args(app.arguments());
+
+ for (int n = 0; n < args.count(); n++) {
+ if (args[n].toLower() == "-n") {
+ themename = args[n+1];
+ n++;
+ } else if (args[n].toLower() == "-s") {
+ basedir = QDir(args[n+1]);
+ n++;
+ } else if (args[n].toLower() == "-t") {
+ targetname = args[n+1];
+ n++;
+ } else if (args[n].toLower() == "-v") {
+ verboseOn = true;
+ } else if (args[n].toLower() == "-f") {
+ filename = args[n+1];
+ }
+ }
+
+ if (filename.length() > 0) {
+ if (!QFile::exists(filename)) {
+ std::cout << "Error: file " << filename.toStdString() << " does not exist.\n";
+ } else {
+ // Open file and parse lines. Each line should have three value separated with:
+ QFile themesToBeIndexed(filename);
+ if (themesToBeIndexed.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QTextStream in(&themesToBeIndexed);
+
+ while(!in.atEnd()) {
+ QString line = in.readLine();
+
+ QStringList values = line.split(' ');
+ if (values.count() == 3) {
+ themename = values[0];
+ basedir = values[1];
+ targetname = values[2];
+
+ targetname.replace('\\', '/');
+ // Check that targetname has / at the end
+ if (!targetname.endsWith('/')) {
+ targetname.append('/');
+ }
+ processDir(basedir, themename, targetname);
+ }
+ }
+
+ themesToBeIndexed.close();
+
+ // Loop through themes string list and call processDir
+ } else {
+ std::cout << "Error: file " << filename.toStdString() << " could not be opened.\n";
+ }
+ }
+ } else {
+ // Index only given theme
+
+ targetname.replace('\\', '/');
+ // Check that targetname has / at the end
+ if (!targetname.endsWith('/')) {
+ targetname.append('/');
+ }
+
+ processDir(basedir, themename, targetname);
+
+ }
+ }
+
+ IndexItems.clear();
+ return 0;
+}
+