Files
qtcreator/project_fs/projectfilesystemwidget.cpp

563 lines
18 KiB
C++

#include "projectfilesystemwidget.h"
#include <extensionsystem/pluginmanager.h>
#include <extensionsystem/pluginspec.h>
#include <coreplugin/icore.h>
#include <coreplugin/fileutils.h>
#include <coreplugin/navigationwidget.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/editormanager/editormanager.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorericons.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projecttreewidget.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <utils/utilsicons.h>
#include <utils/macroexpander.h>
#include <utils/fsengine/fileiconprovider.h>
#include <QApplication>
#include <QDesktopServices>
#include <QMessageBox>
#include <QInputDialog>
#include <QScrollBar>
#include <QClipboard>
QFileInfo projectfs_menu_target;
enum ItemType {
itProject = 1,
itFile,
itDir
};
const int roleFullPath = Qt::UserRole;
const int roleItemType = Qt::UserRole + 1;
const int roleIsDir = Qt::UserRole + 2;
const int roleProject = Qt::UserRole + 3;
const int roleLabel = Qt::UserRole + 4;
void setItemProjectName(ProjectExplorer::Project * pr, QTreeWidgetItem * item) {
if (!pr || !item) return;
QString dn = pr->displayName(), l = item->data(0, roleLabel).toString();
if (!l.isEmpty()) dn += " (" + l + ")";
item->setText(0, dn);
}
QAction * newSeparator() {
QAction * sep = new QAction();
sep->setSeparator(true);
return sep;
}
ProjectFilesystemWidget::ProjectFilesystemWidget(QWidget * parent): QWidget(parent) {
setupUi(this);
item_target = nullptr;
in_proc = need_rebuild = false;
int is = style()->pixelMetric(QStyle::PM_ButtonIconSize, 0, this);
label->setFixedSize(is, is);
buttonClear->setIcon(Utils::Icons::CLEAN.icon());
buttonExpand->setIcon(Utils::Icons::EXPAND.icon());
buttonCollapse->setIcon(Utils::Icons::COLLAPSE.icon());
actionOpen_here->setIcon(Utils::Icons::OPENFILE.icon());
actionOpen_external->setIcon(Utils::Icons::OPENFILE.icon());
actionShow_external->setIcon(Utils::FileIconProvider::icon(QFileIconProvider::Folder));
actionCopy_name->setIcon(Utils::Icons::COPY.icon());
actionCopy_path->setIcon(Utils::Icons::COPY.icon());
actionBuild->setIcon(ProjectExplorer::Icons::BUILD_SMALL.icon());
actionClean->setIcon(Utils::Icons::CLEAN.icon());
actionRun->setIcon(ProjectExplorer::Icons::RUN_FLAT.icon());
actionClose_project->setIcon(Utils::Icons::CLOSE_TOOLBAR.icon());
this_actions << actionSet_as_startup << actionBuild << actionRun;
this_actions << newSeparator();
this_actions << actionRebuild << actionClean;
this_actions << newSeparator();
this_actions << actionClose_project;
this_actions << newSeparator();
this_actions << actionOpen_here << actionOpen_external << actionShow_external << actionOpen_terminal;
this_actions << newSeparator();
this_actions << actionCopy_name << actionCopy_path;
proj_plug = 0;
//connect(ProjectExplorer::ProjectTree::instance(), SIGNAL(subtreeChanged(ProjectExplorer::FolderNode*)), this, SLOT(projectsChanged()));
connect(ProjectExplorer::ProjectManager::instance(), SIGNAL(startupProjectChanged(ProjectExplorer::Project *)), this, SLOT(startupProjectChanged()));
connect(ProjectExplorer::ProjectManager::instance(), &ProjectExplorer::ProjectManager::projectAdded, [this](ProjectExplorer::Project * p){
createProjectTree(p);
filter();
startupProjectChanged();
});
connect(ProjectExplorer::ProjectManager::instance(), &ProjectExplorer::ProjectManager::projectDisplayNameChanged, [this](ProjectExplorer::Project * p){
projectNameChanged();
});
connect(ProjectExplorer::ProjectManager::instance(), &ProjectExplorer::ProjectManager::projectRemoved, [this](ProjectExplorer::Project * p){
QTreeWidgetItem * pi = projectItem(p);
if (!pi) return;
delete pi;
cleanMappedItems();
startupProjectChanged();
});
//connect(ProjectExplorer::ProjectManager::instance(), SIGNAL(), this, SLOT(startupProjectChanged()));
connect(Core::EditorManager::instance(), SIGNAL(currentEditorChanged(Core::IEditor*)), this, SLOT(currentFileChanged()));
connect(&fs_watcher, SIGNAL(directoryChanged(const QString &)), this, SLOT(directoryChanged(const QString &)));
projectsChanged();
}
void ProjectFilesystemWidget::setCurrentFilter(const FilterDialog::Filter & v) {
cur_filter = v;
projectsChanged();
}
void ProjectFilesystemWidget::changeEvent(QEvent * e) {
QWidget::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
retranslateUi(this);
break;
default:
break;
}
}
void ProjectFilesystemWidget::timerEvent(QTimerEvent * e) {
if (timer_fs_changed > 0)
killTimer(timer_fs_changed);
timer_fs_changed = 0;
int spos = tree->verticalScrollBar()->value();
last_expanded.clear();
rememberExpanded(tree->invisibleRootItem());
for (QString path: fs_changed) {
QTreeWidgetItem * ti = item_map.value(path);
//QMessageBox::information(0, "", "changed " + path + " " + QString::number((qulonglong)ti));
if (ti) {
createTree(ti, path, false);
cleanMappedItems();
}
}
filter();
restoreExpanded(tree->invisibleRootItem());
fs_changed.clear();
currentFileChanged();
tree->verticalScrollBar()->setValue(spos);
QWidget::timerEvent(e);
}
void ProjectFilesystemWidget::projectNameChanged() {
for (int i = 0; i < tree->topLevelItemCount(); ++i) {
ProjectExplorer::Project * p = (ProjectExplorer::Project *)itemProject(tree->topLevelItem(i));
if (!p) continue;
setItemProjectName(p, tree->topLevelItem(i));
}
}
void ProjectFilesystemWidget::createTree(QTreeWidgetItem * ti, const QString & dir, bool proc_events) {
if (ti->childCount() > 0) {
qDeleteAll(ti->takeChildren());
}
QFileInfoList fl = QDir(dir).entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot, QDir::LocaleAware | QDir::DirsFirst);
if (proc_events) checkProcEvents();
fs_watcher.addPath(dir);
item_map[dir] = ti;
for (QFileInfo i: fl) {
QString nit = i.fileName();
if (i.isDir()) {
if (!cur_filter.filterDir(nit))
continue;
} else {
if (!cur_filter.filterFile(nit))
continue;
}
QTreeWidgetItem * ni = new QTreeWidgetItem();
ni->setText(0, nit);
ni->setIcon(0, Utils::FileIconProvider::icon(Utils::FilePath::fromString(i.absoluteFilePath())));
ni->setData(0, roleFullPath, i.absoluteFilePath());
ni->setData(0, roleItemType, i.isDir() ? itDir : itFile);
ni->setData(0, roleIsDir, i.isDir());
item_map[i.absoluteFilePath()] = ni;
if (i.isDir()) {
createTree(ni, dir + "/" + i.fileName(), proc_events);
}
ti->addChild(ni);
}
//if (ti->childCount() == 0) delete ti;
}
bool ProjectFilesystemWidget::filterTree(QTreeWidgetItem * ti, const QString & filter) {
bool ret = false;
for (int i = 0; i < ti->childCount(); ++i) {
QTreeWidgetItem * ci = ti->child(i);
QString cit = ci->text(0);
if (ci->data(0, roleIsDir).toBool()) {
if (!filterTree(ci, filter)) {
ci->setHidden(true);
continue;
}
ci->setHidden(false);
ret = true;
} else {
bool f = false;
if (filter.isEmpty()) {
f = true;
} else {
f = f || cit.contains(filter);
}
ci->setHidden(!f);
if (f) ret = true;
}
}
return ret;
}
void ProjectFilesystemWidget::filter() {
QString f = lineFilter->text();
for (int i = 0; i < tree->topLevelItemCount(); ++i) {
QTreeWidgetItem * ti = tree->topLevelItem(i);
filterTree(ti, f);
}
}
void ProjectFilesystemWidget::rememberExpanded(QTreeWidgetItem * ti) {
//QMessageBox::information(0, ti->data(0, roleFullPath).toString(), QString::number(ti->childCount()));
for (int i = 0; i < ti->childCount(); ++i) {
QTreeWidgetItem * ci = ti->child(i);
if (ci->data(0, roleIsDir).toBool()) {
if (ci->isExpanded())
last_expanded << ci->data(0, roleFullPath).toString();
rememberExpanded(ci);
}
}
}
void ProjectFilesystemWidget::restoreExpanded(QTreeWidgetItem * ti) {
for (int i = 0; i < ti->childCount(); ++i) {
QTreeWidgetItem * ci = ti->child(i);
if (ci->data(0, roleIsDir).toBool()) {
if (last_expanded.contains(ci->data(0, roleFullPath).toString()))
ci->setExpanded(true);
restoreExpanded(ci);
}
}
}
void ProjectFilesystemWidget::setExtVariable() {
}
void ProjectFilesystemWidget::checkProcEvents() {
if (tm.elapsed() < 10) return;
QApplication::processEvents();
tm.start();
}
void * ProjectFilesystemWidget::itemProject(QTreeWidgetItem * ti) {
if (!ti) return nullptr;
return (void*)(ti->data(0, roleProject).toULongLong());
}
void ProjectFilesystemWidget::projectsChanged() {
if (in_proc) {
need_rebuild = true;
return;
}
in_proc = true;
tm.start();
last_expanded.clear();
item_map.clear();
int spos = tree->verticalScrollBar()->value();
rememberExpanded(tree->invisibleRootItem());
tree->clear();
fs_watcher.removePaths(fs_watcher.directories());
QApplication::setOverrideCursor(Qt::WaitCursor);
QList<ProjectExplorer::Project *> pl = ProjectExplorer::ProjectManager::projects();
for (ProjectExplorer::Project * p: pl) {
createProjectTree(p);
}
startupProjectChanged();
currentFileChanged();
filter();
restoreExpanded(tree->invisibleRootItem());
QApplication::restoreOverrideCursor();
qApp->processEvents();
tree->verticalScrollBar()->setValue(spos);
in_proc = false;
if (need_rebuild) {
need_rebuild = false;
projectsChanged();
}
}
void ProjectFilesystemWidget::createProjectTree(void * p) {
if (!p) return;
ProjectExplorer::Project * pr = (ProjectExplorer::Project *)p;
QTreeWidgetItem * ri = new QTreeWidgetItem();
QString dir = pr->projectDirectory().toString();
QFile logo(dir + "/logo.png");
if (logo.exists())
ri->setIcon(0, QIcon(logo.fileName()));
else
ri->setIcon(0, Utils::FileIconProvider::icon(QFileIconProvider::Folder));
QFile label(dir + "/label.txt");
if (label.exists()) {
label.open(QIODevice::ReadOnly);
ri->setData(0, roleLabel, QString::fromUtf8(label.readAll()).trimmed());
} else
ri->setData(0, roleLabel, QString());
ri->setData(0, roleProject, (qulonglong)pr);
ri->setData(0, roleFullPath, dir);
ri->setData(0, roleIsDir, true);
ri->setData(0, roleItemType, itProject);
setItemProjectName(pr, ri);
createTree(ri, dir);
tree->addTopLevelItem(ri);
}
void ProjectFilesystemWidget::filterClicked() {
filter_dialog.setFilter(cur_filter);
if (filter_dialog.exec() == QDialog::Rejected) return;
setCurrentFilter(filter_dialog.filter());
}
void ProjectFilesystemWidget::currentFileChanged() {
Core::IDocument * cd = Core::EditorManager::instance()->currentDocument();
if (!cd) return;
QString np = cd->filePath().toString();
QTreeWidgetItem * ti = item_map.value(np);
if (!ti) return;
tree->setCurrentItem(ti);
tree->expandItem(ti);
}
void ProjectFilesystemWidget::startupProjectChanged() {
ProjectExplorer::Project * sp = ProjectExplorer::ProjectManager::startupProject();
QFont f(tree->font()), bf(f);
bf.setBold(true);
for (int i = 0; i < tree->topLevelItemCount(); ++i) {
QTreeWidgetItem * ti = tree->topLevelItem(i);
ti->setFont(0, f);
if (!sp) continue;
if (sp->projectDirectory().toString() == ti->data(0, roleFullPath).toString())
ti->setFont(0, bf);
}
}
QTreeWidgetItem * ProjectFilesystemWidget::projectItem(void * p) {
if (!p) return nullptr;
for (int i = 0; i < tree->topLevelItemCount(); ++i) {
QTreeWidgetItem * ti = tree->topLevelItem(i);
if (itemProject(ti) == p)
return ti;
}
return nullptr;
}
void ProjectFilesystemWidget::cleanMappedItems() {
QList<QTreeWidgetItem*> ail = tree->findItems("", Qt::MatchContains | Qt::MatchRecursive), map_values = item_map.values();
QSet<QTreeWidgetItem*> ais;
//QString removed;
for (auto * i: ail) ais << i;
for (auto * i: map_values)
if (!ais.contains(i)) {
//removed += item_map.key(i) + "\n";
QString path = item_map.key(i);
if (QDir(path).exists())
fs_watcher.removePath(path);
item_map.remove(path);
}
}
void ProjectFilesystemWidget::directoryChanged(const QString & path) {
if (timer_fs_changed > 0)
killTimer(timer_fs_changed);
timer_fs_changed = startTimer(500);
fs_changed << path;
//QMessageBox::information(0, "", "map " + QString::number(ais.size()));
//QMessageBox::information(0, "", "remove " + removed);
}
void ProjectFilesystemWidget::on_tree_itemDoubleClicked(QTreeWidgetItem * item, int) {
if (!item) return;
QString afp = item->data(0, roleFullPath).toString();
bool dir = item->data(0, roleIsDir).toBool();
if (dir) return;
if (afp.isEmpty()) return;
Core::EditorManager::openEditor(Utils::FilePath::fromString(afp));
}
void ProjectFilesystemWidget::on_lineFilter_textChanged(const QString & ) {
filter();
}
void ProjectFilesystemWidget::on_tree_itemClicked(QTreeWidgetItem * item, int column) {
projectfs_menu_target = QFileInfo();
if (!item) {
setExtVariable();
return;
}
projectfs_menu_target = QFileInfo(item->data(0, roleFullPath).toString());
setExtVariable();
}
void ProjectFilesystemWidget::on_tree_customContextMenuRequested(const QPoint & pos) {
projectfs_menu_target = QFileInfo();
QTreeWidgetItem * item = tree->itemAt(pos);
//QMessageBox::information(this, "", QString::number(index.row()));
item_target = nullptr;
if (!item) {
setExtVariable();
return;
}
item_target = item;
projectfs_menu_target = QFileInfo(item->data(0, roleFullPath).toString());
setExtVariable();
actionOpen_here->setVisible(!projectfs_menu_target.isDir());
actionOpen_external->setVisible(!projectfs_menu_target.isDir());
popup_menu.clear();
/*if (item->data(0, roleItemType).toInt() == itProject) {
QString proj_path = item->data(0, roleFullPath).toString();
ProjectExplorer::Project * project = Utils::findOrDefault(ProjectExplorer::ProjectManager::projects(),
[proj_path](const ProjectExplorer::Project * p) {return p->containerNode()->path() == proj_path;});
//ProjectExplorer::ProjectTree::instance()->setCurrent
QMenu * contextMenu = Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_PROJECTCONTEXT)->menu();
//contextMenu = Core::ActionManager::actionContainer(Constants::M_FOLDERCONTEXT)->menu();
//contextMenu = Core::ActionManager::actionContainer(Constants::M_FILECONTEXT)->menu();
popup_menu.addActions(contextMenu->actions());
}*/
ProjectExplorer::Project * p = (ProjectExplorer::Project *)itemProject(item_target);
if (p) {
actionSet_as_startup->setVisible(p != ProjectExplorer::ProjectManager::startupProject());
bool can_build = !p->needsConfiguration(), can_run = false;
if (p->activeTarget()) {
can_build = can_build && p->activeTarget()->activeBuildConfiguration();
can_run = p->activeTarget()->activeRunConfiguration();
}
actionBuild->setVisible(can_build);
actionClean->setVisible(can_build);
actionRebuild->setVisible(can_build);
actionRun->setVisible(can_run);
} else {
actionSet_as_startup->setVisible(false);
actionBuild->setVisible(false);
actionClean->setVisible(false);
actionRebuild->setVisible(false);
actionRun->setVisible(false);
}
actionClose_project->setVisible(p);
popup_menu.addActions(this_actions);
popup_menu.popup(tree->mapToGlobal(pos));
}
void ProjectFilesystemWidget::on_actionOpen_here_triggered() {
if (projectfs_menu_target.path().isEmpty()) return;
Core::EditorManager::openEditor(Utils::FilePath::fromString(projectfs_menu_target.absoluteFilePath()), Core::Constants::K_DEFAULT_TEXT_EDITOR_ID);
}
void ProjectFilesystemWidget::on_actionOpen_external_triggered() {
if (projectfs_menu_target.path().isEmpty()) return;
QString wd = QDir::current().absolutePath();
QDir::setCurrent(projectfs_menu_target.absoluteDir().path());
QDesktopServices::openUrl(QUrl::fromLocalFile(projectfs_menu_target.absoluteFilePath()));
QDir::setCurrent(wd);
}
void ProjectFilesystemWidget::on_actionShow_external_triggered() {
if (projectfs_menu_target.path().isEmpty()) return;
Core::FileUtils::showInGraphicalShell(Core::ICore::mainWindow(), Utils::FilePath::fromString(projectfs_menu_target.absoluteFilePath()));
}
void ProjectFilesystemWidget::on_actionOpen_terminal_triggered() {
if (projectfs_menu_target.path().isEmpty()) return;
Core::FileUtils::openTerminal(Utils::FilePath::fromString(QDir(projectfs_menu_target.absoluteFilePath()).path()), {});
}
void ProjectFilesystemWidget::on_actionCopy_name_triggered() {
if (projectfs_menu_target.path().isEmpty()) return;
QApplication::clipboard()->setText(projectfs_menu_target.fileName());
}
void ProjectFilesystemWidget::on_actionCopy_path_triggered() {
if (projectfs_menu_target.path().isEmpty()) return;
QApplication::clipboard()->setText(projectfs_menu_target.absoluteFilePath());
}
void ProjectFilesystemWidget::on_actionSet_as_startup_triggered() {
ProjectExplorer::Project * p = (ProjectExplorer::Project *)itemProject(item_target);
if (!p) return;
ProjectExplorer::ProjectManager::setStartupProject(p);
}
void ProjectFilesystemWidget::on_actionBuild_triggered() {
ProjectExplorer::Project * p = (ProjectExplorer::Project *)itemProject(item_target);
if (!p) return;
ProjectExplorer::BuildManager::buildProjectWithDependencies(p);
}
void ProjectFilesystemWidget::on_actionRebuild_triggered() {
ProjectExplorer::Project * p = (ProjectExplorer::Project *)itemProject(item_target);
if (!p) return;
ProjectExplorer::BuildManager::rebuildProjectWithDependencies(p, ProjectExplorer::ConfigSelection::Active);
}
void ProjectFilesystemWidget::on_actionClean_triggered() {
ProjectExplorer::Project * p = (ProjectExplorer::Project *)itemProject(item_target);
if (!p) return;
ProjectExplorer::BuildManager::cleanProjectWithoutDependencies(p);
}
void ProjectFilesystemWidget::on_actionRun_triggered() {
ProjectExplorer::Project * p = (ProjectExplorer::Project *)itemProject(item_target);
if (!p) return;
ProjectExplorer::ProjectExplorerPlugin::runProject(p, ProjectExplorer::Constants::NORMAL_RUN_MODE);
}
void ProjectFilesystemWidget::on_actionClose_project_triggered() {
ProjectExplorer::Project * p = (ProjectExplorer::Project *)itemProject(item_target);
if (!p) return;
if (QMessageBox::question(0, "Project close", tr("Are you sure to close project \"%1\"?").arg(p->displayName())) != QMessageBox::Yes)
return;
ProjectExplorer::ProjectManager::removeProject(p);
}