File indexing completed on 2024-05-12 05:44:27
0001 /*************************************************************************** 0002 * Copyright (C) 2008 by Rajko Albrecht ral@alwins-world.de * 0003 * https://kde.org/applications/development/org.kde.kdesvn * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, * 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0013 * GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License * 0016 * along with this program; if not, write to the * 0017 * Free Software Foundation, Inc., * 0018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 0019 ***************************************************************************/ 0020 0021 #include "svnitemmodel.h" 0022 #include "getinfothread.h" 0023 #include "helpers/kdesvn_debug.h" 0024 #include "settings/kdesvnsettings.h" 0025 #include "svnactions.h" 0026 #include "svnfrontend/maintreewidget.h" 0027 #include "svnitemnode.h" 0028 0029 #include "svnqt/client.h" 0030 #include "svnqt/path.h" 0031 #include "svnqt/status.h" 0032 #include "svnqt/svnqt_defines.h" 0033 0034 #include <KDirWatch> 0035 #include <KLocalizedString> 0036 #include <KUrlMimeData> 0037 0038 #include <QBrush> 0039 #include <QDir> 0040 #include <QFileInfo> 0041 #include <QItemSelectionModel> 0042 #include <QMimeData> 0043 #include <QUuid> 0044 0045 /***************************** 0046 * Internal data class begin * 0047 *****************************/ 0048 class SvnItemModelData 0049 { 0050 SvnItemModelData(const SvnItemModelData &); 0051 SvnItemModelData &operator=(const SvnItemModelData &); 0052 0053 public: 0054 SvnItemModelData(SvnItemModel *aCb, MainTreeWidget *display) 0055 : m_rootNode(nullptr) 0056 , m_SvnActions(nullptr) 0057 , m_Cb(aCb) 0058 , m_Display(display) 0059 , m_DirWatch(nullptr) 0060 { 0061 m_Uid = QUuid::createUuid().toString(); 0062 m_InfoThread = new GetInfoThread(aCb); 0063 } 0064 0065 ~SvnItemModelData() 0066 { 0067 m_InfoThread->cancelMe(); 0068 if (!m_InfoThread->wait(500)) { 0069 m_InfoThread->terminate(); 0070 } 0071 delete m_InfoThread; 0072 0073 delete m_rootNode; 0074 delete m_DirWatch; 0075 m_rootNode = nullptr; 0076 } 0077 0078 void clear() 0079 { 0080 delete m_rootNode; 0081 delete m_DirWatch; 0082 m_DirWatch = nullptr; 0083 m_rootNode = new SvnItemModelNodeDir(m_SvnActions, m_Display); 0084 } 0085 0086 SvnItemModelNode *nodeForIndex(const QModelIndex &index) const 0087 { 0088 return index.isValid() ? static_cast<SvnItemModelNode *>(index.internalPointer()) : m_rootNode; 0089 } 0090 0091 QModelIndex indexForNode(SvnItemModelNode *node, int rowNumber = -1) const 0092 { 0093 if (!node || node == m_rootNode) { 0094 return QModelIndex(); 0095 } 0096 return m_Cb->createIndex(rowNumber == -1 ? node->rowNumber() : rowNumber, 0, node); 0097 } 0098 0099 bool isRemoteAdded(const svn::Status &_Stat) const 0100 { 0101 return m_SvnActions->isUpdated(_Stat.path()) && _Stat.validReposStatus() && !_Stat.validLocalStatus(); 0102 } 0103 0104 bool MustCreateDir(const svn::Status &_Stat) const 0105 { 0106 // keep in sync with SvnItem::isDir() 0107 if (_Stat.entry().isValid() || isRemoteAdded(_Stat)) { 0108 if (_Stat.entry().kind() != svn_node_unknown) { 0109 return _Stat.entry().kind() == svn_node_dir; 0110 } 0111 } 0112 /* must be a local file */ 0113 QFileInfo f(_Stat.path()); 0114 return f.isDir(); 0115 } 0116 0117 void addWatchFile(const QString &aFile) 0118 { 0119 if (m_DirWatch) { 0120 m_DirWatch->addFile(aFile); 0121 } 0122 } 0123 void addWatchDir(const QString &aDir) 0124 { 0125 if (m_DirWatch) { 0126 m_DirWatch->addDir(aDir); 0127 } 0128 } 0129 0130 SvnItemModelNodeDir *m_rootNode; 0131 0132 SvnActions *m_SvnActions; 0133 SvnItemModel *m_Cb; 0134 MainTreeWidget *m_Display; 0135 KDirWatch *m_DirWatch; 0136 QString m_Uid; 0137 mutable GetInfoThread *m_InfoThread; 0138 }; 0139 /***************************** 0140 * Internal data class end * 0141 *****************************/ 0142 0143 SvnItemModel::SvnItemModel(MainTreeWidget *display, QObject *parent) 0144 : QAbstractItemModel(parent) 0145 , m_Data(new SvnItemModelData(this, display)) 0146 { 0147 m_Data->m_SvnActions = new SvnActions(display); 0148 m_Data->m_rootNode = new SvnItemModelNodeDir(m_Data->m_SvnActions, display); 0149 } 0150 0151 SvnItemModel::~SvnItemModel() 0152 { 0153 } 0154 0155 SvnItemModelNode *SvnItemModel::firstRootChild() 0156 { 0157 if (!m_Data->m_rootNode) { 0158 return nullptr; 0159 } 0160 return m_Data->m_rootNode->child(0); 0161 } 0162 0163 QModelIndex SvnItemModel::firstRootIndex() 0164 { 0165 return m_Data->indexForNode(firstRootChild()); 0166 } 0167 0168 SvnItemModelNode *SvnItemModel::nodeForIndex(const QModelIndex &index) 0169 { 0170 return m_Data->nodeForIndex(index); 0171 } 0172 0173 void SvnItemModel::setRootNodeStat(const svn::StatusPtr &stat) 0174 { 0175 m_Data->m_rootNode->setStat(stat); 0176 } 0177 0178 void SvnItemModel::clear() 0179 { 0180 int numRows = m_Data->m_rootNode->childList().count(); 0181 if (numRows > 0) 0182 beginRemoveRows(QModelIndex(), 0, numRows - 1); 0183 m_Data->clear(); 0184 if (numRows > 0) 0185 endRemoveRows(); 0186 } 0187 0188 void SvnItemModel::beginRemoveRows(const QModelIndex &parent, int first, int last) 0189 { 0190 m_Data->m_InfoThread->clearNodes(); 0191 m_Data->m_InfoThread->cancelMe(); 0192 if (!m_Data->m_InfoThread->wait(1000)) { } 0193 QAbstractItemModel::beginRemoveRows(parent, first, last); 0194 } 0195 0196 void SvnItemModel::clearNodeDir(SvnItemModelNodeDir *node) 0197 { 0198 QModelIndex ind = m_Data->indexForNode(node); 0199 if (!node) { 0200 node = m_Data->m_rootNode; 0201 } 0202 int numRows = node->childList().size(); 0203 beginRemoveRows(ind, 0, numRows); 0204 node->clear(); 0205 endRemoveRows(); 0206 } 0207 0208 bool SvnItemModel::hasChildren(const QModelIndex &parent) const 0209 { 0210 if (!parent.isValid()) { 0211 return true; 0212 } 0213 return static_cast<SvnItemModelNode *>(parent.internalPointer())->NodeHasChilds(); 0214 } 0215 0216 bool SvnItemModel::filterIndex(const QModelIndex &parent, int childRow, svnmodel::ItemTypeFlag showOnly) const 0217 { 0218 SvnItemModelNode *node = m_Data->nodeForIndex(parent); 0219 if (childRow < 0) { 0220 return false; 0221 } 0222 if (!node->NodeIsDir()) { 0223 qCDebug(KDESVN_LOG) << "Parent ist kein Dir" << Qt::endl; 0224 return false; 0225 } 0226 SvnItemModelNode *child = static_cast<SvnItemModelNodeDir *>(node)->child(childRow); 0227 if (child) { 0228 if ((child->isDir() && !showOnly.testFlag(svnmodel::Dir)) || (!child->isDir() && !showOnly.testFlag(svnmodel::File))) { 0229 return true; 0230 } 0231 return ItemDisplay::filterOut(child); 0232 } 0233 return false; 0234 } 0235 0236 QVariant SvnItemModel::data(const QModelIndex &index, int role) const 0237 { 0238 SvnItemModelNode *node = m_Data->nodeForIndex(index); 0239 switch (role) { 0240 case Qt::DisplayRole: 0241 case SORT_ROLE: 0242 switch (index.column()) { 0243 case Name: 0244 return node->shortName(); 0245 case Status: 0246 return node->infoText(); 0247 case LastRevision: 0248 return QString::number(node->cmtRev()); 0249 case LastAuthor: 0250 return node->cmtAuthor(); 0251 case LastDate: 0252 return node->fullDate(); 0253 case Locked: 0254 return node->lockOwner(); 0255 } 0256 break; 0257 case Qt::DecorationRole: 0258 if (index.column() == 0) { 0259 int size = Kdesvnsettings::listview_icon_size(); 0260 bool overlay = Kdesvnsettings::display_overlays(); 0261 return node->getPixmap(size, overlay); 0262 } 0263 break; 0264 case Qt::EditRole: 0265 switch (index.column()) { 0266 case Name: 0267 return node->shortName(); 0268 } 0269 break; 0270 case Qt::BackgroundRole: { 0271 QColor cl = node->backgroundColor(); 0272 if (cl.isValid()) { 0273 return QBrush(cl); 0274 } 0275 break; 0276 } 0277 case Qt::ToolTipRole: { 0278 switch (index.column()) { 0279 case Name: 0280 if (node->hasToolTipText()) { 0281 return node->getToolTipText(); 0282 } else { 0283 m_Data->m_InfoThread->appendNode(node); 0284 return QVariant(); 0285 } 0286 } 0287 break; 0288 } 0289 } 0290 return QVariant(); 0291 } 0292 0293 QModelIndex SvnItemModel::index(int row, int column, const QModelIndex &parent) const 0294 { 0295 SvnItemModelNode *node = m_Data->nodeForIndex(parent); 0296 if (row < 0) { 0297 return QModelIndex(); 0298 } 0299 Q_ASSERT(node->NodeIsDir()); 0300 SvnItemModelNode *child = static_cast<SvnItemModelNodeDir *>(node)->child(row); 0301 if (child) { 0302 return createIndex(row, column, child); 0303 } else { 0304 return QModelIndex(); 0305 } 0306 } 0307 0308 QVariant SvnItemModel::headerData(int section, Qt::Orientation orientation, int role) const 0309 { 0310 if (orientation == Qt::Vertical) { 0311 return QVariant(); 0312 } 0313 switch (role) { 0314 case Qt::DisplayRole: 0315 switch (section) { 0316 case Name: 0317 return (i18n("Name")); 0318 case Status: 0319 return (i18n("Status")); 0320 case LastRevision: 0321 return (i18n("Last changed Revision")); 0322 case LastAuthor: 0323 return (i18n("Last author")); 0324 case LastDate: 0325 return (i18n("Last change date")); 0326 case Locked: 0327 return (i18n("Locked by")); 0328 } 0329 } 0330 return QVariant(); 0331 } 0332 0333 int SvnItemModel::columnCount(const QModelIndex & /*parent*/) const 0334 { 0335 return ColumnCount; 0336 } 0337 0338 int SvnItemModel::rowCount(const QModelIndex &parent) const 0339 { 0340 if (!m_Data || !m_Data->m_rootNode) { 0341 return 0; 0342 } 0343 0344 if (!parent.isValid()) { 0345 return m_Data->m_rootNode->childList().count(); 0346 } 0347 SvnItemModelNodeDir *node = static_cast<SvnItemModelNodeDir *>(m_Data->nodeForIndex(parent)); 0348 return node->childList().count(); 0349 } 0350 0351 QModelIndex SvnItemModel::parent(const QModelIndex &index) const 0352 { 0353 if (!index.isValid()) { 0354 return QModelIndex(); 0355 } 0356 SvnItemModelNode *child = static_cast<SvnItemModelNode *>(index.internalPointer()); 0357 return m_Data->indexForNode(child->parent()); 0358 } 0359 0360 SvnActions *SvnItemModel::svnWrapper() 0361 { 0362 return m_Data->m_SvnActions; 0363 } 0364 0365 int SvnItemModel::checkDirs(const QString &_what, SvnItemModelNode *_parent) 0366 { 0367 QString what = _what; 0368 svn::StatusEntries dlist; 0369 while (what.endsWith(QLatin1Char('/'))) { 0370 what.chop(1); 0371 } 0372 // prevent this from checking unversioned folder. FIXME: what happen when we do open url on a non-working-copy folder?? 0373 #ifdef DEBUG_TIMER 0374 QTime _counttime; 0375 _counttime.start(); 0376 #endif 0377 0378 if (!m_Data->m_Display->isWorkingCopy() || (!_parent) || ((_parent) && (_parent->isVersioned()))) { 0379 if (!svnWrapper()->makeStatus(what, dlist, m_Data->m_Display->baseRevision(), false, true, true)) { 0380 return -1; 0381 } 0382 } else { 0383 return checkUnversionedDirs(_parent); 0384 } 0385 #ifdef DEBUG_TIMER 0386 qCDebug(KDESVN_LOG) << "Time for getting entries: " << _counttime.elapsed(); 0387 _counttime.restart(); 0388 #endif 0389 svn::StatusEntries neweritems; 0390 svnWrapper()->getaddedItems(what, neweritems); 0391 dlist += neweritems; 0392 SvnItemModelNode *node = nullptr; 0393 for (auto it = dlist.begin(); it != dlist.end(); ++it) { 0394 const svn::StatusPtr &sp = *it; 0395 if (sp->path() == what || sp->entry().url().toString() == what) { 0396 if (!_parent) { 0397 // toplevel item 0398 beginInsertRows(m_Data->indexForNode(m_Data->m_rootNode), 0, 0); 0399 if (sp->entry().kind() == svn_node_dir) { 0400 node = new SvnItemModelNodeDir(m_Data->m_rootNode, svnWrapper(), m_Data->m_Display); 0401 } else { 0402 node = new SvnItemModelNode(m_Data->m_rootNode, svnWrapper(), m_Data->m_Display); 0403 } 0404 node->setStat(sp); 0405 m_Data->m_rootNode->m_Children.prepend(node); 0406 endInsertRows(); 0407 } 0408 dlist.erase(it); 0409 break; 0410 } 0411 } 0412 if (_parent) { 0413 node = _parent; 0414 } 0415 #ifdef DEBUG_TIMER 0416 qCDebug(KDESVN_LOG) << "Time finding parent node: " << _counttime.elapsed(); 0417 #endif 0418 insertDirs(node, dlist); 0419 return dlist.size(); 0420 } 0421 0422 void SvnItemModel::insertDirs(SvnItemModelNode *_parent, svn::StatusEntries &dlist) 0423 { 0424 if (dlist.isEmpty()) { 0425 return; 0426 } 0427 QModelIndex ind = m_Data->indexForNode(_parent); 0428 SvnItemModelNodeDir *parent; 0429 if (!_parent) { 0430 parent = m_Data->m_rootNode; 0431 } else { 0432 parent = static_cast<SvnItemModelNodeDir *>(_parent); 0433 } 0434 SvnItemModelNode *node = nullptr; 0435 beginInsertRows(ind, parent->childList().count(), parent->childList().count() + dlist.count() - 1); 0436 #ifdef DEBUG_TIMER 0437 QTime _counttime; 0438 _counttime.start(); 0439 #endif 0440 for (const svn::StatusPtr &sp : dlist) { 0441 #ifdef DEBUG_TIMER 0442 _counttime.restart(); 0443 #endif 0444 if (m_Data->MustCreateDir(*sp)) { 0445 node = new SvnItemModelNodeDir(parent, svnWrapper(), m_Data->m_Display); 0446 } else { 0447 node = new SvnItemModelNode(parent, svnWrapper(), m_Data->m_Display); 0448 } 0449 node->setStat(sp); 0450 #ifdef DEBUG_TIMER 0451 // qCDebug(KDESVN_LOG)<<"Time creating item: "<<_counttime.elapsed(); 0452 _counttime.restart(); 0453 #endif 0454 if (m_Data->m_Display->isWorkingCopy() && m_Data->m_DirWatch) { 0455 if (node->isDir()) { 0456 m_Data->addWatchDir(node->fullName()); 0457 } else { 0458 m_Data->addWatchFile(node->fullName()); 0459 } 0460 } 0461 #ifdef DEBUG_TIMER 0462 // qCDebug(KDESVN_LOG)<<"Time add watch: "<<_counttime.elapsed(); 0463 _counttime.restart(); 0464 #endif 0465 parent->m_Children.append(node); 0466 #ifdef DEBUG_TIMER 0467 // qCDebug(KDESVN_LOG)<<"Time append node: "<<_counttime.elapsed(); 0468 #endif 0469 } 0470 #ifdef DEBUG_TIMER 0471 _counttime.restart(); 0472 #endif 0473 endInsertRows(); 0474 #ifdef DEBUG_TIMER 0475 // qCDebug(KDESVN_LOG)<<"Time append all node: "<<_counttime.elapsed(); 0476 #endif 0477 } 0478 0479 bool SvnItemModel::canFetchMore(const QModelIndex &parent) const 0480 { 0481 if (!parent.isValid()) { 0482 return false; 0483 } 0484 SvnItemModelNode *node = static_cast<SvnItemModelNode *>(parent.internalPointer()); 0485 return node->NodeHasChilds() && static_cast<SvnItemModelNodeDir *>(node)->childList().isEmpty(); 0486 } 0487 0488 void SvnItemModel::fetchMore(const QModelIndex &parent) 0489 { 0490 SvnItemModelNode *node = static_cast<SvnItemModelNode *>(parent.internalPointer()); 0491 if (!node->isDir()) { 0492 return; 0493 } 0494 if (checkDirs(node->fullName(), node) > 0) { 0495 emit itemsFetched(parent); 0496 } 0497 } 0498 0499 bool SvnItemModel::insertRows(int, int, const QModelIndex &) 0500 { 0501 return false; 0502 } 0503 0504 bool SvnItemModel::insertColumns(int, int, const QModelIndex &) 0505 { 0506 return false; 0507 } 0508 0509 bool SvnItemModel::removeRows(int, int, const QModelIndex &) 0510 { 0511 return false; 0512 } 0513 0514 bool SvnItemModel::removeColumns(int, int, const QModelIndex &) 0515 { 0516 return false; 0517 } 0518 Qt::ItemFlags SvnItemModel::flags(const QModelIndex &index) const 0519 { 0520 Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable; 0521 if (index.column() == Name) { 0522 f |= /*Qt::ItemIsEditable |*/ Qt::ItemIsDragEnabled; 0523 } 0524 if (!index.isValid()) { 0525 f |= Qt::ItemIsDropEnabled; 0526 } else { 0527 SvnItemModelNode *node = m_Data->nodeForIndex(index); 0528 if (node && node->isDir()) { 0529 f |= Qt::ItemIsDropEnabled; 0530 } 0531 } 0532 return f; 0533 } 0534 0535 Qt::DropActions SvnItemModel::supportedDropActions() const 0536 { 0537 return Qt::CopyAction | Qt::MoveAction; 0538 } 0539 0540 QStringList SvnItemModel::mimeTypes() const 0541 { 0542 return QStringList() << QLatin1String("text/uri-list") 0543 /* << QLatin1String( "application/x-kde-cutselection" ) */ // TODO 0544 //<< QLatin1String( "text/plain" ) 0545 << QLatin1String("application/x-kde-urilist"); 0546 } 0547 0548 bool SvnItemModel::dropUrls(const QList<QUrl> &data, Qt::DropAction action, int row, int column, const QModelIndex &parent, bool intern) 0549 { 0550 Q_UNUSED(row); 0551 Q_UNUSED(column); 0552 if (action == Qt::IgnoreAction) { 0553 return true; 0554 } 0555 if (action == Qt::LinkAction) { 0556 return false; 0557 } 0558 emit urlDropped(data, action, parent, intern); 0559 return true; 0560 } 0561 0562 QMimeData *SvnItemModel::mimeData(const QModelIndexList &indexes) const 0563 { 0564 QList<QUrl> urls; 0565 for (const QModelIndex &index : indexes) { 0566 if (index.column() == 0) { 0567 urls << m_Data->nodeForIndex(index)->kdeName(m_Data->m_Display->baseRevision()); 0568 } 0569 } 0570 QMimeData *mimeData = new QMimeData(); 0571 mimeData->setUrls(urls); 0572 0573 KUrlMimeData::MetaDataMap metaMap; 0574 metaMap[QStringLiteral("kdesvn-source")] = QLatin1Char('t'); 0575 metaMap[QStringLiteral("kdesvn-id")] = uniqueIdentifier(); 0576 KUrlMimeData::setMetaData(metaMap, mimeData); 0577 0578 return mimeData; 0579 } 0580 0581 void SvnItemModel::makeIgnore(const QModelIndex &index) 0582 { 0583 if (!index.isValid()) { 0584 return; 0585 } 0586 SvnItemModelNode *node = m_Data->nodeForIndex(index); 0587 if (!node || node == m_Data->m_rootNode || node->isRealVersioned()) { 0588 return; 0589 } 0590 SvnItemModelNodeDir *pa = node->parent(); 0591 if (!pa) { 0592 return; 0593 } 0594 if (m_Data->m_SvnActions->makeIgnoreEntry(node, node->isIgnored())) { 0595 refreshIndex(index); 0596 refreshItem(pa); 0597 } 0598 } 0599 0600 bool SvnItemModel::refreshItem(SvnItemModelNode *item) 0601 { 0602 if (!item || item == m_Data->m_rootNode) { 0603 return false; 0604 } 0605 try { 0606 item->setStat(m_Data->m_SvnActions->svnclient()->singleStatus(item->fullName(), false, m_Data->m_Display->baseRevision())); 0607 } catch (const svn::ClientException &e) { 0608 item->setStat(svn::StatusPtr(new svn::Status)); 0609 return false; 0610 } 0611 return true; 0612 } 0613 0614 bool SvnItemModel::refreshIndex(const QModelIndex &idx) 0615 { 0616 bool ret = refreshItem(m_Data->nodeForIndex(idx)); 0617 emitDataChangedRow(idx); 0618 return ret; 0619 } 0620 0621 void SvnItemModel::emitDataChangedRow(const QModelIndex &idx) 0622 { 0623 const auto colS(index(idx.row(), 0, idx.parent())); 0624 const auto colE(index(idx.row(), columnCount() - 1, idx.parent())); 0625 emit dataChanged(colS, colE); 0626 } 0627 0628 SvnItemModelNode *SvnItemModel::findPath(const svn::Path &_p) 0629 { 0630 QString ip = _p.path(); 0631 SvnItemModelNode *n1 = firstRootChild(); 0632 if (n1) { 0633 if (n1->fullName().length() < ip.length()) { 0634 ip = ip.right(ip.length() - n1->fullName().length()); 0635 } else if (n1->fullName() == ip) { 0636 return n1; 0637 } 0638 if (!n1->isDir()) { 0639 return nullptr; 0640 } 0641 const QVector<QStringRef> lp = ip.splitRef(QLatin1Char('/'), QString::SkipEmptyParts); 0642 SvnItemModelNodeDir *d1 = static_cast<SvnItemModelNodeDir *>(n1); 0643 return d1->findPath(lp); 0644 } 0645 return nullptr; 0646 } 0647 0648 QModelIndex SvnItemModel::findIndex(const svn::Path &_p) 0649 { 0650 return m_Data->indexForNode(findPath(_p)); 0651 } 0652 0653 void SvnItemModel::initDirWatch() 0654 { 0655 delete m_Data->m_DirWatch; 0656 m_Data->m_DirWatch = nullptr; 0657 if (m_Data->m_Display->isWorkingCopy()) { 0658 m_Data->m_DirWatch = new KDirWatch(this); 0659 connect(m_Data->m_DirWatch, &KDirWatch::dirty, this, &SvnItemModel::slotDirty); 0660 connect(m_Data->m_DirWatch, &KDirWatch::created, this, &SvnItemModel::slotCreated); 0661 connect(m_Data->m_DirWatch, &KDirWatch::deleted, this, &SvnItemModel::slotDeleted); 0662 if (m_Data->m_DirWatch) { 0663 m_Data->m_DirWatch->addDir(m_Data->m_Display->baseUri() + QLatin1Char('/'), KDirWatch::WatchDirOnly); 0664 m_Data->m_DirWatch->startScan(true); 0665 } 0666 } 0667 } 0668 0669 void SvnItemModel::slotCreated(const QString &what) 0670 { 0671 QModelIndex ind = findIndex(what); 0672 if (!ind.isValid()) { 0673 return; 0674 } 0675 SvnItemModelNode *n = static_cast<SvnItemModelNode *>(ind.internalPointer()); 0676 if (!n) { 0677 return; 0678 } 0679 if (n->isRealVersioned()) { 0680 refreshIndex(ind); 0681 } 0682 } 0683 0684 void SvnItemModel::slotDeleted(const QString &what) 0685 { 0686 QModelIndex ind = findIndex(what); 0687 if (!ind.isValid()) { 0688 m_Data->m_DirWatch->removeDir(what); 0689 m_Data->m_DirWatch->removeFile(what); 0690 return; 0691 } 0692 SvnItemModelNode *n = static_cast<SvnItemModelNode *>(ind.internalPointer()); 0693 if (!n) { 0694 return; 0695 } 0696 if (!n->isRealVersioned()) { 0697 SvnItemModelNodeDir *p = n->parent(); 0698 QModelIndex pi = m_Data->indexForNode(p); 0699 if (!pi.isValid()) { 0700 return; 0701 } 0702 if (ind.row() >= p->m_Children.count()) { 0703 return; 0704 } 0705 beginRemoveRows(pi, ind.row(), ind.row()); 0706 p->m_Children.removeAt(ind.row()); 0707 endRemoveRows(); 0708 if (n->isDir()) { 0709 m_Data->m_DirWatch->removeDir(what); 0710 } else { 0711 m_Data->m_DirWatch->removeFile(what); 0712 } 0713 } else { 0714 refreshIndex(ind); 0715 } 0716 } 0717 0718 void SvnItemModel::checkAddNewItems(const QModelIndex &ind) 0719 { 0720 SvnItemModelNodeDir *n = static_cast<SvnItemModelNodeDir *>(ind.internalPointer()); 0721 QString what = n->fullName(); 0722 svn::StatusEntries dlist; 0723 while (what.endsWith(QLatin1Char('/'))) { 0724 what.chop(1); 0725 } 0726 if (!svnWrapper()->makeStatus(what, dlist, m_Data->m_Display->baseRevision(), false, true, true)) { 0727 return; 0728 } 0729 const auto pred = [&](const svn::StatusPtr &sp) -> bool { 0730 return n->contains(sp->path()) || sp->path() == what; 0731 }; 0732 dlist.erase(std::remove_if(dlist.begin(), dlist.end(), pred), dlist.end()); 0733 if (!dlist.isEmpty()) { 0734 insertDirs(n, dlist); 0735 } 0736 } 0737 0738 void SvnItemModel::slotDirty(const QString &what) 0739 { 0740 QModelIndex ind = findIndex(what); 0741 if (!ind.isValid()) { 0742 return; 0743 } 0744 SvnItemModelNode *n = static_cast<SvnItemModelNode *>(ind.internalPointer()); 0745 if (!n) { 0746 return; 0747 } 0748 if (n->isRealVersioned()) { 0749 if (!n->isDir()) { 0750 refreshIndex(ind); 0751 } else { 0752 checkAddNewItems(ind); 0753 } 0754 } else if (n->isDir()) { 0755 checkUnversionedDirs(n); 0756 } 0757 } 0758 0759 bool SvnItemModel::checkRootNode() 0760 { 0761 if (!m_Data->m_rootNode) { 0762 return false; 0763 } 0764 try { 0765 m_Data->m_rootNode->setStat(m_Data->m_SvnActions->svnclient()->singleStatus(m_Data->m_Display->baseUri(), false, m_Data->m_Display->baseRevision())); 0766 } catch (const svn::ClientException &e) { 0767 m_Data->m_rootNode->setStat(svn::StatusPtr(new svn::Status)); 0768 emit clientException(e.msg()); 0769 return false; 0770 } 0771 return true; 0772 } 0773 0774 bool SvnItemModel::refreshCurrentTree() 0775 { 0776 bool check_created = false; 0777 if (!m_Data->m_rootNode) { 0778 return false; 0779 } 0780 SvnItemModelNodeDir *_start = m_Data->m_rootNode; 0781 if (m_Data->m_Display->isWorkingCopy()) { 0782 if (!m_Data->m_rootNode->m_Children.isEmpty() && m_Data->m_rootNode->m_Children.at(0)->NodeIsDir()) { 0783 _start = static_cast<SvnItemModelNodeDir *>(m_Data->m_rootNode->m_Children.at(0)); 0784 refreshItem(_start); 0785 } else { 0786 return false; 0787 } 0788 } else { 0789 if (!checkRootNode()) { 0790 return false; 0791 } 0792 _start = m_Data->m_rootNode; 0793 check_created = true; 0794 } 0795 return refreshDirnode(_start, check_created); 0796 } 0797 0798 bool SvnItemModel::refreshDirnode(SvnItemModelNodeDir *node, bool check_empty, bool notrec) 0799 { 0800 if (!node) { 0801 if (m_Data->m_Display->isWorkingCopy()) { 0802 return false; 0803 } else { 0804 if (!checkRootNode()) { 0805 return false; 0806 } 0807 node = m_Data->m_rootNode; 0808 } 0809 } 0810 QString what = (node != m_Data->m_rootNode) ? node->fullName() : m_Data->m_Display->baseUri(); 0811 0812 if (node->m_Children.isEmpty() && !check_empty) { 0813 if (node->fullName() == m_Data->m_Display->baseUri()) { 0814 return refreshItem(node); 0815 } 0816 return true; 0817 } 0818 svn::StatusEntries dlist; 0819 0820 if (!svnWrapper()->makeStatus(what, dlist, m_Data->m_Display->baseRevision())) { 0821 return false; 0822 } 0823 if (m_Data->m_Display->isWorkingCopy()) { 0824 svn::StatusEntries neweritems; 0825 svnWrapper()->getaddedItems(what, neweritems); 0826 dlist += neweritems; 0827 } 0828 0829 for (auto it = dlist.begin(); it != dlist.end(); ++it) { 0830 if ((*it)->path() == what) { 0831 dlist.erase(it); 0832 break; 0833 } 0834 } 0835 QModelIndex ind = m_Data->indexForNode(node); 0836 for (int i = 0; i < node->m_Children.size(); ++i) { 0837 const SvnItemModelNode *n = node->m_Children[i]; 0838 bool found = false; 0839 for (const auto &entry : qAsConst(dlist)) { 0840 if (entry->path() == n->fullName()) { 0841 found = true; 0842 break; 0843 } 0844 } 0845 if (!found) { 0846 beginRemoveRows(ind, i, i); 0847 node->m_Children.removeAt(i); 0848 delete n; 0849 endRemoveRows(); 0850 --i; 0851 } 0852 } 0853 0854 for (auto it = dlist.begin(); it != dlist.end();) { 0855 int index = node->indexOf((*it)->path()); 0856 if (index != -1) { 0857 SvnItemModelNode *n = node->m_Children[index]; 0858 n->setStat((*it)); 0859 if (n->NodeIsDir() != n->isDir()) { 0860 beginRemoveRows(ind, index, index); 0861 node->m_Children.removeAt(index); 0862 delete n; 0863 endRemoveRows(); 0864 } else { 0865 it = dlist.erase(it); 0866 } 0867 } else { 0868 ++it; 0869 } 0870 } 0871 0872 // make sure that we do not read in the whole tree when just refreshing the current tree. 0873 if (!node->m_Children.isEmpty() && !notrec) { 0874 for (auto &child : node->m_Children) { 0875 if (child->NodeIsDir()) { 0876 // both other parameters makes no sense at this point - defaults 0877 refreshDirnode(static_cast<SvnItemModelNodeDir *>(child), false, false); 0878 } 0879 } 0880 } 0881 // after so we don't recurse about it. 0882 insertDirs(node, dlist); 0883 if (!dlist.isEmpty()) { 0884 emit itemsFetched(m_Data->indexForNode(node)); 0885 } 0886 return true; 0887 } 0888 0889 int SvnItemModel::checkUnversionedDirs(SvnItemModelNode *_parent) 0890 { 0891 if (!_parent || !_parent->isDir()) { 0892 // no toplevel unversioned - kdesvn is not a filemanager 0893 return 0; 0894 } 0895 QDir d(_parent->fullName()); 0896 d.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); 0897 QFileInfoList list = d.entryInfoList(); 0898 if (list.isEmpty()) { 0899 return 0; 0900 } 0901 svn::StatusEntries dlist; 0902 SvnItemModelNodeDir *n = static_cast<SvnItemModelNodeDir *>(_parent); 0903 for (const auto &fi : list) { 0904 if (!(n->contains(fi.absoluteFilePath()) || fi.absoluteFilePath() == n->fullName())) { 0905 svn::StatusPtr stat(new svn::Status(fi.absoluteFilePath())); 0906 dlist.append(stat); 0907 } 0908 } 0909 if (!dlist.isEmpty()) { 0910 insertDirs(_parent, dlist); 0911 } 0912 return dlist.size(); 0913 } 0914 0915 const QString &SvnItemModel::uniqueIdentifier() const 0916 { 0917 return m_Data->m_Uid; 0918 } 0919 0920 void SvnItemModel::slotNotifyMessage(const QString &msg) 0921 { 0922 qCDebug(KDESVN_LOG) << msg; 0923 } 0924 0925 #include "moc_svnitemmodel.cpp"