diff --git a/src/ksvnwidgets/models/commitmodel.cpp b/src/ksvnwidgets/models/commitmodel.cpp index 4b5be8ab..ac9d1ffa 100644 --- a/src/ksvnwidgets/models/commitmodel.cpp +++ b/src/ksvnwidgets/models/commitmodel.cpp @@ -1,316 +1,325 @@ /*************************************************************************** * Copyright (C) 2005-2009 by Rajko Albrecht * * ral@alwins-world.de * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU 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 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 "commitmodel.h" #include "commitmodelhelper.h" #include "svnqt/commititem.h" #include CommitModel::CommitModel(const svn::CommitItemList &aList, QObject *parent) : QAbstractItemModel(parent) { setCommitData(aList); } /********************* * Begin CommitModel * *********************/ CommitModel::CommitModel(const CommitActionEntries &_checked, const CommitActionEntries &_notchecked, QObject *parent) : QAbstractItemModel(parent) { setCommitData(_checked, _notchecked); } CommitModel::~CommitModel() { } void CommitModel::setCommitData(const svn::CommitItemList &aList) { - beginRemoveRows(QModelIndex(), 0, m_List.count()); - m_List.clear(); - endRemoveRows(); - - m_List.reserve(aList.size()); - beginInsertRows(QModelIndex(), 0, aList.size() - 1); - for (int j = 0; j < aList.size(); ++j) { - m_List.append(CommitModelNodePtr(new CommitModelNode(aList[j]))); + if (!m_List.isEmpty()) { + beginRemoveRows(QModelIndex(), 0, m_List.count() - 1); + m_List.clear(); + endRemoveRows(); + } + + if (!aList.isEmpty()) { + m_List.reserve(aList.size()); + beginInsertRows(QModelIndex(), 0, aList.size() - 1); + for (int j = 0; j < aList.size(); ++j) { + m_List.append(CommitModelNodePtr(new CommitModelNode(aList[j]))); + } + endInsertRows(); } - endInsertRows(); } void CommitModel::setCommitData(const CommitActionEntries &checked, const CommitActionEntries ¬checked) { - beginRemoveRows(QModelIndex(), 0, m_List.count()); - m_List.clear(); - endRemoveRows(); - - m_List.reserve(checked.size() + notchecked.size()); - beginInsertRows(QModelIndex(), 0, checked.size() + notchecked.size() - 1); - for (int j = 0; j < checked.size(); ++j) { - m_List.append(CommitModelNodePtr(new CommitModelNode(checked[j], true))); + if (!m_List.isEmpty()) { + beginRemoveRows(QModelIndex(), 0, m_List.count() - 1); + m_List.clear(); + endRemoveRows(); } - for (int j = 0; j < notchecked.size(); ++j) { - m_List.append(CommitModelNodePtr(new CommitModelNode(notchecked[j], false))); + + const int totalSize = checked.size() + notchecked.size(); + if (totalSize > 0) { + m_List.reserve(totalSize); + beginInsertRows(QModelIndex(), 0, totalSize - 1); + for (int j = 0; j < checked.size(); ++j) { + m_List.append(CommitModelNodePtr(new CommitModelNode(checked[j], true))); + } + for (int j = 0; j < notchecked.size(); ++j) { + m_List.append(CommitModelNodePtr(new CommitModelNode(notchecked[j], false))); + } + endInsertRows(); } - endInsertRows(); } int CommitModel::ActionColumn()const { return 0; } int CommitModel::ItemColumn()const { return 1; } CommitModelNodePtr CommitModel::node(const QModelIndex &index) { if (!index.isValid() || index.row() >= m_List.count()) { return CommitModelNodePtr(); } return m_List.at(index.row()); } CommitActionEntries CommitModel::checkedEntries()const { CommitActionEntries res; for (int i = 0; i < m_List.count(); ++i) { if (m_List.at(i)->checked()) { res.append(m_List.at(i)->actionEntry()); } } return res; } void CommitModel::markItems(bool mark, CommitActionEntry::ACTION_TYPE _type) { QVariant v = mark ? int(2) : int(0); for (int i = 0; i < m_List.count(); ++i) { if (m_List.at(i)->actionEntry().type() & _type) { QModelIndex _index = index(i, 0, QModelIndex()); setData(_index, v, Qt::CheckStateRole); dataChanged(_index, _index); } } } /*! \fn CommitModel::removeEntries(const QStringList&) */ void CommitModel::removeEntries(const QStringList &_items) { QStringList items = _items; // items is normally much smaller than m_List, therefore // iterate over the items in the inner loop for (int j = m_List.count() - 1; j >= 0; --j) { const QString aeName = m_List.at(j)->actionEntry().name(); for (int i = items.size() - 1; i >= 0; --i) { if (aeName == items.at(i)) { beginRemoveRows(QModelIndex(), j, j); m_List.remove(j); endRemoveRows(); items.removeAt(i); break; // break inner loop } } if (items.isEmpty()) break; } } const CommitModelNodePtr CommitModel::dataForRow(int row) const { if (row < 0 || row >= m_List.size()) return CommitModelNodePtr(); return m_List.at(row); } /************************************ * begin overload of Model methods * ************************************/ QModelIndex CommitModel::index(int row, int column, const QModelIndex & /*parent*/)const { if (row < 0 || row >= m_List.count()) { return QModelIndex(); } const CommitModelNodePtr &n = m_List.at(row); return createIndex(row, column, n.data()); } QModelIndex CommitModel::parent(const QModelIndex &)const { // we have no tree... return QModelIndex(); } QVariant CommitModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= m_List.count() || role != Qt::DisplayRole) { return QVariant(); } const CommitModelNodePtr &n = m_List.at(index.row()); if (index.column() == ActionColumn()) { return n->actionEntry().action(); } if (index.column() == ItemColumn()) { return n->actionEntry().name(); } return QVariant(); } QVariant CommitModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == ActionColumn()) { return i18n("Action"); } if (section == ItemColumn()) { return i18n("Entry"); } } return QAbstractItemModel::headerData(section, orientation, role); } int CommitModel::rowCount(const QModelIndex &) const { return m_List.count(); } int CommitModel::columnCount(const QModelIndex &) const { return 2; } /************************************ * end overload of Model methods * ************************************/ /********************* * end CommitModel * *********************/ /************************************ * begin CommitModelCheckitem * ************************************/ CommitModelCheckitem::CommitModelCheckitem(const CommitActionEntries &_checked, const CommitActionEntries &_notchecked, QObject *parent) : CommitModel(_checked, _notchecked, parent) { } CommitModelCheckitem::~CommitModelCheckitem() { } int CommitModelCheckitem::ActionColumn()const { return 1; } int CommitModelCheckitem::ItemColumn()const { return 0; } Qt::ItemFlags CommitModelCheckitem::flags(const QModelIndex &index) const { if (index.isValid() && index.column() == ItemColumn()) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; } return CommitModel::flags(index); } QVariant CommitModelCheckitem::data(const QModelIndex &index, int role) const { if (index.column() != ItemColumn() || role != Qt::CheckStateRole || !index.isValid() || index.row() >= m_List.count()) { return CommitModel::data(index, role); } if (m_List.at(index.row())->checked()) { return Qt::Checked; } return Qt::Unchecked; } bool CommitModelCheckitem::setData(const QModelIndex &index, const QVariant &value, int role) { if (index.column() != ItemColumn() || role != Qt::CheckStateRole || !index.isValid() || index.row() >= m_List.count()) { return CommitModel::setData(index, value, role); } if (value.type() == QVariant::Int) { CommitModelNodePtr _l = m_List.at(index.row()); bool old = _l->checked(); bool nv = value.toInt() > 0; _l->setChecked(nv); if (old != nv) { emit dataChanged(index, index); } return old != nv; } return false; } /************************************ * end CommitModelCheckitem * ************************************/ /*************************** * Begin CommitFilterModel * ***************************/ CommitFilterModel::CommitFilterModel(QObject *parent) : QSortFilterProxyModel(parent) , m_sourceModel(0) , m_visibleTypes(CommitActionEntry::ALL) {} CommitFilterModel::~CommitFilterModel() {} void CommitFilterModel::setSourceModel(QAbstractItemModel *sourceModel) { m_sourceModel = qobject_cast(sourceModel); QSortFilterProxyModel::setSourceModel(sourceModel); } bool CommitFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { if (!m_sourceModel || source_parent.isValid()) return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); const CommitModelNodePtr node = m_sourceModel->dataForRow(source_row); return ((node->actionEntry().type() & m_visibleTypes) != 0); } void CommitFilterModel::hideItems(bool bHide, CommitActionEntry::ACTION_TYPE aType) { const CommitActionEntry::ActionTypes curVisibleTypes = m_visibleTypes; if (bHide) { m_visibleTypes &= ~aType; } else { m_visibleTypes |= aType; } if (m_visibleTypes != curVisibleTypes) { invalidateFilter(); } } /************************* * end CommitFilterModel * *************************/ diff --git a/src/svnfrontend/models/svnitemmodel.cpp b/src/svnfrontend/models/svnitemmodel.cpp index 0c76e50d..8e99e642 100644 --- a/src/svnfrontend/models/svnitemmodel.cpp +++ b/src/svnfrontend/models/svnitemmodel.cpp @@ -1,921 +1,923 @@ /*************************************************************************** * Copyright (C) 2008 by Rajko Albrecht ral@alwins-world.de * * http://kdesvn.alwins-world.de/ * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU 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 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 "svnitemmodel.h" #include "svnitemnode.h" #include "svnactions.h" #include "getinfothread.h" #include "svnfrontend/maintreewidget.h" #include "settings/kdesvnsettings.h" #include "helpers/kdesvn_debug.h" #include "svnqt/status.h" #include "svnqt/client.h" #include "svnqt/path.h" #include "svnqt/svnqt_defines.h" #include #include #include #include #include #include #include #include #include /***************************** * Internal data class begin * *****************************/ class SvnItemModelData { SvnItemModelData(const SvnItemModelData &); SvnItemModelData &operator=(const SvnItemModelData &); public: SvnItemModelData(SvnItemModel *aCb, MainTreeWidget *display) : m_rootNode(0), m_SvnActions(NULL), m_Cb(aCb), m_Display(display), m_DirWatch(NULL) { m_Uid = QUuid::createUuid().toString(); m_InfoThread = new GetInfoThread(aCb); } ~SvnItemModelData() { m_InfoThread->cancelMe(); if (!m_InfoThread->wait(500)) { m_InfoThread->terminate(); } delete m_InfoThread; delete m_rootNode; delete m_DirWatch; m_rootNode = 0; } void clear() { delete m_rootNode; delete m_DirWatch; m_DirWatch = 0; m_rootNode = new SvnItemModelNodeDir(m_SvnActions, m_Display); } SvnItemModelNode *nodeForIndex(const QModelIndex &index)const { return index.isValid() ? static_cast(index.internalPointer()) : m_rootNode; } QModelIndex indexForNode(SvnItemModelNode *node, int rowNumber = -1)const { if (!node || node == m_rootNode) { return QModelIndex(); } return m_Cb->createIndex(rowNumber == -1 ? node->rowNumber() : rowNumber, 0, node); } bool isRemoteAdded(const svn::Status &_Stat)const { return m_SvnActions->isUpdated(_Stat.path()) && _Stat.validReposStatus() && !_Stat.validLocalStatus(); } bool MustCreateDir(const svn::Status &_Stat)const { // keep in sync with SvnItem::isDir() if (_Stat.entry().isValid() || isRemoteAdded(_Stat)) { if (_Stat.entry().kind() != svn_node_unknown) { return _Stat.entry().kind() == svn_node_dir; } } /* must be a local file */ QFileInfo f(_Stat.path()); return f.isDir(); } void addWatchFile(const QString &aFile) { if (m_DirWatch) { m_DirWatch->addFile(aFile); } } void addWatchDir(const QString &aDir) { if (m_DirWatch) { m_DirWatch->addDir(aDir); } } SvnItemModelNodeDir *m_rootNode; SvnActions *m_SvnActions; SvnItemModel *m_Cb; MainTreeWidget *m_Display; KDirWatch *m_DirWatch; QString m_Uid; mutable GetInfoThread *m_InfoThread; }; /***************************** * Internal data class end * *****************************/ SvnItemModel::SvnItemModel(MainTreeWidget *display, QObject *parent) : QAbstractItemModel(parent), m_Data(new SvnItemModelData(this, display)) { m_Data->m_SvnActions = new SvnActions(display); m_Data->m_rootNode = new SvnItemModelNodeDir(m_Data->m_SvnActions, display); } SvnItemModel::~SvnItemModel() { } SvnItemModelNode *SvnItemModel::firstRootChild() { if (!m_Data->m_rootNode) { return 0; } return m_Data->m_rootNode->child(0); } QModelIndex SvnItemModel::firstRootIndex() { return m_Data->indexForNode(firstRootChild()); } SvnItemModelNode *SvnItemModel::nodeForIndex(const QModelIndex &index) { return m_Data->nodeForIndex(index); } void SvnItemModel::setRootNodeStat(const svn::StatusPtr &stat) { m_Data->m_rootNode->setStat(stat); } void SvnItemModel::clear() { int numRows = m_Data->m_rootNode->childList().count(); - beginRemoveRows(QModelIndex(), 0, numRows); + if (numRows > 0) + beginRemoveRows(QModelIndex(), 0, numRows - 1); m_Data->clear(); - endRemoveRows(); + if (numRows > 0) + endRemoveRows(); } void SvnItemModel::beginRemoveRows(const QModelIndex &parent, int first, int last) { m_Data->m_InfoThread->clearNodes(); m_Data->m_InfoThread->cancelMe(); if (!m_Data->m_InfoThread->wait(1000)) { } QAbstractItemModel::beginRemoveRows(parent, first, last); } void SvnItemModel::clearNodeDir(SvnItemModelNodeDir *node) { QModelIndex ind = m_Data->indexForNode(node); if (!node) { node = m_Data->m_rootNode; } int numRows = node->childList().size(); beginRemoveRows(ind, 0, numRows); node->clear(); endRemoveRows(); } bool SvnItemModel::hasChildren(const QModelIndex &parent)const { if (!parent.isValid()) { return true; } return static_cast(parent.internalPointer())->NodeHasChilds(); } bool SvnItemModel::filterIndex(const QModelIndex &parent, int childRow, svnmodel::ItemTypeFlag showOnly)const { SvnItemModelNode *node = m_Data->nodeForIndex(parent); if (childRow < 0) { return false; } if (!node->NodeIsDir()) { qCDebug(KDESVN_LOG) << "Parent ist kein Dir" << endl; return false; } SvnItemModelNode *child = static_cast(node)->child(childRow); if (child) { if ((child->isDir() && !showOnly.testFlag(svnmodel::Dir)) || (!child->isDir() && !showOnly.testFlag(svnmodel::File))) { return true; } return ItemDisplay::filterOut(child); } return false; } QVariant SvnItemModel::data(const QModelIndex &index, int role)const { SvnItemModelNode *node = m_Data->nodeForIndex(index); switch (role) { case Qt::DisplayRole: case SORT_ROLE: switch (index.column()) { case Name: return node->shortName(); case Status: return node->infoText(); case LastRevision: return QString::number(node->cmtRev()); case LastAuthor: return node->cmtAuthor(); case LastDate: return node->fullDate(); case Locked: return node->lockOwner(); } break; case Qt::DecorationRole: if (index.column() == 0) { int size = Kdesvnsettings::listview_icon_size(); bool overlay = Kdesvnsettings::display_overlays(); return node->getPixmap(size, overlay); } break; case Qt::EditRole: switch (index.column()) { case Name: return node->shortName(); } break; case Qt::BackgroundRole: { QColor cl = node->backgroundColor(); if (cl.isValid()) { return QBrush(cl); } break; } case Qt::ToolTipRole: { switch (index.column()) { case Name: if (node->hasToolTipText()) { return node->getToolTipText(); } else { m_Data->m_InfoThread->appendNode(node); return QVariant(); } } break; } } return QVariant(); } QModelIndex SvnItemModel::index(int row, int column, const QModelIndex &parent)const { SvnItemModelNode *node = m_Data->nodeForIndex(parent); if (row < 0) { return QModelIndex(); } Q_ASSERT(node->NodeIsDir()); SvnItemModelNode *child = static_cast(node)->child(row); if (child) { return createIndex(row, column, child); } else { return QModelIndex(); } } QVariant SvnItemModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Vertical) { return QVariant(); } switch (role) { case Qt::DisplayRole: switch (section) { case Name: return (i18n("Name")); case Status: return (i18n("Status")); case LastRevision: return (i18n("Last changed Revision")); case LastAuthor: return (i18n("Last author")); case LastDate: return (i18n("Last change date")); case Locked: return (i18n("Locked by")); } } return QVariant(); } int SvnItemModel::columnCount(const QModelIndex & /*parent*/)const { return ColumnCount; } int SvnItemModel::rowCount(const QModelIndex &parent)const { if (!m_Data || !m_Data->m_rootNode) { return 0; } if (!parent.isValid()) { return m_Data->m_rootNode->childList().count(); } SvnItemModelNodeDir *node = static_cast(m_Data->nodeForIndex(parent)); return node->childList().count(); } QModelIndex SvnItemModel::parent(const QModelIndex &index)const { if (!index.isValid()) { return QModelIndex(); } SvnItemModelNode *child = static_cast(index.internalPointer()); return m_Data->indexForNode(child->parent()); } SvnActions *SvnItemModel::svnWrapper() { return m_Data->m_SvnActions; } int SvnItemModel::checkDirs(const QString &_what, SvnItemModelNode *_parent) { QString what = _what; svn::StatusEntries dlist; while (what.endsWith(QLatin1Char('/'))) { what.chop(1); } // prevent this from checking unversioned folder. FIXME: what happen when we do open url on a non-working-copy folder?? #ifdef DEBUG_TIMER QTime _counttime; _counttime.start(); #endif if (!m_Data->m_Display->isWorkingCopy() || (!_parent) || ((_parent) && (_parent->isVersioned()))) { if (!svnWrapper()->makeStatus(what, dlist, m_Data->m_Display->baseRevision(), false, true, true)) { return -1; } } else { return checkUnversionedDirs(_parent); } #ifdef DEBUG_TIMER qCDebug(KDESVN_LOG) << "Time for getting entries: " << _counttime.elapsed(); _counttime.restart(); #endif svn::StatusEntries neweritems; svnWrapper()->getaddedItems(what, neweritems); dlist += neweritems; svn::StatusEntries::iterator it = dlist.begin(); SvnItemModelNode *node = 0; for (; it != dlist.end(); ++it) { if ((*it)->path() == what || (*it)->entry().url().toString() == what) { if (!_parent) { // toplevel item beginInsertRows(m_Data->indexForNode(m_Data->m_rootNode), 0, 0); if ((*it)->entry().kind() == svn_node_dir) { node = new SvnItemModelNodeDir(m_Data->m_rootNode, svnWrapper(), m_Data->m_Display); } else { node = new SvnItemModelNode(m_Data->m_rootNode, svnWrapper(), m_Data->m_Display); } node->setStat((*it)); m_Data->m_rootNode->m_Children.prepend(node); endInsertRows(); } dlist.erase(it); break; } } if (_parent) { node = _parent; } #ifdef DEBUG_TIMER qCDebug(KDESVN_LOG) << "Time finding parent node: " << _counttime.elapsed(); #endif insertDirs(node, dlist); return dlist.size(); } void SvnItemModel::insertDirs(SvnItemModelNode *_parent, svn::StatusEntries &dlist) { if (dlist.isEmpty()) { return; } QModelIndex ind = m_Data->indexForNode(_parent); SvnItemModelNodeDir *parent; if (!_parent) { parent = m_Data->m_rootNode; } else { parent = static_cast(_parent); } SvnItemModelNode *node = 0; beginInsertRows(ind, parent->childList().count(), parent->childList().count() + dlist.count() - 1); svn::StatusEntries::iterator it = dlist.begin(); #ifdef DEBUG_TIMER QTime _counttime; _counttime.start(); #endif for (; it != dlist.end(); ++it) { #ifdef DEBUG_TIMER _counttime.restart(); #endif if (m_Data->MustCreateDir(*(*it))) { node = new SvnItemModelNodeDir(parent, svnWrapper(), m_Data->m_Display); } else { node = new SvnItemModelNode(parent, svnWrapper(), m_Data->m_Display); } node->setStat((*it)); #ifdef DEBUG_TIMER // qCDebug(KDESVN_LOG)<<"Time creating item: "<<_counttime.elapsed(); _counttime.restart(); #endif if (m_Data->m_Display->isWorkingCopy() && m_Data->m_DirWatch) { if (node->isDir()) { m_Data->addWatchDir(node->fullName()); } else { m_Data->addWatchFile(node->fullName()); } } #ifdef DEBUG_TIMER // qCDebug(KDESVN_LOG)<<"Time add watch: "<<_counttime.elapsed(); _counttime.restart(); #endif parent->m_Children.append(node); #ifdef DEBUG_TIMER // qCDebug(KDESVN_LOG)<<"Time append node: "<<_counttime.elapsed(); #endif } #ifdef DEBUG_TIMER _counttime.restart(); #endif endInsertRows(); #ifdef DEBUG_TIMER // qCDebug(KDESVN_LOG)<<"Time append all node: "<<_counttime.elapsed(); #endif } bool SvnItemModel::canFetchMore(const QModelIndex &parent)const { if (!parent.isValid()) { return false; } SvnItemModelNode *node = static_cast(parent.internalPointer()); return node->NodeHasChilds() && static_cast(node)->childList().isEmpty(); } void SvnItemModel::fetchMore(const QModelIndex &parent) { SvnItemModelNode *node = static_cast(parent.internalPointer()); if (!node->isDir()) { return; } if (checkDirs(node->fullName(), node) > 0) { emit itemsFetched(parent); } } bool SvnItemModel::insertRows(int , int, const QModelIndex &) { return false; } bool SvnItemModel::insertColumns(int, int, const QModelIndex &) { return false; } bool SvnItemModel::removeRows(int, int, const QModelIndex &) { return false; } bool SvnItemModel::removeColumns(int, int, const QModelIndex &) { return false; } Qt::ItemFlags SvnItemModel::flags(const QModelIndex &index) const { Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable; if (index.column() == Name) { f |= /*Qt::ItemIsEditable |*/ Qt::ItemIsDragEnabled; } if (!index.isValid()) { f |= Qt::ItemIsDropEnabled; } else { SvnItemModelNode *node = m_Data->nodeForIndex(index); if (node && node->isDir()) { f |= Qt::ItemIsDropEnabled; } } return f; } Qt::DropActions SvnItemModel::supportedDropActions()const { return Qt::CopyAction | Qt::MoveAction; } QStringList SvnItemModel::mimeTypes() const { return QStringList() << QLatin1String("text/uri-list") /* << QLatin1String( "application/x-kde-cutselection" ) */ // TODO //<< QLatin1String( "text/plain" ) << QLatin1String("application/x-kde-urilist"); } bool SvnItemModel::dropUrls(const QList &data, Qt::DropAction action, int row, int column, const QModelIndex &parent, bool intern) { Q_UNUSED(row); Q_UNUSED(column); if (action == Qt::IgnoreAction) { return true; } if (action == Qt::LinkAction) { return false; } emit urlDropped(data, action, parent, intern); return true; } QMimeData *SvnItemModel::mimeData(const QModelIndexList &indexes)const { QList urls; foreach (const QModelIndex &index, indexes) { if (index.column() == 0) { urls << m_Data->nodeForIndex(index)->kdeName(m_Data->m_Display->baseRevision()); } } QMimeData *mimeData = new QMimeData(); mimeData->setUrls(urls); KUrlMimeData::MetaDataMap metaMap; metaMap["kdesvn-source"] = QLatin1Char('t'); metaMap["kdesvn-id"] = uniqueIdentifier(); KUrlMimeData::setMetaData(metaMap, mimeData); return mimeData; } void SvnItemModel::makeIgnore(const QModelIndex &index) { if (!index.isValid()) { return; } SvnItemModelNode *node = m_Data->nodeForIndex(index); if (!node || node == m_Data->m_rootNode || node->isRealVersioned()) { return; } SvnItemModelNodeDir *pa = node->parent(); if (!pa) { return; } if (m_Data->m_SvnActions->makeIgnoreEntry(node, node->isIgnored())) { refreshIndex(index); refreshItem(pa); } } bool SvnItemModel::refreshItem(SvnItemModelNode *item) { if (!item || item == m_Data->m_rootNode) { return false; } try { item->setStat(m_Data->m_SvnActions->svnclient()->singleStatus(item->fullName(), false, m_Data->m_Display->baseRevision())); } catch (const svn::ClientException &e) { item->setStat(svn::StatusPtr(new svn::Status)); return false; } return true; } bool SvnItemModel::refreshIndex(const QModelIndex &ind, bool sendSignal) { bool ret = refreshItem(m_Data->nodeForIndex(ind)); if (sendSignal) { emit dataChanged(ind, ind); } return ret; } SvnItemModelNode *SvnItemModel::findPath(const svn::Path &_p) { QString ip = _p.path(); SvnItemModelNode *n1 = firstRootChild(); if (n1) { if (n1->fullName().length() < ip.length()) { ip = ip.right(ip.length() - n1->fullName().length()); } else if (n1->fullName() == ip) { return n1; } if (!n1->isDir()) { return 0; } QStringList lp = ip.split('/', QString::SkipEmptyParts); SvnItemModelNodeDir *d1 = static_cast(n1); return d1->findPath(lp); } return 0; } QModelIndex SvnItemModel::findIndex(const svn::Path &_p) { return m_Data->indexForNode(findPath(_p)); } void SvnItemModel::initDirWatch() { delete m_Data->m_DirWatch; m_Data->m_DirWatch = 0; if (m_Data->m_Display->isWorkingCopy()) { m_Data->m_DirWatch = new KDirWatch(this); connect(m_Data->m_DirWatch, SIGNAL(dirty(QString)), this, SLOT(slotDirty(QString))); connect(m_Data->m_DirWatch, SIGNAL(created(QString)), this, SLOT(slotCreated(QString))); connect(m_Data->m_DirWatch, SIGNAL(deleted(QString)), this, SLOT(slotDeleted(QString))); if (m_Data->m_DirWatch) { m_Data->m_DirWatch->addDir(m_Data->m_Display->baseUri() + QLatin1Char('/'), KDirWatch::WatchDirOnly); m_Data->m_DirWatch->startScan(true); } } } void SvnItemModel::slotCreated(const QString &what) { QModelIndex ind = findIndex(what); if (!ind.isValid()) { return; } SvnItemModelNode *n = static_cast(ind.internalPointer()); if (!n) { return; } if (n->isRealVersioned()) { refreshIndex(ind); } } void SvnItemModel::slotDeleted(const QString &what) { QModelIndex ind = findIndex(what); if (!ind.isValid()) { m_Data->m_DirWatch->removeDir(what); m_Data->m_DirWatch->removeFile(what); return; } SvnItemModelNode *n = static_cast(ind.internalPointer()); if (!n) { return; } if (!n->isRealVersioned()) { SvnItemModelNodeDir *p = n->parent(); QModelIndex pi = m_Data->indexForNode(p); if (!pi.isValid()) { return; } if (ind.row() >= p->m_Children.count()) { return; } beginRemoveRows(pi, ind.row(), ind.row()); p->m_Children.removeAt(ind.row()); endRemoveRows(); if (n->isDir()) { m_Data->m_DirWatch->removeDir(what); } else { m_Data->m_DirWatch->removeFile(what); } } else { refreshIndex(ind); } } void SvnItemModel::checkAddNewItems(const QModelIndex &ind) { SvnItemModelNodeDir *n = static_cast(ind.internalPointer()); QString what = n->fullName(); svn::StatusEntries dlist; while (what.endsWith(QLatin1Char('/'))) { what.chop(1); } if (!svnWrapper()->makeStatus(what, dlist, m_Data->m_Display->baseRevision(), false, true, true)) { return; } svn::StatusEntries::iterator it; for (it = dlist.begin(); it != dlist.end();) { if (n->contains((*it)->path()) || (*it)->path() == what) { it = dlist.erase(it); } else { ++it; } } if (!dlist.isEmpty()) { insertDirs(n, dlist); } } void SvnItemModel::slotDirty(const QString &what) { QModelIndex ind = findIndex(what); if (!ind.isValid()) { return; } SvnItemModelNode *n = static_cast(ind.internalPointer()); if (!n) { return; } if (n->isRealVersioned()) { if (!n->isDir()) { refreshIndex(ind); } else { checkAddNewItems(ind); } } else if (n->isDir()) { checkUnversionedDirs(n); } } bool SvnItemModel::checkRootNode() { if (!m_Data->m_rootNode) { return false; } try { m_Data->m_rootNode->setStat(m_Data->m_SvnActions->svnclient()->singleStatus(m_Data->m_Display->baseUri(), false, m_Data->m_Display->baseRevision())); } catch (const svn::ClientException &e) { m_Data->m_rootNode->setStat(svn::StatusPtr(new svn::Status)); emit clientException(e.msg()); return false; } return true; } bool SvnItemModel::refreshCurrentTree() { bool check_created = false; if (!m_Data->m_rootNode) { return false; } SvnItemModelNodeDir *_start = m_Data->m_rootNode; if (m_Data->m_Display->isWorkingCopy()) { if (!m_Data->m_rootNode->m_Children.isEmpty() && m_Data->m_rootNode->m_Children.at(0)->NodeIsDir()) { _start = static_cast(m_Data->m_rootNode->m_Children.at(0)); refreshItem(_start); } else { return false; } } else { if (!checkRootNode()) { return false; } _start = m_Data->m_rootNode; check_created = true; } return refreshDirnode(_start, check_created); } bool SvnItemModel::refreshDirnode(SvnItemModelNodeDir *node, bool check_empty, bool notrec) { if (!node) { if (m_Data->m_Display->isWorkingCopy()) { return false; } else { if (!checkRootNode()) { return false; } node = m_Data->m_rootNode; } } QString what = (node != m_Data->m_rootNode) ? node->fullName() : m_Data->m_Display->baseUri(); if (node->m_Children.isEmpty() && !check_empty) { if (node->fullName() == m_Data->m_Display->baseUri()) { return refreshItem(node); } return true; } svn::StatusEntries dlist; if (!svnWrapper()->makeStatus(what, dlist, m_Data->m_Display->baseRevision())) { return false; } if (m_Data->m_Display->isWorkingCopy()) { svn::StatusEntries neweritems; svnWrapper()->getaddedItems(what, neweritems); dlist += neweritems; } svn::StatusEntries::iterator it = dlist.begin(); for (it = dlist.begin(); it != dlist.end(); ++it) { if ((*it)->path() == what) { dlist.erase(it); break; } } QModelIndex ind = m_Data->indexForNode(node); for (int i = 0; i < node->m_Children.size(); ++i) { bool found = false; for (it = dlist.begin(); it != dlist.end(); ++it) { if ((*it)->path() == node->m_Children[i]->fullName()) { found = true; break; } } if (!found) { SvnItemModelNode *n = node->m_Children[i]; beginRemoveRows(ind, i, i); node->m_Children.removeAt(i); delete n; endRemoveRows(); --i; } } for (it = dlist.begin(); it != dlist.end();) { int index = node->indexOf((*it)->path()); if (index != -1) { node->m_Children[index]->setStat((*it)); if (node->m_Children[index]->NodeIsDir() != node->m_Children[index]->isDir()) { SvnItemModelNode *n = node->m_Children[index]; beginRemoveRows(ind, index, index); node->m_Children.removeAt(index); delete n; endRemoveRows(); } else { it = dlist.erase(it); } } else { ++it; } } // make sure that we do not read in the whole tree when just refreshing the current tree. if (!node->m_Children.isEmpty() && !notrec) { for (int i = 0; i < node->m_Children.size(); ++i) { if (node->m_Children[i]->NodeIsDir()) { // both other parameters makes no sense at this point - defaults refreshDirnode(static_cast(node->m_Children[i]), false, false); } } } // after so we don't recurse about it. insertDirs(node, dlist); if (!dlist.isEmpty()) { itemsFetched(m_Data->indexForNode(node)); } return true; } int SvnItemModel::checkUnversionedDirs(SvnItemModelNode *_parent) { if (!_parent || !_parent->isDir()) { // no toplevel unversioned - kdesvn is not a filemanager return 0; } QDir d(_parent->fullName()); d.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); QFileInfoList list = d.entryInfoList(); if (list.isEmpty()) { return 0; } svn::StatusEntries dlist; SvnItemModelNodeDir *n = static_cast(_parent); for (QFileInfoList::size_type i = 0; i < list.size(); ++i) { if (!(n->contains(list[i].absoluteFilePath()) || list[i].absoluteFilePath() == n->fullName())) { svn::StatusPtr stat(new svn::Status(list[i].absoluteFilePath())); dlist.append(stat); } } if (!dlist.isEmpty()) { insertDirs(_parent, dlist); } return dlist.size(); } const QString &SvnItemModel::uniqueIdentifier()const { return m_Data->m_Uid; } void SvnItemModel::slotNotifyMessage(const QString &msg) { qCDebug(KDESVN_LOG) << msg; }