File indexing completed on 2024-04-28 04:37:03
0001 /* 0002 SPDX-FileCopyrightText: 2010-2012 Milian Wolff <mail@milianw.de> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "abstractfilemanagerplugin.h" 0008 0009 #include "filemanagerlistjob.h" 0010 #include "projectmodel.h" 0011 #include "helper.h" 0012 0013 #include <QHashIterator> 0014 #include <QFileInfo> 0015 #include <QApplication> 0016 #include <QTimer> 0017 #ifdef TIME_IMPORT_JOB 0018 #include <QElapsedTimer> 0019 #endif 0020 0021 #include <KMessageBox> 0022 #include <KLocalizedString> 0023 #include <KDirWatch> 0024 0025 #include <interfaces/iproject.h> 0026 #include <interfaces/icore.h> 0027 #include <interfaces/iprojectcontroller.h> 0028 #include <serialization/indexedstring.h> 0029 0030 #include "projectfiltermanager.h" 0031 #include "debug.h" 0032 0033 #define ifDebug(x) 0034 0035 using namespace KDevelop; 0036 0037 //BEGIN Helper 0038 0039 namespace { 0040 0041 /** 0042 * Returns the parent folder item for a given item or the project root item if there is no parent. 0043 */ 0044 ProjectFolderItem* parentFolder(ProjectBaseItem* item) 0045 { 0046 if ( item->parent() ) { 0047 return static_cast<ProjectFolderItem*>(item->parent()); 0048 } else { 0049 return item->project()->projectItem(); 0050 } 0051 } 0052 0053 } 0054 0055 //END Helper 0056 0057 //BEGIN Private 0058 0059 class KDevelop::AbstractFileManagerPluginPrivate 0060 { 0061 public: 0062 explicit AbstractFileManagerPluginPrivate(AbstractFileManagerPlugin* qq) 0063 : q(qq) 0064 { 0065 } 0066 0067 AbstractFileManagerPlugin* q; 0068 0069 /** 0070 * The just returned must be started in one way or another for this method 0071 * to have any affect. The job will then auto-delete itself upon completion. 0072 */ 0073 [[nodiscard]] KJob* eventuallyReadFolder(ProjectFolderItem* item); 0074 void addJobItems(FileManagerListJob* job, 0075 ProjectFolderItem* baseItem, 0076 const KIO::UDSEntryList& entries); 0077 0078 void deleted(const QString &path); 0079 void created(const QString &path); 0080 0081 void projectClosing(IProject* project); 0082 void jobFinished(KJob* job); 0083 0084 /// Stops watching the given folder for changes, only useful for local files. 0085 void stopWatcher(ProjectFolderItem* folder); 0086 /// Continues watching the given folder for changes. 0087 void continueWatcher(ProjectFolderItem* folder); 0088 /// Common renaming function. 0089 bool rename(ProjectBaseItem* item, const Path& newPath); 0090 0091 QHash<IProject*, KDirWatch*> m_watchers; 0092 QHash<IProject*, QList<FileManagerListJob*> > m_projectJobs; 0093 QVector<QString> m_stoppedFolders; 0094 ProjectFilterManager m_filters; 0095 }; 0096 0097 void AbstractFileManagerPluginPrivate::projectClosing(IProject* project) 0098 { 0099 const auto projectJobIt = m_projectJobs.constFind(project); 0100 if (projectJobIt != m_projectJobs.constEnd()) { 0101 // make sure the import job does not live longer than the project 0102 // see also addLotsOfFiles test 0103 for (FileManagerListJob* job : *projectJobIt) { 0104 qCDebug(FILEMANAGER) << "killing project job:" << job; 0105 job->kill(); 0106 } 0107 m_projectJobs.remove(project); 0108 } 0109 #ifdef TIME_IMPORT_JOB 0110 QElapsedTimer timer; 0111 if (m_watchers.contains(project)) { 0112 timer.start(); 0113 } 0114 #endif 0115 delete m_watchers.take(project); 0116 #ifdef TIME_IMPORT_JOB 0117 if (timer.isValid()) { 0118 qCDebug(FILEMANAGER) << "Deleting dir watcher took" << timer.elapsed() / 1000.0 << "seconds for project" << project->name(); 0119 } 0120 #endif 0121 m_filters.remove(project); 0122 } 0123 0124 KJob* AbstractFileManagerPluginPrivate::eventuallyReadFolder(ProjectFolderItem* item) 0125 { 0126 auto* listJob = new FileManagerListJob( item ); 0127 m_projectJobs[ item->project() ] << listJob; 0128 qCDebug(FILEMANAGER) << "adding job" << listJob << item << item->path() << "for project" << item->project(); 0129 0130 q->connect( listJob, &FileManagerListJob::finished, 0131 q, [&] (KJob* job) { jobFinished(job); } ); 0132 0133 q->connect( listJob, &FileManagerListJob::entries, 0134 q, [&] (FileManagerListJob* job, ProjectFolderItem* baseItem, const KIO::UDSEntryList& entries) { 0135 addJobItems(job, baseItem, entries); } ); 0136 0137 return listJob; 0138 } 0139 0140 void AbstractFileManagerPluginPrivate::jobFinished(KJob* job) 0141 { 0142 // ensure we don't keep a dangling point in our list 0143 // NOTE: job is potentially emitting its finished signal from its destructor 0144 // or the item that was used internally may have been deleted already 0145 for (auto& jobs : m_projectJobs) { 0146 if (jobs.removeOne(reinterpret_cast<FileManagerListJob*>(job))) { 0147 break; 0148 } 0149 } 0150 } 0151 0152 void AbstractFileManagerPluginPrivate::addJobItems(FileManagerListJob* job, 0153 ProjectFolderItem* baseItem, 0154 const KIO::UDSEntryList& entries) 0155 { 0156 qCDebug(FILEMANAGER) << "reading entries of" << baseItem->path(); 0157 0158 // build lists of valid files and folders with paths relative to the project folder 0159 Path::List files; 0160 Path::List folders; 0161 for (const KIO::UDSEntry& entry : entries) { 0162 QString name = entry.stringValue( KIO::UDSEntry::UDS_NAME ); 0163 if (name == QLatin1String(".") || name == QLatin1String("..")) { 0164 continue; 0165 } 0166 0167 Path path(baseItem->path(), name); 0168 0169 if ( !q->isValid( path, entry.isDir(), baseItem->project() ) ) { 0170 continue; 0171 } else { 0172 if ( entry.isDir() ) { 0173 if( entry.isLink() ) { 0174 const Path linkedPath = baseItem->path().cd(entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST )); 0175 // make sure we don't end in an infinite loop 0176 if( linkedPath.isParentOf( baseItem->project()->path() ) || 0177 baseItem->project()->path().isParentOf( linkedPath ) || 0178 linkedPath == baseItem->project()->path() ) 0179 { 0180 continue; 0181 } 0182 } 0183 folders << path; 0184 } else { 0185 files << path; 0186 } 0187 } 0188 } 0189 0190 ifDebug(qCDebug(FILEMANAGER) << "valid folders:" << folders;) 0191 ifDebug(qCDebug(FILEMANAGER) << "valid files:" << files;) 0192 0193 // remove obsolete rows 0194 for ( int j = 0; j < baseItem->rowCount(); ++j ) { 0195 if ( ProjectFolderItem* f = baseItem->child(j)->folder() ) { 0196 // check if this is still a valid folder 0197 int index = folders.indexOf( f->path() ); 0198 if ( index == -1 ) { 0199 // folder got removed or is now invalid 0200 delete f; 0201 --j; 0202 } else { 0203 // this folder already exists in the view 0204 folders.remove( index ); 0205 // no need to add this item, but we still want to recurse into it 0206 job->addSubDir( f ); 0207 emit q->reloadedFolderItem( f ); 0208 } 0209 } else if ( ProjectFileItem* f = baseItem->child(j)->file() ) { 0210 // check if this is still a valid file 0211 int index = files.indexOf( f->path() ); 0212 if ( index == -1 ) { 0213 // file got removed or is now invalid 0214 ifDebug(qCDebug(FILEMANAGER) << "removing file:" << f << f->path();) 0215 delete f; 0216 --j; 0217 } else { 0218 // this file already exists in the view 0219 files.remove( index ); 0220 emit q->reloadedFileItem( f ); 0221 } 0222 } 0223 } 0224 0225 // add new rows 0226 for (const Path& path : qAsConst(files)) { 0227 ProjectFileItem* file = q->createFileItem( baseItem->project(), path, baseItem ); 0228 if (file) { 0229 emit q->fileAdded( file ); 0230 } 0231 } 0232 for (const Path& path : qAsConst(folders)) { 0233 ProjectFolderItem* folder = q->createFolderItem( baseItem->project(), path, baseItem ); 0234 if (folder) { 0235 emit q->folderAdded( folder ); 0236 job->addSubDir( folder ); 0237 } 0238 } 0239 } 0240 0241 void AbstractFileManagerPluginPrivate::created(const QString& path_) 0242 { 0243 qCDebug(FILEMANAGER) << "created:" << path_; 0244 QFileInfo info(path_); 0245 if (!info.exists()) { 0246 // we delay handling of the signal, so maybe the path actually got removed again 0247 return; 0248 } 0249 0250 ///FIXME: share memory with parent 0251 const Path path(path_); 0252 const IndexedString indexedPath(path.pathOrUrl()); 0253 const IndexedString indexedParent(path.parent().pathOrUrl()); 0254 0255 QHashIterator<IProject*, KDirWatch*> it(m_watchers); 0256 while (it.hasNext()) { 0257 const auto p = it.next().key(); 0258 if ( !p->projectItem()->model() ) { 0259 // not yet finished with loading 0260 // FIXME: how should this be handled? see unit test 0261 continue; 0262 } 0263 if ( !q->isValid(path, info.isDir(), p) ) { 0264 continue; 0265 } 0266 if ( info.isDir() ) { 0267 bool found = false; 0268 const auto folderItems = p->foldersForPath(indexedPath); 0269 for (ProjectFolderItem* folder : folderItems) { 0270 // exists already in this project, happens e.g. when we restart the dirwatcher 0271 // or if we delete and remove folders consecutively https://bugs.kde.org/show_bug.cgi?id=260741 0272 qCDebug(FILEMANAGER) << "force reload of" << path << folder; 0273 auto job = eventuallyReadFolder( folder ); 0274 job->start(); 0275 found = true; 0276 } 0277 if ( found ) { 0278 continue; 0279 } 0280 } else if (!p->filesForPath(indexedPath).isEmpty()) { 0281 // also gets triggered for kate's backup files 0282 continue; 0283 } 0284 const auto parentItems = p->foldersForPath(indexedParent); 0285 for (ProjectFolderItem* parentItem : parentItems) { 0286 if ( info.isDir() ) { 0287 ProjectFolderItem* folder = q->createFolderItem( p, path, parentItem ); 0288 if (folder) { 0289 emit q->folderAdded( folder ); 0290 auto job = eventuallyReadFolder( folder ); 0291 job->start(); 0292 } 0293 } else { 0294 ProjectFileItem* file = q->createFileItem( p, path, parentItem ); 0295 if (file) { 0296 emit q->fileAdded( file ); 0297 } 0298 } 0299 } 0300 } 0301 } 0302 0303 void AbstractFileManagerPluginPrivate::deleted(const QString& path_) 0304 { 0305 if ( QFile::exists(path_) ) { 0306 // we delay handling of the signal, so maybe the path actually exists again 0307 return; 0308 } 0309 // ensure that the path is not inside a stopped folder 0310 for (const QString& folder : qAsConst(m_stoppedFolders)) { 0311 if (path_.startsWith(folder)) { 0312 return; 0313 } 0314 } 0315 qCDebug(FILEMANAGER) << "deleted:" << path_; 0316 0317 const Path path(QUrl::fromLocalFile(path_)); 0318 const IndexedString indexed(path.pathOrUrl()); 0319 0320 QHashIterator<IProject*, KDirWatch*> it(m_watchers); 0321 while (it.hasNext()) { 0322 const auto p = it.next().key(); 0323 if (path == p->path()) { 0324 KMessageBox::error(qApp->activeWindow(), 0325 i18n("The base folder of project <b>%1</b>" 0326 " got deleted or moved outside of KDevelop.\n" 0327 "The project has to be closed.", p->name()), 0328 i18nc("@title:window", "Project Folder Deleted") ); 0329 ICore::self()->projectController()->closeProject(p); 0330 continue; 0331 } 0332 if ( !p->projectItem()->model() ) { 0333 // not yet finished with loading 0334 // FIXME: how should this be handled? see unit test 0335 continue; 0336 } 0337 const auto folderItems = p->foldersForPath(indexed); 0338 for (ProjectFolderItem* item : folderItems) { 0339 delete item; 0340 } 0341 const auto fileItems = p->filesForPath(indexed); 0342 for (ProjectFileItem* item : fileItems) { 0343 emit q->fileRemoved(item); 0344 ifDebug(qCDebug(FILEMANAGER) << "removing file" << item;) 0345 delete item; 0346 } 0347 } 0348 } 0349 0350 bool AbstractFileManagerPluginPrivate::rename(ProjectBaseItem* item, const Path& newPath) 0351 { 0352 if ( !q->isValid(newPath, true, item->project()) ) { 0353 int cancel = KMessageBox::warningContinueCancel( qApp->activeWindow(), 0354 i18n("You tried to rename '%1' to '%2', but the latter is filtered and will be hidden.\n" 0355 "Do you want to continue?", item->text(), newPath.lastPathSegment()), 0356 QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QStringLiteral("GenericManagerRenameToFiltered") 0357 ); 0358 if ( cancel == KMessageBox::Cancel ) { 0359 return false; 0360 } 0361 } 0362 const auto parentItems = item->project()->foldersForPath(IndexedString(newPath.parent().pathOrUrl())); 0363 for (ProjectFolderItem* parent : parentItems) { 0364 if ( parent->folder() ) { 0365 stopWatcher(parent); 0366 const Path source = item->path(); 0367 bool success = renameUrl( item->project(), source.toUrl(), newPath.toUrl() ); 0368 if ( success ) { 0369 item->setPath( newPath ); 0370 item->parent()->takeRow( item->row() ); 0371 parent->appendRow( item ); 0372 if (item->file()) { 0373 emit q->fileRenamed(source, item->file()); 0374 } else { 0375 Q_ASSERT(item->folder()); 0376 emit q->folderRenamed(source, item->folder()); 0377 } 0378 } 0379 continueWatcher(parent); 0380 return success; 0381 } 0382 } 0383 return false; 0384 } 0385 0386 void AbstractFileManagerPluginPrivate::stopWatcher(ProjectFolderItem* folder) 0387 { 0388 if ( !folder->path().isLocalFile() ) { 0389 return; 0390 } 0391 Q_ASSERT(m_watchers.contains(folder->project())); 0392 const QString path = folder->path().toLocalFile(); 0393 m_watchers[folder->project()]->stopDirScan(path); 0394 m_stoppedFolders.append(path); 0395 } 0396 0397 void AbstractFileManagerPluginPrivate::continueWatcher(ProjectFolderItem* folder) 0398 { 0399 if ( !folder->path().isLocalFile() ) { 0400 return; 0401 } 0402 auto watcher = m_watchers.value(folder->project(), nullptr); 0403 Q_ASSERT(watcher); 0404 const QString path = folder->path().toLocalFile(); 0405 if (!watcher->restartDirScan(path)) { 0406 // path wasn't being watched yet - can we be 100% certain of that will never happen? 0407 qCWarning(FILEMANAGER) << "Folder" << path << "in project" << folder->project()->name() << "wasn't yet being watched"; 0408 watcher->addDir(path); 0409 } 0410 const int idx = m_stoppedFolders.indexOf(path); 0411 if (idx != -1) { 0412 m_stoppedFolders.remove(idx); 0413 } 0414 } 0415 //END Private 0416 0417 //BEGIN Plugin 0418 0419 AbstractFileManagerPlugin::AbstractFileManagerPlugin( const QString& componentName, 0420 QObject *parent, 0421 const QVariantList & /*args*/ ) 0422 : IProjectFileManager(), 0423 IPlugin( componentName, parent ), 0424 d_ptr(new AbstractFileManagerPluginPrivate(this)) 0425 { 0426 connect(core()->projectController(), &IProjectController::projectClosing, 0427 this, [this] (IProject* project) { Q_D(AbstractFileManagerPlugin); d->projectClosing(project); }); 0428 connect(core()->projectController()->projectModel(), &ProjectModel::rowsAboutToBeRemoved, 0429 this, [this] (const QModelIndex& parent, int first, int last) { 0430 Q_D(AbstractFileManagerPlugin); 0431 // cleanup list jobs to remove about-to-be-dangling pointers 0432 auto* model = core()->projectController()->projectModel(); 0433 for (int i = first; i <= last; ++i) { 0434 const auto index = model->index(i, 0, parent); 0435 auto* item = index.data(ProjectModel::ProjectItemRole).value<ProjectBaseItem*>(); 0436 Q_ASSERT(item); 0437 for (auto* job : d->m_projectJobs.value(item->project())) { 0438 job->handleRemovedItem(item); 0439 } 0440 } 0441 }); 0442 } 0443 0444 AbstractFileManagerPlugin::~AbstractFileManagerPlugin() = default; 0445 0446 IProjectFileManager::Features AbstractFileManagerPlugin::features() const 0447 { 0448 return Features( Folders | Files ); 0449 } 0450 0451 QList<ProjectFolderItem*> AbstractFileManagerPlugin::parse( ProjectFolderItem *item ) 0452 { 0453 // we are async, can't return anything here 0454 qCDebug(FILEMANAGER) << "note: parse will always return an empty list"; 0455 Q_UNUSED(item); 0456 return QList<ProjectFolderItem*>(); 0457 } 0458 0459 ProjectFolderItem *AbstractFileManagerPlugin::import( IProject *project ) 0460 { 0461 Q_D(AbstractFileManagerPlugin); 0462 0463 ProjectFolderItem *projectRoot = createFolderItem( project, project->path(), nullptr ); 0464 emit folderAdded( projectRoot ); 0465 qCDebug(FILEMANAGER) << "imported new project" << project->name() << "at" << projectRoot->path(); 0466 0467 ///TODO: check if this works for remote files when something gets changed through another KDE app 0468 if ( project->path().isLocalFile() ) { 0469 auto watcher = new KDirWatch( project ); 0470 0471 // set up the signal handling 0472 // NOTE: We delay handling of the creation/deletion events here by one second to prevent 0473 // useless or even outright wrong handling of events during common git workflows. 0474 // I.e. sometimes we used to get a 'delete' event during a rebase which was never 0475 // followed up by a 'created' signal, even though the file actually exists after 0476 // the rebase. 0477 // see also: https://bugs.kde.org/show_bug.cgi?id=404184 0478 connect(watcher, &KDirWatch::created, 0479 this, [this] (const QString& path) { 0480 QTimer::singleShot(1000, this, [this, path]() { 0481 Q_D(AbstractFileManagerPlugin); 0482 d->created(path); 0483 }); 0484 }); 0485 connect(watcher, &KDirWatch::deleted, 0486 this, [this] (const QString& path) { 0487 QTimer::singleShot(1000, this, [this, path]() { 0488 Q_D(AbstractFileManagerPlugin); 0489 d->deleted(path); 0490 }); 0491 }); 0492 watcher->addDir(project->path().toLocalFile(), KDirWatch::WatchSubDirs | KDirWatch:: WatchFiles ); 0493 d->m_watchers[project] = watcher; 0494 } 0495 0496 d->m_filters.add(project); 0497 0498 return projectRoot; 0499 } 0500 0501 KJob* AbstractFileManagerPlugin::createImportJob(ProjectFolderItem* item) 0502 { 0503 Q_D(AbstractFileManagerPlugin); 0504 0505 return d->eventuallyReadFolder(item); 0506 } 0507 0508 bool AbstractFileManagerPlugin::reload( ProjectFolderItem* item ) 0509 { 0510 Q_D(AbstractFileManagerPlugin); 0511 0512 qCDebug(FILEMANAGER) << "reloading item" << item->path(); 0513 auto job = d->eventuallyReadFolder( item->folder() ); 0514 job->start(); 0515 return true; 0516 } 0517 0518 ProjectFolderItem* AbstractFileManagerPlugin::addFolder( const Path& folder, 0519 ProjectFolderItem * parent ) 0520 { 0521 Q_D(AbstractFileManagerPlugin); 0522 0523 qCDebug(FILEMANAGER) << "adding folder" << folder << "to" << parent->path(); 0524 ProjectFolderItem* created = nullptr; 0525 d->stopWatcher(parent); 0526 if ( createFolder(folder.toUrl()) ) { 0527 created = createFolderItem( parent->project(), folder, parent ); 0528 if (created) { 0529 emit folderAdded(created); 0530 } 0531 } 0532 d->continueWatcher(parent); 0533 return created; 0534 } 0535 0536 0537 ProjectFileItem* AbstractFileManagerPlugin::addFile( const Path& file, 0538 ProjectFolderItem * parent ) 0539 { 0540 Q_D(AbstractFileManagerPlugin); 0541 0542 qCDebug(FILEMANAGER) << "adding file" << file << "to" << parent->path(); 0543 ProjectFileItem* created = nullptr; 0544 d->stopWatcher(parent); 0545 if ( createFile(file.toUrl()) ) { 0546 created = createFileItem( parent->project(), file, parent ); 0547 if (created) { 0548 emit fileAdded(created); 0549 } 0550 } 0551 d->continueWatcher(parent); 0552 return created; 0553 } 0554 0555 bool AbstractFileManagerPlugin::renameFolder(ProjectFolderItem* folder, const Path& newPath) 0556 { 0557 Q_D(AbstractFileManagerPlugin); 0558 0559 qCDebug(FILEMANAGER) << "trying to rename a folder:" << folder->path() << newPath; 0560 return d->rename(folder, newPath); 0561 } 0562 0563 bool AbstractFileManagerPlugin::renameFile(ProjectFileItem* file, const Path& newPath) 0564 { 0565 Q_D(AbstractFileManagerPlugin); 0566 0567 qCDebug(FILEMANAGER) << "trying to rename a file:" << file->path() << newPath; 0568 return d->rename(file, newPath); 0569 } 0570 0571 bool AbstractFileManagerPlugin::removeFilesAndFolders(const QList<ProjectBaseItem*> &items) 0572 { 0573 Q_D(AbstractFileManagerPlugin); 0574 0575 bool success = true; 0576 for (ProjectBaseItem* item : items) { 0577 Q_ASSERT(item->folder() || item->file()); 0578 0579 ProjectFolderItem* parent = parentFolder(item); 0580 d->stopWatcher(parent); 0581 0582 success &= removeUrl(parent->project(), item->path().toUrl(), true); 0583 if ( success ) { 0584 if (item->file()) { 0585 emit fileRemoved(item->file()); 0586 } else { 0587 Q_ASSERT(item->folder()); 0588 emit folderRemoved(item->folder()); 0589 } 0590 delete item; 0591 } 0592 0593 d->continueWatcher(parent); 0594 if ( !success ) 0595 break; 0596 } 0597 return success; 0598 } 0599 0600 bool AbstractFileManagerPlugin::moveFilesAndFolders(const QList< ProjectBaseItem* >& items, ProjectFolderItem* newParent) 0601 { 0602 Q_D(AbstractFileManagerPlugin); 0603 0604 bool success = true; 0605 for (ProjectBaseItem* item : items) { 0606 Q_ASSERT(item->folder() || item->file()); 0607 0608 ProjectFolderItem* oldParent = parentFolder(item); 0609 d->stopWatcher(oldParent); 0610 d->stopWatcher(newParent); 0611 0612 const Path oldPath = item->path(); 0613 const Path newPath(newParent->path(), item->baseName()); 0614 0615 success &= renameUrl(oldParent->project(), oldPath.toUrl(), newPath. toUrl()); 0616 if ( success ) { 0617 if (item->file()) { 0618 emit fileRemoved(item->file()); 0619 } else { 0620 emit folderRemoved(item->folder()); 0621 } 0622 delete item; 0623 auto* const readJob = d->eventuallyReadFolder(newParent); 0624 // reload first level synchronously, deeper levels will run async 0625 // this is required for code that expects the new item to exist after 0626 // this method finished 0627 readJob->exec(); 0628 } 0629 0630 d->continueWatcher(oldParent); 0631 d->continueWatcher(newParent); 0632 if ( !success ) 0633 break; 0634 } 0635 return success; 0636 } 0637 0638 bool AbstractFileManagerPlugin::copyFilesAndFolders(const Path::List& items, ProjectFolderItem* newParent) 0639 { 0640 Q_D(AbstractFileManagerPlugin); 0641 0642 bool success = true; 0643 for (const Path& item : items) { 0644 d->stopWatcher(newParent); 0645 0646 success &= copyUrl(newParent->project(), item.toUrl(), newParent->path().toUrl()); 0647 if ( success ) { 0648 auto* const readJob = d->eventuallyReadFolder(newParent); 0649 // reload first level synchronously, deeper levels will run async 0650 // this is required for code that expects the new item to exist after 0651 // this method finished 0652 readJob->exec(); 0653 } 0654 0655 d->continueWatcher(newParent); 0656 if ( !success ) 0657 break; 0658 } 0659 return success; 0660 } 0661 0662 bool AbstractFileManagerPlugin::isValid( const Path& path, const bool isFolder, 0663 IProject* project ) const 0664 { 0665 Q_D(const AbstractFileManagerPlugin); 0666 0667 return d->m_filters.isValid( path, isFolder, project ); 0668 } 0669 0670 ProjectFileItem* AbstractFileManagerPlugin::createFileItem( IProject* project, const Path& path, 0671 ProjectBaseItem* parent ) 0672 { 0673 return new ProjectFileItem( project, path, parent ); 0674 } 0675 0676 ProjectFolderItem* AbstractFileManagerPlugin::createFolderItem( IProject* project, const Path& path, 0677 ProjectBaseItem* parent ) 0678 { 0679 return new ProjectFolderItem( project, path, parent ); 0680 } 0681 0682 KDirWatch* AbstractFileManagerPlugin::projectWatcher( IProject* project ) const 0683 { 0684 Q_D(const AbstractFileManagerPlugin); 0685 0686 return d->m_watchers.value( project, nullptr ); 0687 } 0688 0689 //END Plugin 0690 0691 #include "moc_abstractfilemanagerplugin.cpp"