File indexing completed on 2024-04-28 04:37:04
0001 /* 0002 SPDX-FileCopyrightText: 2007 Andreas Pakulat <apaku@gmx.de> 0003 SPDX-FileCopyrightText: 2009 Aleix Pol <aleixpol@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "projectbuildsetmodel.h" 0009 0010 #include <QVariant> 0011 0012 #include <KLocalizedString> 0013 #include <KConfigGroup> 0014 0015 #include <interfaces/icore.h> 0016 #include <interfaces/iproject.h> 0017 #include <interfaces/iprojectcontroller.h> 0018 #include <interfaces/isession.h> 0019 0020 #include "projectmodel.h" 0021 #include <util/kdevstringhandler.h> 0022 #include <QIcon> 0023 0024 namespace KDevelop 0025 { 0026 0027 BuildItem::BuildItem() 0028 { 0029 } 0030 0031 BuildItem::BuildItem( const QStringList & itemPath ) 0032 : m_itemPath( itemPath ) 0033 { 0034 } 0035 0036 BuildItem::BuildItem( KDevelop::ProjectBaseItem* item ) 0037 { 0038 initializeFromItem( item ); 0039 } 0040 0041 BuildItem::BuildItem( const BuildItem& rhs ) 0042 : m_itemPath(rhs.itemPath()) 0043 { 0044 } 0045 0046 void BuildItem::initializeFromItem( KDevelop::ProjectBaseItem* item ) 0047 { 0048 Q_ASSERT(item); 0049 KDevelop::ProjectModel* model=KDevelop::ICore::self()->projectController()->projectModel(); 0050 0051 m_itemPath = model->pathFromIndex(item->index()); 0052 } 0053 0054 QString BuildItem::itemName() const 0055 { 0056 return m_itemPath.last(); 0057 } 0058 0059 QString BuildItem::itemProject() const 0060 { 0061 return m_itemPath.first(); 0062 } 0063 0064 KDevelop::ProjectBaseItem* BuildItem::findItem() const 0065 { 0066 KDevelop::ProjectModel* model=KDevelop::ICore::self()->projectController()->projectModel(); 0067 QModelIndex idx = model->pathToIndex(m_itemPath); 0068 return model->itemFromIndex(idx); 0069 } 0070 0071 bool operator==( const BuildItem& rhs, const BuildItem& lhs ) 0072 { 0073 return( rhs.itemPath() == lhs.itemPath() ); 0074 } 0075 0076 BuildItem& BuildItem::operator=( const BuildItem& rhs ) 0077 { 0078 if( this == &rhs ) 0079 return *this; 0080 m_itemPath = rhs.itemPath(); 0081 return *this; 0082 } 0083 0084 0085 class ProjectBuildSetModelPrivate 0086 { 0087 public: 0088 QList<BuildItem> items; 0089 QList<QStringList> orderingCache; 0090 }; 0091 0092 0093 ProjectBuildSetModel::ProjectBuildSetModel( QObject* parent ) 0094 : QAbstractTableModel( parent ) 0095 , d_ptr(new ProjectBuildSetModelPrivate) 0096 { 0097 } 0098 0099 ProjectBuildSetModel::~ProjectBuildSetModel() = default; 0100 0101 void ProjectBuildSetModel::loadFromSession( ISession* session ) 0102 { 0103 Q_D(ProjectBuildSetModel); 0104 0105 if (!session) { 0106 return; 0107 } 0108 0109 // Load the item ordering cache 0110 KConfigGroup sessionBuildSetConfig = session->config()->group( "Buildset" ); 0111 const QVariantList sessionBuildItems = KDevelop::stringToQVariant(sessionBuildSetConfig.readEntry("BuildItems", QString())).toList(); 0112 d->orderingCache.reserve(d->orderingCache.size() + sessionBuildItems.size()); 0113 for (const QVariant& item : sessionBuildItems) { 0114 d->orderingCache.append(item.toStringList()); 0115 } 0116 } 0117 0118 void ProjectBuildSetModel::storeToSession( ISession* session ) 0119 { 0120 Q_D(ProjectBuildSetModel); 0121 0122 if (!session) { 0123 return; 0124 } 0125 0126 // Store the item ordering cache 0127 QVariantList sessionBuildItems; 0128 sessionBuildItems.reserve(d->orderingCache.size()); 0129 for (const QStringList& item : qAsConst(d->orderingCache)) { 0130 sessionBuildItems.append( item ); 0131 } 0132 KConfigGroup sessionBuildSetConfig = session->config()->group( "Buildset" ); 0133 sessionBuildSetConfig.writeEntry("BuildItems", KDevelop::qvariantToString( QVariant( sessionBuildItems ) )); 0134 sessionBuildSetConfig.sync(); 0135 } 0136 0137 0138 int ProjectBuildSetModel::findInsertionPlace( const QStringList& itemPath ) 0139 { 0140 /* 0141 * The ordering cache list is a superset of the build set, and must be ordered in the same way. 0142 * Example: 0143 * (items) A - B ----- D --------- G 0144 * (orderingCache) A - B - C - D - E - F - G 0145 * 0146 * We scan orderingCache until we find the required item (absent in items: say, F). 0147 * In process of scanning we synchronize position in orderingCache with position in items; 0148 * so, when we reach F, we have D as last synchronization point and hence return it 0149 * as the insertion place (actually, we return the next item's index - here, index of G). 0150 * 0151 * If an item cannot be found in the ordering list, we append it to the list. 0152 */ 0153 0154 Q_D(ProjectBuildSetModel); 0155 0156 int insertionIndex = 0; 0157 bool found = false; 0158 // Points to the item which is next to last synchronization point. 0159 QList<BuildItem>::iterator nextItemIterator = d->items.begin(); 0160 0161 for (auto& orderedItemPath : qAsConst(d->orderingCache)) { 0162 if (itemPath == orderedItemPath) { 0163 found = true; 0164 break; 0165 } 0166 if (nextItemIterator != d->items.end() && 0167 nextItemIterator->itemPath() == orderedItemPath) { 0168 ++insertionIndex; 0169 ++nextItemIterator; 0170 } 0171 } 0172 0173 if( !found ) { 0174 d->orderingCache.append(itemPath); 0175 } 0176 Q_ASSERT( insertionIndex >= 0 && insertionIndex <= d->items.size() ); 0177 return insertionIndex; 0178 } 0179 0180 void ProjectBuildSetModel::removeItemsWithCache( const QList<int>& itemIndices ) 0181 { 0182 /* 0183 * Removes the items with given indices from both the build set and the ordering cache. 0184 * List is given since removing many items together is more efficient than by one. 0185 * 0186 * Indices in the list shall be sorted. 0187 */ 0188 0189 Q_D(ProjectBuildSetModel); 0190 0191 QList<int> itemIndicesCopy = itemIndices; 0192 0193 beginRemoveRows( QModelIndex(), itemIndices.first(), itemIndices.last() ); 0194 for (QList<QStringList>::iterator cacheIterator = d->orderingCache.end() - 1; 0195 cacheIterator >= d->orderingCache.begin() && !itemIndicesCopy.isEmpty();) { 0196 0197 int index = itemIndicesCopy.back(); 0198 Q_ASSERT( index >= 0 && index < d->items.size() ); 0199 if (*cacheIterator == d->items.at(index).itemPath()) { 0200 cacheIterator = d->orderingCache.erase(cacheIterator); 0201 d->items.removeAt(index); 0202 itemIndicesCopy.removeLast(); 0203 } 0204 --cacheIterator; 0205 0206 } // for 0207 endRemoveRows(); 0208 0209 Q_ASSERT( itemIndicesCopy.isEmpty() ); 0210 } 0211 0212 void ProjectBuildSetModel::insertItemWithCache( const BuildItem& item ) 0213 { 0214 Q_D(ProjectBuildSetModel); 0215 0216 int insertionPlace = findInsertionPlace( item.itemPath() ); 0217 beginInsertRows( QModelIndex(), insertionPlace, insertionPlace ); 0218 d->items.insert(insertionPlace, item); 0219 endInsertRows(); 0220 } 0221 0222 void ProjectBuildSetModel::insertItemsOverrideCache( int index, const QList< BuildItem >& items ) 0223 { 0224 Q_D(ProjectBuildSetModel); 0225 0226 Q_ASSERT( index >= 0 && index <= d->items.size() ); 0227 0228 if (index == d->items.size()) { 0229 beginInsertRows( QModelIndex(), index, index + items.size() - 1 ); 0230 d->items.append(items); 0231 d->orderingCache.reserve(d->orderingCache.size() + items.size()); 0232 for (const BuildItem& item : items) { 0233 d->orderingCache.append(item.itemPath()); 0234 } 0235 endInsertRows(); 0236 } else { 0237 int indexInCache = d->orderingCache.indexOf(d->items.at(index).itemPath()); 0238 Q_ASSERT( indexInCache >= 0 ); 0239 0240 beginInsertRows( QModelIndex(), index, index + items.size() - 1 ); 0241 for( int i = 0; i < items.size(); ++i ) { 0242 const BuildItem& item = items.at( i ); 0243 d->items.insert(index + i, item); 0244 d->orderingCache.insert(indexInCache + i, item.itemPath()); 0245 } 0246 endInsertRows(); 0247 } 0248 } 0249 0250 QVariant ProjectBuildSetModel::data( const QModelIndex& idx, int role ) const 0251 { 0252 Q_D(const ProjectBuildSetModel); 0253 0254 if( !idx.isValid() || idx.row() < 0 || idx.column() < 0 0255 || idx.row() >= rowCount() || idx.column() >= columnCount()) 0256 { 0257 return QVariant(); 0258 } 0259 0260 if(role == Qt::DisplayRole) { 0261 switch( idx.column() ) 0262 { 0263 case 0: 0264 return d->items.at(idx.row()).itemName(); 0265 case 1: 0266 return KDevelop::joinWithEscaping(d->items.at(idx.row()).itemPath(), QLatin1Char('/'), QLatin1Char('\\')); 0267 } 0268 } else if(role == Qt::DecorationRole && idx.column()==0) { 0269 KDevelop::ProjectBaseItem* item = d->items.at(idx.row()).findItem(); 0270 if( item ) { 0271 return QIcon::fromTheme( item->iconName() ); 0272 } 0273 } 0274 return QVariant(); 0275 } 0276 0277 QVariant ProjectBuildSetModel::headerData( int section, Qt::Orientation orientation, int role ) const 0278 { 0279 if( section < 0 || section >= columnCount() 0280 || orientation != Qt::Horizontal || role != Qt::DisplayRole ) 0281 return QVariant(); 0282 0283 switch( section ) 0284 { 0285 case 0: 0286 return i18nc("@title:column buildset item name", "Name"); 0287 case 1: 0288 return i18nc("@title:column buildset item path", "Path"); 0289 } 0290 return QVariant(); 0291 } 0292 0293 int ProjectBuildSetModel::rowCount( const QModelIndex& parent ) const 0294 { 0295 Q_D(const ProjectBuildSetModel); 0296 0297 if( parent.isValid() ) 0298 return 0; 0299 return d->items.count(); 0300 } 0301 0302 int ProjectBuildSetModel::columnCount( const QModelIndex& parent ) const 0303 { 0304 if( parent.isValid() ) 0305 return 0; 0306 return 2; 0307 } 0308 0309 void ProjectBuildSetModel::addProjectItem( KDevelop::ProjectBaseItem* item ) 0310 { 0311 Q_D(ProjectBuildSetModel); 0312 0313 BuildItem buildItem( item ); 0314 if (d->items.contains(buildItem)) 0315 return; 0316 0317 insertItemWithCache( buildItem ); 0318 } 0319 0320 bool ProjectBuildSetModel::removeRows( int row, int count, const QModelIndex& parent ) 0321 { 0322 if( parent.isValid() || row > rowCount() || row < 0 || (row+count) > rowCount() || count <= 0 ) 0323 return false; 0324 0325 QList<int> itemsToRemove; 0326 itemsToRemove.reserve(count); 0327 for( int i = row; i < row+count; i++ ) 0328 { 0329 itemsToRemove.append( i ); 0330 } 0331 removeItemsWithCache( itemsToRemove ); 0332 return true; 0333 } 0334 0335 QList<BuildItem> ProjectBuildSetModel::items() const 0336 { 0337 Q_D(const ProjectBuildSetModel); 0338 0339 return d->items; 0340 } 0341 0342 void ProjectBuildSetModel::projectClosed( KDevelop::IProject* project ) 0343 { 0344 Q_D(ProjectBuildSetModel); 0345 0346 for (int i = d->items.count() - 1; i >= 0; --i) { 0347 if (d->items.at(i).itemProject() == project->name()) { 0348 beginRemoveRows( QModelIndex(), i, i ); 0349 d->items.removeAt(i); 0350 endRemoveRows(); 0351 } 0352 } 0353 } 0354 0355 void ProjectBuildSetModel::saveToProject( KDevelop::IProject* project ) const 0356 { 0357 Q_D(const ProjectBuildSetModel); 0358 0359 QVariantList paths; 0360 for (const BuildItem& item : qAsConst(d->items)) { 0361 if( item.itemProject() == project->name() ) 0362 paths.append(item.itemPath()); 0363 } 0364 KConfigGroup base = project->projectConfiguration()->group("Buildset"); 0365 base.writeEntry("BuildItems", KDevelop::qvariantToString( QVariant( paths ) )); 0366 base.sync(); 0367 } 0368 0369 void ProjectBuildSetModel::loadFromProject( KDevelop::IProject* project ) 0370 { 0371 KConfigGroup base = project->projectConfiguration()->group("Buildset"); 0372 if (base.hasKey("BuildItems")) { 0373 const QVariantList items = KDevelop::stringToQVariant(base.readEntry("BuildItems", QString())).toList(); 0374 0375 for (const QVariant& path : items) { 0376 insertItemWithCache( BuildItem( path.toStringList() ) ); 0377 } 0378 } else { 0379 // Add project to buildset, but only if there is no configuration for this project yet. 0380 addProjectItem( project->projectItem() ); 0381 } 0382 } 0383 0384 void ProjectBuildSetModel::moveRowsDown(int row, int count) 0385 { 0386 Q_D(ProjectBuildSetModel); 0387 0388 QList<BuildItem> items = d->items.mid(row, count); 0389 removeRows( row, count ); 0390 insertItemsOverrideCache( row + 1, items ); 0391 } 0392 0393 void ProjectBuildSetModel::moveRowsToBottom(int row, int count) 0394 { 0395 Q_D(ProjectBuildSetModel); 0396 0397 QList<BuildItem> items = d->items.mid(row, count); 0398 removeRows( row, count ); 0399 insertItemsOverrideCache( rowCount(), items ); 0400 } 0401 0402 void ProjectBuildSetModel::moveRowsUp(int row, int count) 0403 { 0404 Q_D(ProjectBuildSetModel); 0405 0406 QList<BuildItem> items = d->items.mid(row, count); 0407 removeRows( row, count ); 0408 insertItemsOverrideCache( row - 1, items ); 0409 } 0410 0411 void ProjectBuildSetModel::moveRowsToTop(int row, int count) 0412 { 0413 Q_D(ProjectBuildSetModel); 0414 0415 QList<BuildItem> items = d->items.mid(row, count); 0416 removeRows( row, count ); 0417 insertItemsOverrideCache( 0, items ); 0418 } 0419 0420 } 0421 0422 #include "moc_projectbuildsetmodel.cpp"