| /****************************************************************************  | 
| **  | 
| ** Copyright (C) 2016 The Qt Company Ltd.  | 
| ** Contact: https://www.qt.io/licensing/  | 
| **  | 
| ** This file is part of the Qt VS Tools.  | 
| **  | 
| ** $QT_BEGIN_LICENSE:GPL-EXCEPT$  | 
| ** Commercial License Usage  | 
| ** Licensees holding valid commercial Qt licenses may use this file in  | 
| ** accordance with the commercial license agreement provided with the  | 
| ** Software or, alternatively, in accordance with the terms contained in  | 
| ** a written agreement between you and The Qt Company. For licensing terms  | 
| ** and conditions see https://www.qt.io/terms-conditions. For further  | 
| ** information use the contact form at https://www.qt.io/contact-us.  | 
| **  | 
| ** GNU General Public License Usage  | 
| ** Alternatively, this file may be used under the terms of the GNU  | 
| ** General Public License version 3 as published by the Free Software  | 
| ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT  | 
| ** included in the packaging of this file. Please review the following  | 
| ** information to ensure the GNU General Public License requirements will  | 
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html.  | 
| **  | 
| ** $QT_END_LICENSE$  | 
| **  | 
| ****************************************************************************/  | 
|   | 
| #include "resourcefile_p.h"  | 
|   | 
| #include <QCoreApplication>  | 
| #include <QDebug>  | 
| #include <QDir>  | 
| #include <QFile>  | 
| #include <QMimeData>  | 
| #include <QtAlgorithms>  | 
| #include <QTextStream>  | 
|   | 
| #include <QIcon>  | 
| #include <QImageReader>  | 
|   | 
| #include <QDomDocument>  | 
|   | 
| QT_BEGIN_NAMESPACE  | 
|   | 
|   | 
| /*  | 
| TRANSLATOR qdesigner_internal::ResourceModel  | 
| */  | 
|   | 
| static QString msgFileNameEmpty()  | 
| {  | 
|     return QCoreApplication::translate("Designer", "The file name is empty.");  | 
| }  | 
|   | 
| namespace qdesigner_internal {  | 
|   | 
|   | 
| /******************************************************************************  | 
| ** FileList  | 
| */  | 
|   | 
| bool FileList::containsFile(File *file)  | 
| {  | 
|     foreach (const File *tmpFile, *this)  | 
|         if (tmpFile->name == file->name && tmpFile->prefix() == file->prefix())  | 
|             return true;  | 
|     return false;  | 
| }  | 
|   | 
| /******************************************************************************  | 
| ** ResourceFile  | 
| */  | 
|   | 
| ResourceFile::ResourceFile(const QString &file_name)  | 
| {  | 
|     setFileName(file_name);  | 
| }  | 
|   | 
| ResourceFile::~ResourceFile()  | 
| {  | 
|     clearPrefixList();  | 
| }  | 
|   | 
| bool ResourceFile::load()  | 
| {  | 
|     m_error_message.clear();  | 
|   | 
|     if (m_file_name.isEmpty()) {  | 
|         m_error_message = msgFileNameEmpty();  | 
|         return false;  | 
|     }  | 
|   | 
|     QFile file(m_file_name);  | 
|     if (!file.open(QIODevice::ReadOnly)) {  | 
|         m_error_message = file.errorString();  | 
|         return false;  | 
|     }  | 
|   | 
|     clearPrefixList();  | 
|   | 
|     QDomDocument doc;  | 
|   | 
|     QString error_msg;  | 
|     int error_line, error_col;  | 
|     if (!doc.setContent(&file, &error_msg, &error_line, &error_col)) {  | 
|         m_error_message = QCoreApplication::translate("Designer", "XML error on line %1, col %2: %3")  | 
|                     .arg(error_line).arg(error_col).arg(error_msg);  | 
|         return false;  | 
|     }  | 
|   | 
|     QDomElement root = doc.firstChildElement(QLatin1String("RCC"));  | 
|     if (root.isNull()) {  | 
|         m_error_message = QCoreApplication::translate("Designer", "The <RCC> root element is missing.");  | 
|         return false;  | 
|     }  | 
|   | 
|     QDomElement relt = root.firstChildElement(QLatin1String("qresource"));  | 
|     for (; !relt.isNull(); relt = relt.nextSiblingElement(QLatin1String("qresource"))) {  | 
|   | 
|         QString prefix = fixPrefix(relt.attribute(QLatin1String("prefix")));  | 
|         if (prefix.isEmpty())  | 
|             prefix = QString(QLatin1Char('/'));  | 
|         const QString language = relt.attribute(QLatin1String("lang"));  | 
|   | 
|         const int idx = indexOfPrefix(prefix);  | 
|         Prefix * p = 0;  | 
|         if (idx == -1) {  | 
|             p = new Prefix(prefix, language);  | 
|             m_prefix_list.append(p);  | 
|         } else {  | 
|             p = m_prefix_list[idx];  | 
|         }  | 
|         Q_ASSERT(p);  | 
|   | 
|         QDomElement felt = relt.firstChildElement(QLatin1String("file"));  | 
|         for (; !felt.isNull(); felt = felt.nextSiblingElement(QLatin1String("file"))) {  | 
|             const QString fileName = absolutePath(felt.text());  | 
|             const QString alias = felt.attribute(QLatin1String("alias"));  | 
|             File * const file = new File(p, fileName, alias);  | 
|             p->file_list.append(file);  | 
|         }  | 
|     }  | 
|   | 
|     return true;  | 
| }  | 
|   | 
| bool ResourceFile::save()  | 
| {  | 
|     m_error_message.clear();  | 
|   | 
|     if (m_file_name.isEmpty()) {  | 
|         m_error_message = msgFileNameEmpty();  | 
|         return false;  | 
|     }  | 
|   | 
|     QFile file(m_file_name);  | 
|     if (!file.open(QIODevice::WriteOnly)) {  | 
|         m_error_message = file.errorString();  | 
|         return false;  | 
|     }  | 
|   | 
|     QDomDocument doc;  | 
|     QDomElement root = doc.createElement(QLatin1String("RCC"));  | 
|     doc.appendChild(root);  | 
|   | 
|     const QStringList name_list = prefixList();  | 
|   | 
|     foreach (const QString &name, name_list) {  | 
|         FileList file_list;  | 
|         QString lang;  | 
|         foreach (const Prefix *pref, m_prefix_list) {  | 
|             if (pref->name == name){  | 
|                 file_list += pref->file_list;  | 
|                 lang = pref->lang;  | 
|             }  | 
|         }  | 
|   | 
|         QDomElement relt = doc.createElement(QLatin1String("qresource"));  | 
|         root.appendChild(relt);  | 
|         relt.setAttribute(QLatin1String("prefix"), name);  | 
|         if (!lang.isEmpty())  | 
|             relt.setAttribute(QLatin1String("lang"), lang);  | 
|   | 
|         foreach (const File *f, file_list) {  | 
|             const File &file = *f;  | 
|             QDomElement felt = doc.createElement(QLatin1String("file"));  | 
|             relt.appendChild(felt);  | 
|             const QString conv_file = relativePath(file.name).replace(QDir::separator(), QLatin1Char('/'));  | 
|             const QDomText text = doc.createTextNode(conv_file);  | 
|             felt.appendChild(text);  | 
|             if (!file.alias.isEmpty())  | 
|                 felt.setAttribute(QLatin1String("alias"), file.alias);  | 
|         }  | 
|     }  | 
|   | 
|     QTextStream stream(&file);  | 
|     doc.save(stream, 4);  | 
|   | 
|     return true;  | 
| }  | 
|   | 
| bool ResourceFile::split(const QString &_path, QString *prefix, QString *file) const  | 
| {  | 
|     prefix->clear();  | 
|     file->clear();  | 
|   | 
|     QString path = _path;  | 
|     if (!path.startsWith(QLatin1Char(':')))  | 
|         return false;  | 
|     path = path.mid(1);  | 
|   | 
|     for (int i = 0; i < m_prefix_list.size(); ++i) {  | 
|         Prefix const * const &pref = m_prefix_list.at(i);  | 
|         if (!path.startsWith(pref->name))  | 
|             continue;  | 
|   | 
|         *prefix = pref->name;  | 
|         if (pref->name == QString(QLatin1Char('/')))  | 
|             *file = path.mid(1);  | 
|         else  | 
|             *file = path.mid(pref->name.size() + 1);  | 
|   | 
|         const QString filePath = absolutePath(*file);  | 
|   | 
|         for (int j = 0; j < pref->file_list.count(); j++) {  | 
|             File const * const &f = pref->file_list.at(j);  | 
|             if (!f->alias.isEmpty()) {  | 
|                 if (absolutePath(f->alias) == filePath) {  | 
|                     *file = f->name;  | 
|                     return true;  | 
|                 }  | 
|             } else if (f->name == filePath)  | 
|                 return true;  | 
|         }  | 
|     }  | 
|   | 
|     return false;  | 
| }  | 
|   | 
| QString ResourceFile::resolvePath(const QString &path) const  | 
| {  | 
|     QString prefix, file;  | 
|     if (split(path, &prefix, &file))  | 
|         return absolutePath(file);  | 
|   | 
|     return QString();  | 
| }  | 
|   | 
| QStringList ResourceFile::prefixList() const  | 
| {  | 
|     QStringList result;  | 
|     for (int i = 0; i < m_prefix_list.size(); ++i)  | 
|         result.append(m_prefix_list.at(i)->name);  | 
|     return result;  | 
| }  | 
|   | 
| bool ResourceFile::isEmpty() const  | 
| {  | 
|     return m_file_name.isEmpty() && m_prefix_list.isEmpty();  | 
| }  | 
|   | 
| QStringList ResourceFile::fileList(int pref_idx) const  | 
| {  | 
|     QStringList result;  | 
|     Q_ASSERT(pref_idx >= 0 && pref_idx < m_prefix_list.count());  | 
|     const FileList &abs_file_list = m_prefix_list.at(pref_idx)->file_list;  | 
|     foreach (const File *abs_file, abs_file_list)  | 
|         result.append(relativePath(abs_file->name));  | 
|     return result;  | 
| }  | 
|   | 
| void ResourceFile::addFile(int prefix_idx, const QString &file, int file_idx)  | 
| {  | 
|     Prefix * const p = m_prefix_list[prefix_idx];  | 
|     Q_ASSERT(p);  | 
|     FileList &files = p->file_list;  | 
|     Q_ASSERT(file_idx >= -1 && file_idx <= files.size());  | 
|     if (file_idx == -1)  | 
|         file_idx = files.size();  | 
|     files.insert(file_idx, new File(p, absolutePath(file)));  | 
| }  | 
|   | 
| void ResourceFile::addPrefix(const QString &prefix, int prefix_idx)  | 
| {  | 
|     QString fixed_prefix = fixPrefix(prefix);  | 
|     if (indexOfPrefix(fixed_prefix) != -1)  | 
|         return;  | 
|   | 
|     Q_ASSERT(prefix_idx >= -1 && prefix_idx <= m_prefix_list.size());  | 
|     if (prefix_idx == -1)  | 
|         prefix_idx = m_prefix_list.size();  | 
|     m_prefix_list.insert(prefix_idx, new Prefix(fixed_prefix));  | 
| }  | 
|   | 
| void ResourceFile::removePrefix(int prefix_idx)  | 
| {  | 
|     Q_ASSERT(prefix_idx >= 0 && prefix_idx < m_prefix_list.count());  | 
|     Prefix * const p = m_prefix_list.at(prefix_idx);  | 
|     delete p;  | 
|     m_prefix_list.removeAt(prefix_idx);  | 
| }  | 
|   | 
| void ResourceFile::removeFile(int prefix_idx, int file_idx)  | 
| {  | 
|     Q_ASSERT(prefix_idx >= 0 && prefix_idx < m_prefix_list.count());  | 
|     FileList &fileList = m_prefix_list[prefix_idx]->file_list;  | 
|     Q_ASSERT(file_idx >= 0 && file_idx < fileList.count());  | 
|     delete fileList.at(file_idx);  | 
|     fileList.removeAt(file_idx);  | 
| }  | 
|   | 
| void ResourceFile::replacePrefix(int prefix_idx, const QString &prefix)  | 
| {  | 
|     Q_ASSERT(prefix_idx >= 0 && prefix_idx < m_prefix_list.count());  | 
|     m_prefix_list[prefix_idx]->name = fixPrefix(prefix);  | 
| }  | 
|   | 
| void ResourceFile::replaceLang(int prefix_idx, const QString &lang)  | 
| {  | 
|     Q_ASSERT(prefix_idx >= 0 && prefix_idx < m_prefix_list.count());  | 
|     m_prefix_list[prefix_idx]->lang = lang;  | 
| }  | 
|   | 
| void ResourceFile::replaceAlias(int prefix_idx, int file_idx, const QString &alias)  | 
| {  | 
|     Q_ASSERT(prefix_idx >= 0 && prefix_idx < m_prefix_list.count());  | 
|     FileList &fileList = m_prefix_list.at(prefix_idx)->file_list;  | 
|     Q_ASSERT(file_idx >= 0 && file_idx < fileList.count());  | 
|     fileList[file_idx]->alias = alias;  | 
| }  | 
|   | 
|   | 
| void ResourceFile::replaceFile(int pref_idx, int file_idx, const QString &file)  | 
| {  | 
|     Q_ASSERT(pref_idx >= 0 && pref_idx < m_prefix_list.count());  | 
|     FileList &fileList = m_prefix_list.at(pref_idx)->file_list;  | 
|     Q_ASSERT(file_idx >= 0 && file_idx < fileList.count());  | 
|     fileList[file_idx]->name = file;  | 
| }  | 
|   | 
| int ResourceFile::indexOfPrefix(const QString &prefix) const  | 
| {  | 
|     QString fixed_prefix = fixPrefix(prefix);  | 
|     for (int i = 0; i < m_prefix_list.size(); ++i) {  | 
|         if (m_prefix_list.at(i)->name == fixed_prefix)  | 
|             return i;  | 
|     }  | 
|     return -1;  | 
| }  | 
|   | 
| int ResourceFile::indexOfFile(int pref_idx, const QString &file) const  | 
| {  | 
|     Q_ASSERT(pref_idx >= 0 && pref_idx < m_prefix_list.count());  | 
|     Prefix * const p = m_prefix_list.at(pref_idx);  | 
|     File equalFile(p, absolutePath(file));  | 
|     return p->file_list.indexOf(&equalFile);  | 
| }  | 
|   | 
| QString ResourceFile::relativePath(const QString &abs_path) const  | 
| {  | 
|     if (m_file_name.isEmpty() || QFileInfo(abs_path).isRelative())  | 
|          return abs_path;  | 
|   | 
|     QFileInfo fileInfo(m_file_name);  | 
|     return fileInfo.absoluteDir().relativeFilePath(abs_path);  | 
| }  | 
|   | 
| QString ResourceFile::absolutePath(const QString &rel_path) const  | 
| {  | 
|     const QFileInfo fi(rel_path);  | 
|     if (fi.isAbsolute())  | 
|         return rel_path;  | 
|   | 
|     QString rc = QFileInfo(m_file_name).path();  | 
|     rc +=  QDir::separator();  | 
|     rc += rel_path;  | 
|     return QDir::cleanPath(rc);  | 
| }  | 
|   | 
| bool ResourceFile::contains(const QString &prefix, const QString &file) const  | 
| {  | 
|     int pref_idx = indexOfPrefix(prefix);  | 
|     if (pref_idx == -1)  | 
|         return false;  | 
|     if (file.isEmpty())  | 
|         return true;  | 
|     Q_ASSERT(pref_idx >= 0 && pref_idx < m_prefix_list.count());  | 
|     Prefix * const p = m_prefix_list.at(pref_idx);  | 
|     Q_ASSERT(p);  | 
|     File equalFile(p, absolutePath(file));  | 
|     return p->file_list.containsFile(&equalFile);  | 
| }  | 
|   | 
| bool ResourceFile::contains(int pref_idx, const QString &file) const  | 
| {  | 
|     Q_ASSERT(pref_idx >= 0 && pref_idx < m_prefix_list.count());  | 
|     Prefix * const p = m_prefix_list.at(pref_idx);  | 
|     File equalFile(p, absolutePath(file));  | 
|     return p->file_list.containsFile(&equalFile);  | 
| }  | 
|   | 
| /*static*/ QString ResourceFile::fixPrefix(const QString &prefix)  | 
| {  | 
|     const QChar slash = QLatin1Char('/');  | 
|     QString result = QString(slash);  | 
|     for (int i = 0; i < prefix.size(); ++i) {  | 
|         const QChar c = prefix.at(i);  | 
|         if (c == slash && result.at(result.size() - 1) == slash)  | 
|             continue;  | 
|         result.append(c);  | 
|     }  | 
|   | 
|     if (result.size() > 1 && result.endsWith(slash))  | 
|         result = result.mid(0, result.size() - 1);  | 
|   | 
|     return result;  | 
| }  | 
|   | 
| int ResourceFile::prefixCount() const  | 
| {  | 
|     return m_prefix_list.size();  | 
| }  | 
|   | 
| QString ResourceFile::prefix(int idx) const  | 
| {  | 
|     Q_ASSERT((idx >= 0) && (idx < m_prefix_list.count()));  | 
|     return m_prefix_list.at(idx)->name;  | 
| }  | 
|   | 
| QString ResourceFile::lang(int idx) const  | 
| {  | 
|     Q_ASSERT(idx >= 0 && idx < m_prefix_list.count());  | 
|     return m_prefix_list.at(idx)->lang;  | 
| }  | 
|   | 
| int ResourceFile::fileCount(int prefix_idx) const  | 
| {  | 
|     Q_ASSERT(prefix_idx >= 0 && prefix_idx < m_prefix_list.count());  | 
|     return m_prefix_list.at(prefix_idx)->file_list.size();  | 
| }  | 
|   | 
| QString ResourceFile::file(int prefix_idx, int file_idx) const  | 
| {  | 
|     Q_ASSERT(prefix_idx >= 0 && prefix_idx < m_prefix_list.count());  | 
|     FileList &fileList = m_prefix_list.at(prefix_idx)->file_list;  | 
|     Q_ASSERT(file_idx >= 0 && file_idx < fileList.count());  | 
|     return fileList.at(file_idx)->name;  | 
| }  | 
|   | 
| QString ResourceFile::alias(int prefix_idx, int file_idx) const  | 
| {  | 
|     Q_ASSERT(prefix_idx >= 0 && prefix_idx < m_prefix_list.count());  | 
|     FileList &fileList = m_prefix_list.at(prefix_idx)->file_list;  | 
|     Q_ASSERT(file_idx >= 0 && file_idx < fileList.count());  | 
|     return fileList.at(file_idx)->alias;  | 
| }  | 
|   | 
| void * ResourceFile::prefixPointer(int prefixIndex) const  | 
| {  | 
|     Q_ASSERT(prefixIndex >= 0 && prefixIndex < m_prefix_list.count());  | 
|     return m_prefix_list.at(prefixIndex);  | 
| }  | 
|   | 
| void * ResourceFile::filePointer(int prefixIndex, int fileIndex) const  | 
| {  | 
|     Q_ASSERT(prefixIndex >= 0 && prefixIndex < m_prefix_list.count());  | 
|     FileList &fileList = m_prefix_list.at(prefixIndex)->file_list;  | 
|     Q_ASSERT(fileIndex >= 0 && fileIndex < fileList.count());  | 
|     return fileList.at(fileIndex);  | 
| }  | 
|   | 
| int ResourceFile::prefixPointerIndex(const Prefix *prefix) const  | 
| {  | 
|     int const count = m_prefix_list.count();  | 
|     for (int i = 0; i < count; i++) {  | 
|         Prefix * const other = m_prefix_list.at(i);  | 
|         if (*other == *prefix)  | 
|             return i;  | 
|     }  | 
|     return -1;  | 
| }  | 
|   | 
| void ResourceFile::clearPrefixList()  | 
| {  | 
|     qDeleteAll(m_prefix_list);  | 
|     m_prefix_list.clear();  | 
| }  | 
|   | 
| /******************************************************************************  | 
| ** ResourceModel  | 
| */  | 
|   | 
| ResourceModel::ResourceModel(const ResourceFile &resource_file, QObject *parent)  | 
|     : QAbstractItemModel(parent), m_resource_file(resource_file),  m_dirty(false)  | 
| {  | 
| }  | 
|   | 
| void ResourceModel::setDirty(bool b)  | 
| {  | 
|     if (b == m_dirty)  | 
|         return;  | 
|   | 
|     m_dirty = b;  | 
|     emit dirtyChanged(b);  | 
| }  | 
|   | 
| QModelIndex ResourceModel::index(int row, int column, const QModelIndex &parent) const  | 
| {  | 
|     if (column != 0)  | 
|         return QModelIndex();  | 
|   | 
|     void * internalPointer = 0;  | 
|     if (parent.isValid()) {  | 
|         void * const pip = parent.internalPointer();  | 
|         if (pip == 0)  | 
|             return QModelIndex();  | 
|   | 
|         // File node  | 
|         Node * const node = reinterpret_cast<Node *>(pip);  | 
|         Prefix * const prefix = node->prefix();  | 
|         Q_ASSERT(prefix);  | 
|         if (row < 0 || row >= prefix->file_list.count())  | 
|             return QModelIndex();  | 
|         const int prefixIndex = m_resource_file.prefixPointerIndex(prefix);  | 
|         const int fileIndex = row;  | 
|         internalPointer = m_resource_file.filePointer(prefixIndex, fileIndex);  | 
|     } else {  | 
|         // Prefix node  | 
|         if (row < 0 || row >= m_resource_file.prefixCount())  | 
|             return QModelIndex();  | 
|         internalPointer = m_resource_file.prefixPointer(row);  | 
|     }  | 
|     Q_ASSERT(internalPointer);  | 
|     return createIndex(row, 0, internalPointer);  | 
| }  | 
|   | 
| QModelIndex ResourceModel::parent(const QModelIndex &index) const  | 
| {  | 
|     if (!index.isValid())  | 
|         return QModelIndex();  | 
|   | 
|     void * const internalPointer = index.internalPointer();  | 
|     if (internalPointer == 0)  | 
|         return QModelIndex();  | 
|     Node * const node = reinterpret_cast<Node *>(internalPointer);  | 
|     Prefix * const prefix = node->prefix();  | 
|     Q_ASSERT(prefix);  | 
|     bool const isFileNode = (prefix != node);  | 
|   | 
|     if (isFileNode) {  | 
|         const int row = m_resource_file.prefixPointerIndex(prefix);  | 
|         Q_ASSERT(row >= 0);  | 
|         return createIndex(row, 0, prefix);  | 
|     } else {  | 
|         return QModelIndex();  | 
|     }  | 
| }  | 
|   | 
| int ResourceModel::rowCount(const QModelIndex &parent) const  | 
| {  | 
|     if (parent.isValid()) {  | 
|         void * const internalPointer = parent.internalPointer();  | 
|         Node * const node = reinterpret_cast<Node *>(internalPointer);  | 
|         Prefix * const prefix = node->prefix();  | 
|         Q_ASSERT(prefix);  | 
|         bool const isFileNode = (prefix != node);  | 
|   | 
|         if (isFileNode) {  | 
|             return 0;  | 
|         } else {  | 
|             return prefix->file_list.count();  | 
|         }  | 
|     } else {  | 
|         return m_resource_file.prefixCount();  | 
|     }  | 
| }  | 
|   | 
| int ResourceModel::columnCount(const QModelIndex &) const  | 
| {  | 
|     return 1;  | 
| }  | 
|   | 
| bool ResourceModel::hasChildren(const QModelIndex &parent) const  | 
| {  | 
|     return rowCount(parent) != 0;  | 
| }  | 
|   | 
| Qt::DropActions ResourceModel::supportedDropActions() const  | 
| {  | 
|     // Only action that works for QListWidget and the like.  | 
|     return Qt::CopyAction;  | 
| }  | 
|   | 
| bool ResourceModel::iconFileExtension(const QString &path)  | 
| {  | 
|     static QStringList ext_list;  | 
|     if (ext_list.isEmpty()) {  | 
|         const QList<QByteArray> _ext_list = QImageReader::supportedImageFormats();  | 
|         foreach (const QByteArray &ext, _ext_list) {  | 
|             QString dotExt = QString(QLatin1Char('.'));  | 
|             dotExt  += QString::fromLocal8Bit(ext);  | 
|             ext_list.append(dotExt);  | 
|         }  | 
|     }  | 
|   | 
|     foreach (const QString &ext, ext_list) {  | 
|         if (path.endsWith(ext, Qt::CaseInsensitive))  | 
|             return true;  | 
|     }  | 
|   | 
|     return false;  | 
| }  | 
|   | 
| static inline void appendParenthesized(const QString &what, QString &s)  | 
| {  | 
|     s += QLatin1String(" (");  | 
|     s += what;  | 
|     s += QLatin1Char(')');  | 
| }  | 
|   | 
| QVariant ResourceModel::data(const QModelIndex &index, int role) const  | 
| {  | 
|     if (!index.isValid())  | 
|         return QVariant();  | 
|   | 
|     const void *internalPointer = index.internalPointer();  | 
|     const Node *node = reinterpret_cast<const Node *>(internalPointer);  | 
|     const Prefix *prefix = node->prefix();  | 
|     File *file = node->file();  | 
|     Q_ASSERT(prefix);  | 
|     const bool isFileNode = (prefix != node);  | 
|   | 
|     QVariant result;  | 
|   | 
|     switch (role) {  | 
|     case Qt::DisplayRole:  | 
|         {  | 
|             QString stringRes;  | 
|             if (!isFileNode) {  | 
|                 // Prefix node  | 
|                 stringRes = prefix->name;  | 
|                 const QString &lang = prefix->lang;  | 
|                 if (!lang.isEmpty())  | 
|                     appendParenthesized(lang, stringRes);  | 
|             } else  {  | 
|                 // File node  | 
|                 Q_ASSERT(file);  | 
|                 QString conv_file = m_resource_file.relativePath(file->name);  | 
|                 stringRes = conv_file.replace(QDir::separator(), QLatin1Char('/'));  | 
|                 const QString alias = file->alias;  | 
|                 if (!alias.isEmpty())  | 
|                     appendParenthesized(alias, stringRes);  | 
|             }  | 
|             result = stringRes;  | 
|         }  | 
|         break;  | 
|     case Qt::DecorationRole:  | 
|         if (isFileNode) {  | 
|             // File node  | 
|             Q_ASSERT(file);  | 
|             if (file->icon.isNull()) {  | 
|                 const QString path = m_resource_file.absolutePath(file->name);  | 
|                 if (iconFileExtension(path))  | 
|                     file->icon = QIcon(path);  | 
|             }  | 
|             if (!file->icon.isNull())  | 
|                 result = file->icon;  | 
|         }  | 
|         break;  | 
|     default:  | 
|         break;  | 
|     }  | 
|     return result;  | 
| }  | 
|   | 
| void ResourceModel::getItem(const QModelIndex &index, QString &prefix, QString &file) const  | 
| {  | 
|     prefix.clear();  | 
|     file.clear();  | 
|   | 
|     if (!index.isValid())  | 
|         return;  | 
|   | 
|     const void *internalPointer = index.internalPointer();  | 
|     const Node *node = reinterpret_cast<const Node *>(internalPointer);  | 
|     const Prefix *p = node->prefix();  | 
|     Q_ASSERT(p);  | 
|     const bool isFileNode = (p != node);  | 
|   | 
|     if (isFileNode) {  | 
|         const File *f = node->file();  | 
|         Q_ASSERT(f);  | 
|         if (!f->alias.isEmpty())  | 
|             file = f->alias;  | 
|         else  | 
|             file = f->name;  | 
|     } else {  | 
|         prefix = p->name;  | 
|     }  | 
| }  | 
|   | 
| QString ResourceModel::lang(const QModelIndex &index) const  | 
| {  | 
|     if (!index.isValid())  | 
|         return QString();  | 
|   | 
|     return m_resource_file.lang(index.row());  | 
| }  | 
|   | 
| QString ResourceModel::alias(const QModelIndex &index) const  | 
| {  | 
|     if (!index.isValid() || !index.parent().isValid())  | 
|         return QString();  | 
|     return m_resource_file.alias(index.parent().row(), index.row());  | 
| }  | 
|   | 
| QString ResourceModel::file(const QModelIndex &index) const  | 
| {  | 
|     if (!index.isValid() || !index.parent().isValid())  | 
|         return QString();  | 
|     return m_resource_file.file(index.parent().row(), index.row());  | 
| }  | 
|   | 
| QModelIndex ResourceModel::getIndex(const QString &prefixed_file)  | 
| {  | 
|     QString prefix, file;  | 
|     if (!m_resource_file.split(prefixed_file, &prefix, &file))  | 
|         return QModelIndex();  | 
|     return getIndex(prefix, file);  | 
| }  | 
|   | 
| QModelIndex ResourceModel::getIndex(const QString &prefix, const QString &file)  | 
| {  | 
|     if (prefix.isEmpty())  | 
|         return QModelIndex();  | 
|   | 
|     const int pref_idx = m_resource_file.indexOfPrefix(prefix);  | 
|     if (pref_idx == -1)  | 
|         return QModelIndex();  | 
|   | 
|     const QModelIndex pref_model_idx = index(pref_idx, 0, QModelIndex());  | 
|     if (file.isEmpty())  | 
|         return pref_model_idx;  | 
|   | 
|     const int file_idx = m_resource_file.indexOfFile(pref_idx, file);  | 
|     if (file_idx == -1)  | 
|         return QModelIndex();  | 
|   | 
|     return index(file_idx, 0, pref_model_idx);  | 
| }  | 
|   | 
| QModelIndex ResourceModel::prefixIndex(const QModelIndex &sel_idx) const  | 
| {  | 
|     if (!sel_idx.isValid())  | 
|         return QModelIndex();  | 
|     const QModelIndex parentIndex = parent(sel_idx);  | 
|     return parentIndex.isValid() ? parentIndex : sel_idx;  | 
| }  | 
|   | 
| QModelIndex ResourceModel::addNewPrefix()  | 
| {  | 
|     const QString format = QLatin1String("/new/prefix%1");  | 
|     int i = 1;  | 
|     QString prefix = format.arg(i);  | 
|     for ( ; m_resource_file.contains(prefix); i++)  | 
|         prefix = format.arg(i);  | 
|   | 
|     i = rowCount(QModelIndex());  | 
|     beginInsertRows(QModelIndex(), i, i);  | 
|     m_resource_file.addPrefix(prefix);  | 
|     endInsertRows();  | 
|   | 
|     setDirty(true);  | 
|   | 
|     return index(i, 0, QModelIndex());  | 
| }  | 
|   | 
| QModelIndex ResourceModel::addFiles(const QModelIndex &model_idx, const QStringList &file_list)  | 
| {  | 
|     const QModelIndex prefixModelIndex = prefixIndex(model_idx);  | 
|     const int prefixArrayIndex = prefixModelIndex.row();  | 
|     const int cursorFileArrayIndex = (prefixModelIndex == model_idx) ? 0 : model_idx.row();  | 
|     int dummy;  | 
|     int lastFileArrayIndex;  | 
|     addFiles(prefixArrayIndex, file_list, cursorFileArrayIndex, dummy, lastFileArrayIndex);  | 
|     return index(lastFileArrayIndex, 0, prefixModelIndex);  | 
| }  | 
|   | 
| void ResourceModel::addFiles(int prefixIndex, const QStringList &fileNames, int cursorFile,  | 
|         int &firstFile, int &lastFile)  | 
| {  | 
|     Q_UNUSED(cursorFile)  | 
|     const QModelIndex prefix_model_idx = index(prefixIndex, 0, QModelIndex());  | 
|     const QStringList &file_list = fileNames;  | 
|     firstFile = -1;  | 
|     lastFile = -1;  | 
|   | 
|     if (!prefix_model_idx.isValid()) {  | 
|         return;  | 
|     }  | 
|     const int prefix_idx = prefixIndex;  | 
|   | 
|     QStringList unique_list;  | 
|     foreach (const QString &file, file_list) {  | 
|         if (!m_resource_file.contains(prefix_idx, file) && !unique_list.contains(file))  | 
|             unique_list.append(file);  | 
|     }  | 
|   | 
|     if (unique_list.isEmpty()) {  | 
|         return;  | 
|     }  | 
|     const int cnt = m_resource_file.fileCount(prefix_idx);  | 
|     beginInsertRows(prefix_model_idx, cnt, cnt + unique_list.count() - 1); // ### FIXME  | 
|   | 
|     foreach (const QString &file, unique_list)  | 
|         m_resource_file.addFile(prefix_idx, file);  | 
|   | 
|     const QFileInfo fi(file_list.last());  | 
|     m_lastResourceDir = fi.absolutePath();  | 
|   | 
|     endInsertRows();  | 
|     setDirty(true);  | 
|   | 
|     firstFile = cnt;  | 
|     lastFile = cnt + unique_list.count() - 1;  | 
| }  | 
|   | 
|   | 
| void ResourceModel::insertPrefix(int prefixIndex, const QString &prefix,  | 
|         const QString &lang)  | 
| {  | 
|     beginInsertRows(QModelIndex(), prefixIndex, prefixIndex);  | 
|     m_resource_file.addPrefix(prefix, prefixIndex);  | 
|     m_resource_file.replaceLang(prefixIndex, lang);  | 
|     endInsertRows();  | 
|     setDirty(true);  | 
| }  | 
|   | 
| void ResourceModel::insertFile(int prefixIndex, int fileIndex,  | 
|         const QString &fileName, const QString &alias)  | 
| {  | 
|     const QModelIndex parent = index(prefixIndex, 0, QModelIndex());  | 
|     beginInsertRows(parent, fileIndex, fileIndex);  | 
|     m_resource_file.addFile(prefixIndex, fileName, fileIndex);  | 
|     m_resource_file.replaceAlias(prefixIndex, fileIndex, alias);  | 
|     endInsertRows();  | 
|     setDirty(true);  | 
| }  | 
|   | 
| void ResourceModel::changePrefix(const QModelIndex &model_idx, const QString &prefix)  | 
| {  | 
|     if (!model_idx.isValid())  | 
|         return;  | 
|   | 
|     const QModelIndex prefix_model_idx = prefixIndex(model_idx);  | 
|     const int prefix_idx = model_idx.row();  | 
|     if (m_resource_file.prefix(prefix_idx) == ResourceFile::fixPrefix(prefix))  | 
|         return;  | 
|   | 
|     if (m_resource_file.contains(prefix))  | 
|         return;  | 
|   | 
|     m_resource_file.replacePrefix(prefix_idx, prefix);  | 
|     emit dataChanged(prefix_model_idx, prefix_model_idx);  | 
|     setDirty(true);  | 
| }  | 
|   | 
| void ResourceModel::changeLang(const QModelIndex &model_idx, const QString &lang)  | 
| {  | 
|     if (!model_idx.isValid())  | 
|         return;  | 
|   | 
|     const QModelIndex prefix_model_idx = prefixIndex(model_idx);  | 
|     const int prefix_idx = model_idx.row();  | 
|     if (m_resource_file.lang(prefix_idx) == lang)  | 
|         return;  | 
|   | 
|     m_resource_file.replaceLang(prefix_idx, lang);  | 
|     emit dataChanged(prefix_model_idx, prefix_model_idx);  | 
|     setDirty(true);  | 
| }  | 
|   | 
| void ResourceModel::changeAlias(const QModelIndex &index, const QString &alias)  | 
| {  | 
|     if (!index.parent().isValid())  | 
|         return;  | 
|   | 
|     if (m_resource_file.alias(index.parent().row(), index.row()) == alias)  | 
|         return;  | 
|     m_resource_file.replaceAlias(index.parent().row(), index.row(), alias);  | 
|     emit dataChanged(index, index);  | 
|     setDirty(true);  | 
| }  | 
|   | 
| QModelIndex ResourceModel::deleteItem(const QModelIndex &idx)  | 
| {  | 
|     if (!idx.isValid())  | 
|         return QModelIndex();  | 
|   | 
|     QString dummy, file;  | 
|     getItem(idx, dummy, file);  | 
|     int prefix_idx = -1;  | 
|     int file_idx = -1;  | 
|   | 
|     beginRemoveRows(parent(idx), idx.row(), idx.row());  | 
|     if (file.isEmpty()) {  | 
|         // Remove prefix  | 
|         prefix_idx = idx.row();  | 
|         m_resource_file.removePrefix(prefix_idx);  | 
|         if (prefix_idx == m_resource_file.prefixCount())  | 
|             --prefix_idx;  | 
|     } else {  | 
|         // Remove file  | 
|         prefix_idx = prefixIndex(idx).row();  | 
|         file_idx = idx.row();  | 
|         m_resource_file.removeFile(prefix_idx, file_idx);  | 
|         if (file_idx == m_resource_file.fileCount(prefix_idx))  | 
|             --file_idx;  | 
|     }  | 
|     endRemoveRows();  | 
|   | 
|     setDirty(true);  | 
|   | 
|     if (prefix_idx == -1)  | 
|         return QModelIndex();  | 
|     const QModelIndex prefix_model_idx = index(prefix_idx, 0, QModelIndex());  | 
|     if (file_idx == -1)  | 
|         return prefix_model_idx;  | 
|     return index(file_idx, 0, prefix_model_idx);  | 
| }  | 
|   | 
| bool ResourceModel::reload()  | 
| {  | 
|     beginResetModel();  | 
|     const bool result = m_resource_file.load();  | 
|     if (result)  | 
|         setDirty(false);  | 
|     endResetModel();  | 
|     return result;  | 
| }  | 
|   | 
| bool ResourceModel::save()  | 
| {  | 
|     const bool result = m_resource_file.save();  | 
|     if (result)  | 
|         setDirty(false);  | 
|     return result;  | 
| }  | 
|   | 
| QString ResourceModel::lastResourceOpenDirectory() const  | 
| {  | 
|     if (m_lastResourceDir.isEmpty())  | 
|         return absolutePath(QString());  | 
|     return m_lastResourceDir;  | 
| }  | 
|   | 
| // Create a resource path 'prefix:/file'  | 
| QString ResourceModel::resourcePath(const QString &prefix, const QString &file)  | 
| {  | 
|     QString rc = QString(QLatin1Char(':'));  | 
|     rc += prefix;  | 
|     rc += QLatin1Char('/');  | 
|     rc += file;  | 
|     return QDir::cleanPath(rc);  | 
| }  | 
|   | 
| QMimeData *ResourceModel::mimeData(const QModelIndexList &indexes) const  | 
| {  | 
|     if (indexes.size() != 1)  | 
|         return 0;  | 
|   | 
|     QString prefix, file;  | 
|     getItem(indexes.front(), prefix, file);  | 
|     if (prefix.isEmpty() || file.isEmpty())  | 
|         return 0;  | 
|   | 
|     // DnD format of Designer 4.4  | 
|     QDomDocument doc;  | 
|     QDomElement elem = doc.createElement(QLatin1String("resource"));  | 
|     elem.setAttribute(QLatin1String("type"), QLatin1String("image"));  | 
|     elem.setAttribute(QLatin1String("file"), resourcePath(prefix, file));  | 
|     doc.appendChild(elem);  | 
|   | 
|     QMimeData *rc = new QMimeData;  | 
|     rc->setText(doc.toString());  | 
|     return rc;  | 
| }  | 
|   | 
| } // namespace qdesigner_internal  | 
|   | 
| QT_END_NAMESPACE  |