kde-extraapps/kdevplatform/project/projectbuildsetmodel.cpp
2015-07-26 14:23:17 +03:00

394 lines
12 KiB
C++

/***************************************************************************
* This file is part of KDevelop *
* Copyright 2007 Andreas Pakulat <apaku@gmx.de> *
* Copyright 2009 Aleix Pol <aleixpol@kde.org> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Library General Public License as *
* published by the Free Software Foundation; either version 2 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#include "projectbuildsetmodel.h"
#include <QVariant>
#include <kurl.h>
#include <klocale.h>
#include <kdebug.h>
#include <kconfiggroup.h>
#include <interfaces/icore.h>
#include <interfaces/iproject.h>
#include <interfaces/iprojectcontroller.h>
#include <interfaces/isession.h>
#include "projectmodel.h"
#include <util/kdevstringhandler.h>
#include <kicon.h>
#include <kmimetype.h>
namespace KDevelop
{
BuildItem::BuildItem()
{
}
BuildItem::BuildItem( const QStringList & itemPath )
: m_itemPath( itemPath )
{
}
BuildItem::BuildItem( KDevelop::ProjectBaseItem* item )
{
initializeFromItem( item );
}
BuildItem::BuildItem( const BuildItem& rhs )
{
m_itemPath = rhs.itemPath();
}
void BuildItem::initializeFromItem( KDevelop::ProjectBaseItem* item )
{
Q_ASSERT(item);
KDevelop::ProjectModel* model=KDevelop::ICore::self()->projectController()->projectModel();
m_itemPath = model->pathFromIndex(item->index());
}
QString BuildItem::itemName() const
{
return m_itemPath.last();
}
QString BuildItem::itemProject() const
{
return m_itemPath.first();
}
KDevelop::ProjectBaseItem* BuildItem::findItem() const
{
KDevelop::ProjectModel* model=KDevelop::ICore::self()->projectController()->projectModel();
QModelIndex idx = model->pathToIndex(m_itemPath);
return model->itemFromIndex(idx);
}
bool operator==( const BuildItem& rhs, const BuildItem& lhs )
{
return( rhs.itemPath() == lhs.itemPath() );
}
BuildItem& BuildItem::operator=( const BuildItem& rhs )
{
if( this == &rhs )
return *this;
m_itemPath = rhs.itemPath();
return *this;
}
ProjectBuildSetModel::ProjectBuildSetModel( QObject* parent )
: QAbstractTableModel( parent )
{
}
void ProjectBuildSetModel::loadFromSession( ISession* session )
{
// Load the item ordering cache
KConfigGroup sessionBuildSetConfig = session->config()->group( "Buildset" );
QVariantList sessionBuildItems = KDevelop::stringToQVariant( sessionBuildSetConfig.readEntry( "BuildItems", QString() ) ).toList();
foreach( const QVariant& item, sessionBuildItems ) {
m_orderingCache.append( item.toStringList() );
}
}
void ProjectBuildSetModel::storeToSession( ISession* session )
{
// Store the item ordering cache
QVariantList sessionBuildItems;
foreach( const QStringList& item, m_orderingCache) {
sessionBuildItems.append( item );
}
KConfigGroup sessionBuildSetConfig = session->config()->group( "Buildset" );
sessionBuildSetConfig.writeEntry("BuildItems", KDevelop::qvariantToString( QVariant( sessionBuildItems ) ));
sessionBuildSetConfig.sync();
}
int ProjectBuildSetModel::findInsertionPlace( const QStringList& itemPath )
{
/*
* The ordering cache list is a superset of the build set, and must be ordered in the same way.
* Example:
* (m_items) A - B ----- D --------- G
* (m_orderingCache) A - B - C - D - E - F - G
*
* We scan m_orderingCache until we find the required item (absent in m_items: say, F).
* In process of scanning we synchronize position in m_orderingCache with position in m_items;
* so, when we reach F, we have D as last synchronization point and hence return it
* as the insertion place (actually, we return the next item's index - here, index of G).
*
* If an item cannot be found in the ordering list, we append it to the list.
*/
int insertionIndex = 0;
bool found = false;
QList<QStringList>::iterator orderingCacheIterator = m_orderingCache.begin();
// Points to the item which is next to last synchronization point.
QList<BuildItem>::iterator nextItemIterator = m_items.begin();
while( orderingCacheIterator != m_orderingCache.end() ) {
if( itemPath == *orderingCacheIterator ) {
found = true;
break;
}
if( nextItemIterator != m_items.end() &&
nextItemIterator->itemPath() == *orderingCacheIterator ) {
++insertionIndex;
++nextItemIterator;
}
++orderingCacheIterator;
} // while
if( !found ) {
m_orderingCache.append( itemPath );
}
Q_ASSERT( insertionIndex >= 0 && insertionIndex <= m_items.size() );
return insertionIndex;
}
void ProjectBuildSetModel::removeItemsWithCache( const QList<int>& itemIndices )
{
/*
* Removes the items with given indices from both the build set and the ordering cache.
* List is given since removing many items together is more efficient than by one.
*
* Indices in the list shall be sorted.
*/
QList<int> itemIndicesCopy = itemIndices;
beginRemoveRows( QModelIndex(), itemIndices.first(), itemIndices.last() );
for( QList<QStringList>::iterator cacheIterator = m_orderingCache.end() - 1;
cacheIterator >= m_orderingCache.begin() && !itemIndicesCopy.isEmpty(); ) {
int index = itemIndicesCopy.back();
Q_ASSERT( index >= 0 && index < m_items.size() );
if( *cacheIterator == m_items.at( index ).itemPath() ) {
m_orderingCache.erase( cacheIterator-- );
m_items.removeAt( index );
itemIndicesCopy.removeLast();
} else {
--cacheIterator;
}
} // for
endRemoveRows();
Q_ASSERT( itemIndicesCopy.isEmpty() );
}
void ProjectBuildSetModel::insertItemWithCache( const BuildItem& item )
{
int insertionPlace = findInsertionPlace( item.itemPath() );
beginInsertRows( QModelIndex(), insertionPlace, insertionPlace );
m_items.insert( insertionPlace, item );
endInsertRows();
}
void ProjectBuildSetModel::insertItemsOverrideCache( int index, const QList< BuildItem >& items )
{
Q_ASSERT( index >= 0 && index <= m_items.size() );
if( index == m_items.size() ) {
beginInsertRows( QModelIndex(), index, index + items.size() - 1 );
m_items.append( items );
foreach( const BuildItem& item, items ) {
m_orderingCache.append( item.itemPath() );
}
endInsertRows();
} else {
int indexInCache = m_orderingCache.indexOf( m_items.at( index ).itemPath() );
Q_ASSERT( indexInCache >= 0 );
beginInsertRows( QModelIndex(), index, index + items.size() - 1 );
for( int i = 0; i < items.size(); ++i ) {
const BuildItem& item = items.at( i );
m_items.insert( index + i, item );
m_orderingCache.insert( indexInCache + i, item.itemPath() );
}
endInsertRows();
}
}
QVariant ProjectBuildSetModel::data( const QModelIndex& idx, int role ) const
{
if( !idx.isValid() || idx.row() < 0 || idx.column() < 0
|| idx.row() >= rowCount() || idx.column() >= columnCount())
{
return QVariant();
}
if(role == Qt::DisplayRole) {
switch( idx.column() )
{
case 0:
return m_items.at( idx.row() ).itemName();
break;
case 1:
return KDevelop::joinWithEscaping( m_items.at( idx.row() ).itemPath(), '/', '\\');
break;
}
} else if(role == Qt::DecorationRole && idx.column()==0) {
KDevelop::ProjectBaseItem* item = m_items.at( idx.row() ).findItem();
if( item ) {
return KIcon( item->iconName() );
}
}
return QVariant();
}
QVariant ProjectBuildSetModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
if( section < 0 || section >= columnCount()
|| orientation != Qt::Horizontal || role != Qt::DisplayRole )
return QVariant();
switch( section )
{
case 0:
return i18nc("@title:column buildset item name", "Name");
break;
case 1:
return i18nc("@title:column buildset item path", "Path");
break;
}
return QVariant();
}
int ProjectBuildSetModel::rowCount( const QModelIndex& parent ) const
{
if( parent.isValid() )
return 0;
return m_items.count();
}
int ProjectBuildSetModel::columnCount( const QModelIndex& parent ) const
{
if( parent.isValid() )
return 0;
return 2;
}
void ProjectBuildSetModel::addProjectItem( KDevelop::ProjectBaseItem* item )
{
BuildItem buildItem( item );
if( m_items.contains( buildItem ) )
return;
insertItemWithCache( buildItem );
}
bool ProjectBuildSetModel::removeRows( int row, int count, const QModelIndex& parent )
{
if( parent.isValid() || row > rowCount() || row < 0 || (row+count) > rowCount() || count <= 0 )
return false;
QList<int> itemsToRemove;
for( int i = row; i < row+count; i++ )
{
itemsToRemove.append( i );
}
removeItemsWithCache( itemsToRemove );
return true;
}
QList<BuildItem> ProjectBuildSetModel::items()
{
return m_items ;
}
void ProjectBuildSetModel::projectClosed( KDevelop::IProject* project )
{
for( int i = m_items.count() - 1; i >= 0; i-- )
{
if( m_items.at(i).itemProject() == project->name())
{
beginRemoveRows( QModelIndex(), i, i );
m_items.removeAt(i);
endRemoveRows();
}
}
}
void ProjectBuildSetModel::saveToProject( KDevelop::IProject* project ) const
{
QVariantList paths;
foreach( const BuildItem &item, m_items)
{
if( item.itemProject() == project->name() )
paths.append(item.itemPath());
}
KConfigGroup base = project->projectConfiguration()->group("Buildset");
base.writeEntry("BuildItems", KDevelop::qvariantToString( QVariant( paths ) ));
base.sync();
}
void ProjectBuildSetModel::loadFromProject( KDevelop::IProject* project )
{
KConfigGroup base = project->projectConfiguration()->group("Buildset");
if (base.hasKey("BuildItems")) {
QVariantList items = KDevelop::stringToQVariant(base.readEntry("BuildItems", QString())).toList();
foreach(const QVariant& path, items)
{
insertItemWithCache( BuildItem( path.toStringList() ) );
}
} else {
// Add project to buildset, but only if there is no configuration for this project yet.
addProjectItem( project->projectItem() );
}
}
void ProjectBuildSetModel::moveRowsDown(int row, int count)
{
QList<BuildItem> items = m_items.mid( row, count );
removeRows( row, count );
insertItemsOverrideCache( row + 1, items );
}
void ProjectBuildSetModel::moveRowsToBottom(int row, int count)
{
QList<BuildItem> items = m_items.mid( row, count );
removeRows( row, count );
insertItemsOverrideCache( rowCount(), items );
}
void ProjectBuildSetModel::moveRowsUp(int row, int count)
{
QList<BuildItem> items = m_items.mid( row, count );
removeRows( row, count );
insertItemsOverrideCache( row - 1, items );
}
void ProjectBuildSetModel::moveRowsToTop(int row, int count)
{
QList<BuildItem> items = m_items.mid( row, count );
removeRows( row, count );
insertItemsOverrideCache( 0, items );
}
}