File indexing completed on 2024-05-19 15:45:25
0001 /* 0002 SPDX-FileCopyrightText: 2010 Andreas Pakulat <apaku@gmx.de> 0003 0004 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-or-later 0005 */ 0006 0007 #include "projectpathsmodel.h" 0008 0009 #include <KLocalizedString> 0010 #include <interfaces/iproject.h> 0011 #include <serialization/indexedstring.h> 0012 #include <util/path.h> 0013 0014 using namespace KDevelop; 0015 0016 ProjectPathsModel::ProjectPathsModel( QObject* parent ) 0017 : QAbstractListModel( parent ) 0018 { 0019 } 0020 0021 void ProjectPathsModel::setProject(IProject* w_project) 0022 { 0023 project = w_project; 0024 } 0025 0026 QVariant ProjectPathsModel::data( const QModelIndex& index, int role ) const 0027 { 0028 if( !index.isValid() || index.row() < 0 || index.row() >= rowCount() || index.column() != 0 ) { 0029 return QVariant(); 0030 } 0031 0032 const ConfigEntry& pathConfig = projectPaths.at( index.row() ); 0033 switch( role ) { 0034 case IncludesDataRole: 0035 return pathConfig.includes; 0036 case DefinesDataRole: 0037 return QVariant::fromValue(pathConfig.defines); 0038 case Qt::EditRole: 0039 return sanitizePath( pathConfig.path, true, false ); 0040 case Qt::DisplayRole: { 0041 const QString& path = pathConfig.path; 0042 return (path == QLatin1String(".")) ? QStringLiteral("(project root)") : path; 0043 } 0044 case FullUrlDataRole: 0045 return QVariant::fromValue(QUrl::fromUserInput( sanitizePath( pathConfig.path, true, false ) )); 0046 case CompilerDataRole: 0047 return QVariant::fromValue(pathConfig.compiler); 0048 case ParserArgumentsRole: 0049 return QVariant::fromValue(pathConfig.parserArguments); 0050 default: 0051 break; 0052 } 0053 return QVariant(); 0054 } 0055 0056 int ProjectPathsModel::rowCount( const QModelIndex& parent ) const 0057 { 0058 if( parent.isValid() ) { 0059 return 0; 0060 } 0061 return projectPaths.count(); 0062 } 0063 0064 bool ProjectPathsModel::setData( const QModelIndex& index, const QVariant& value, int role ) 0065 { 0066 if( !index.isValid() || index.row() < 0 || index.row() >= rowCount() || index.column() != 0 ) { 0067 return false; 0068 } 0069 0070 // Do not allow to change path of the first entry; instead, add a new one in that case 0071 if( index.row() == 0 && ( role == Qt::EditRole || role == Qt::DisplayRole || role == FullUrlDataRole ) ) { 0072 QString addedPath = sanitizePath( value.toString(), false ); 0073 0074 // Do not allow duplicates 0075 for (const ConfigEntry& existingConfig : qAsConst(projectPaths)) { 0076 if( addedPath == existingConfig.path ) { 0077 return false; 0078 } 0079 } 0080 projectPaths.insert( 1, ConfigEntry(sanitizePath( value.toString(), false ) )); 0081 emit dataChanged( this->index( 1, 0 ), this->index( projectPaths.count() - 1, 0 ) ); 0082 return true; 0083 } 0084 0085 ConfigEntry& pathConfig = projectPaths[ index.row() ]; 0086 switch( role ) { 0087 case IncludesDataRole: 0088 pathConfig.includes = value.toStringList(); 0089 break; 0090 case DefinesDataRole: 0091 pathConfig.defines = value.value<Defines>(); 0092 break; 0093 case Qt::EditRole: 0094 pathConfig.path = sanitizePath( value.toString(), false ); 0095 break; 0096 case Qt::DisplayRole: 0097 pathConfig.path = sanitizePath( value.toString(), true ); 0098 break; 0099 case FullUrlDataRole: 0100 pathConfig.path = sanitizeUrl(value.toUrl()); 0101 break; 0102 case CompilerDataRole: 0103 pathConfig.compiler = value.value<CompilerPointer>(); 0104 break; 0105 case ParserArgumentsRole: 0106 pathConfig.parserArguments = value.value<ParserArguments>(); 0107 break; 0108 default: 0109 return false; 0110 } 0111 emit dataChanged( index, index ); 0112 return true; 0113 } 0114 0115 Qt::ItemFlags ProjectPathsModel::flags( const QModelIndex& index ) const 0116 { 0117 if( !index.isValid() ) { 0118 return Qt::NoItemFlags; 0119 } 0120 0121 if( index.row() == 0 ) { 0122 return Qt::ItemFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled ); 0123 } 0124 0125 return Qt::ItemFlags( Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled ); 0126 } 0127 0128 QVector< ConfigEntry > ProjectPathsModel::paths() const 0129 { 0130 return projectPaths; 0131 } 0132 0133 void ProjectPathsModel::setPaths(const QVector< ConfigEntry >& paths ) 0134 { 0135 beginResetModel(); 0136 projectPaths.clear(); 0137 for (const ConfigEntry& existingPathConfig : paths) { 0138 // Sanitize the path of loaded config 0139 ConfigEntry config = existingPathConfig; 0140 bool rootPath = config.path == QLatin1String(".") ? true : false; 0141 config.path = sanitizePath(rootPath ? QString() : config.path ); 0142 addPathInternal(config, rootPath); 0143 } 0144 addPathInternal( ConfigEntry(sanitizePath( QString() )), true ); // add an empty "root" config entry if one does not exist 0145 endResetModel(); 0146 } 0147 0148 bool ProjectPathsModel::removeRows( int row, int count, const QModelIndex& parent ) 0149 { 0150 if( row >= 0 && count > 0 && row < rowCount() ) { 0151 beginRemoveRows( parent, row, row + count - 1 ); 0152 0153 for( int i = 0; i < count; ++i ) { 0154 if( projectPaths.at(row).path == QLatin1String(".") ) { 0155 continue; // we won't remove the root item 0156 } 0157 projectPaths.removeAt(row); 0158 } 0159 0160 endRemoveRows(); 0161 return true; 0162 } 0163 return false; 0164 } 0165 0166 void ProjectPathsModel::addPath( const QUrl &url ) 0167 { 0168 if( !project->path().isParentOf(KDevelop::Path(url)) ) { 0169 return; 0170 } 0171 0172 beginInsertRows( QModelIndex(), rowCount(), rowCount() ); 0173 addPathInternal( ConfigEntry(sanitizeUrl(url)), false ); 0174 endInsertRows(); 0175 } 0176 0177 void ProjectPathsModel::addPathInternal( const ConfigEntry& config, bool prepend ) 0178 { 0179 Q_ASSERT(!config.parserArguments.isAnyEmpty()); 0180 0181 // Do not allow duplicates 0182 for (const ConfigEntry& existingConfig : qAsConst(projectPaths)) { 0183 if( config.path == existingConfig.path ) { 0184 return; 0185 } 0186 } 0187 if( prepend ) { 0188 projectPaths.prepend( config ); 0189 } else { 0190 projectPaths.append( config ); 0191 } 0192 } 0193 0194 QString ProjectPathsModel::sanitizeUrl( const QUrl& url, bool needRelative ) const 0195 { 0196 Q_ASSERT( project ); 0197 0198 if (needRelative) { 0199 const auto relativePath = project->path().relativePath(KDevelop::Path(url)); 0200 return relativePath.isEmpty() ? QStringLiteral(".") : relativePath; 0201 } 0202 return url.adjusted(QUrl::StripTrailingSlash | QUrl::NormalizePathSegments).toString(QUrl::PreferLocalFile); 0203 } 0204 0205 QString ProjectPathsModel::sanitizePath( const QString& path, bool expectRelative, bool needRelative ) const 0206 { 0207 Q_ASSERT( project ); 0208 Q_ASSERT( expectRelative || project->inProject(IndexedString(path)) ); 0209 0210 QUrl url; 0211 if( expectRelative ) { 0212 url = KDevelop::Path(project->path(), path).toUrl(); 0213 } else { 0214 url = QUrl::fromUserInput(path); 0215 } 0216 return sanitizeUrl( url, needRelative ); 0217 } 0218 0219 #include "moc_projectpathsmodel.cpp"