forked from qt-creator/qt-creator
Change File System pane to a forest of trees
It shows the file system as a tree, where the user can change the root directory of the view. Currently there are "Computer" (the default), and all project directories. If synchronization with the current editor is enabled, the view automatically switches to the best fitting root directory. Task-number: QTCREATORBUG-8305 Change-Id: Ic265eb49b1e8e0fd8cdeeb4fb1c64b8631f32e21 Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
This commit is contained in:
@@ -26,8 +26,6 @@
|
|||||||
#include "foldernavigationwidget.h"
|
#include "foldernavigationwidget.h"
|
||||||
#include "projectexplorer.h"
|
#include "projectexplorer.h"
|
||||||
|
|
||||||
#include <extensionsystem/pluginmanager.h>
|
|
||||||
|
|
||||||
#include <coreplugin/actionmanager/command.h>
|
#include <coreplugin/actionmanager/command.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/idocument.h>
|
#include <coreplugin/idocument.h>
|
||||||
@@ -35,62 +33,34 @@
|
|||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
#include <coreplugin/editormanager/ieditor.h>
|
#include <coreplugin/editormanager/ieditor.h>
|
||||||
#include <coreplugin/fileutils.h>
|
#include <coreplugin/fileutils.h>
|
||||||
#include <coreplugin/find/findplugin.h>
|
|
||||||
|
|
||||||
#include <texteditor/findinfiles.h>
|
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
#include <utils/pathchooser.h>
|
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/elidinglabel.h>
|
#include <utils/navigationtreeview.h>
|
||||||
#include <utils/itemviews.h>
|
|
||||||
#include <utils/utilsicons.h>
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QComboBox>
|
||||||
|
#include <QHeaderView>
|
||||||
#include <QSize>
|
#include <QSize>
|
||||||
|
#include <QTimer>
|
||||||
#include <QFileSystemModel>
|
#include <QFileSystemModel>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
#include <QSortFilterProxyModel>
|
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QFileDialog>
|
|
||||||
#include <QContextMenuEvent>
|
#include <QContextMenuEvent>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
|
||||||
enum { debug = 0 };
|
|
||||||
|
|
||||||
namespace ProjectExplorer {
|
namespace ProjectExplorer {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
// Hide the '.' entry.
|
static FolderNavigationWidgetFactory *m_instance = nullptr;
|
||||||
class DotRemovalFilter : public QSortFilterProxyModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit DotRemovalFilter(QObject *parent = nullptr);
|
|
||||||
protected:
|
|
||||||
virtual bool filterAcceptsRow(int source_row, const QModelIndex &parent) const;
|
|
||||||
Qt::DropActions supportedDragActions() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
DotRemovalFilter::DotRemovalFilter(QObject *parent) : QSortFilterProxyModel(parent)
|
QVector<FolderNavigationWidgetFactory::DirectoryEntry>
|
||||||
{ }
|
FolderNavigationWidgetFactory::m_rootDirectories = {
|
||||||
|
{FolderNavigationWidget::tr("Computer"), Utils::FileName()}};
|
||||||
bool DotRemovalFilter::filterAcceptsRow(int source_row, const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
const QVariant fileName = sourceModel()->data(parent.child(source_row, 0));
|
|
||||||
if (Utils::HostOsInfo::isAnyUnixHost())
|
|
||||||
if (sourceModel()->data(parent) == QLatin1String("/") && fileName == QLatin1String(".."))
|
|
||||||
return false;
|
|
||||||
return fileName != QLatin1String(".");
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::DropActions DotRemovalFilter::supportedDragActions() const
|
|
||||||
{
|
|
||||||
return sourceModel()->supportedDragActions();
|
|
||||||
}
|
|
||||||
|
|
||||||
// FolderNavigationModel: Shows path as tooltip.
|
// FolderNavigationModel: Shows path as tooltip.
|
||||||
class FolderNavigationModel : public QFileSystemModel
|
class FolderNavigationModel : public QFileSystemModel
|
||||||
@@ -117,42 +87,46 @@ Qt::DropActions FolderNavigationModel::supportedDragActions() const
|
|||||||
return Qt::MoveAction;
|
return Qt::MoveAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
static void showOnlyFirstColumn(QTreeView *view)
|
||||||
\class FolderNavigationWidget
|
{
|
||||||
|
const int columnCount = view->header()->count();
|
||||||
|
for (int i = 1; i < columnCount; ++i)
|
||||||
|
view->setColumnHidden(i, true);
|
||||||
|
}
|
||||||
|
|
||||||
Shows a file system folder
|
/*!
|
||||||
*/
|
\class FolderNavigationWidget
|
||||||
|
|
||||||
|
Shows a file system tree, with the root directory selectable from a dropdown.
|
||||||
|
|
||||||
|
\internal
|
||||||
|
*/
|
||||||
FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent),
|
FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent),
|
||||||
m_listView(new Utils::ListView(this)),
|
m_listView(new Utils::NavigationTreeView(this)),
|
||||||
m_fileSystemModel(new FolderNavigationModel(this)),
|
m_fileSystemModel(new FolderNavigationModel(this)),
|
||||||
m_filterHiddenFilesAction(new QAction(tr("Show Hidden Files"), this)),
|
m_filterHiddenFilesAction(new QAction(tr("Show Hidden Files"), this)),
|
||||||
m_filterModel(new DotRemovalFilter(this)),
|
m_toggleSync(new QToolButton(this)),
|
||||||
m_title(new Utils::ElidingLabel(this)),
|
m_rootSelector(new QComboBox)
|
||||||
m_toggleSync(new QToolButton(this))
|
|
||||||
{
|
{
|
||||||
m_fileSystemModel->setResolveSymlinks(false);
|
m_fileSystemModel->setResolveSymlinks(false);
|
||||||
m_fileSystemModel->setIconProvider(Core::FileIconProvider::iconProvider());
|
m_fileSystemModel->setIconProvider(Core::FileIconProvider::iconProvider());
|
||||||
QDir::Filters filters = QDir::AllDirs | QDir::Files | QDir::Drives
|
QDir::Filters filters = QDir::AllEntries | QDir::NoDotAndDotDot;
|
||||||
| QDir::Readable| QDir::Writable
|
|
||||||
| QDir::Executable | QDir::Hidden;
|
|
||||||
if (Utils::HostOsInfo::isWindowsHost()) // Symlinked directories can cause file watcher warnings on Win32.
|
if (Utils::HostOsInfo::isWindowsHost()) // Symlinked directories can cause file watcher warnings on Win32.
|
||||||
filters |= QDir::NoSymLinks;
|
filters |= QDir::NoSymLinks;
|
||||||
m_fileSystemModel->setFilter(filters);
|
m_fileSystemModel->setFilter(filters);
|
||||||
m_filterModel->setSourceModel(m_fileSystemModel);
|
m_fileSystemModel->setRootPath(QString());
|
||||||
m_filterHiddenFilesAction->setCheckable(true);
|
m_filterHiddenFilesAction->setCheckable(true);
|
||||||
setHiddenFilesFilter(false);
|
setHiddenFilesFilter(false);
|
||||||
m_listView->setIconSize(QSize(16,16));
|
m_listView->setIconSize(QSize(16,16));
|
||||||
m_listView->setModel(m_filterModel);
|
m_listView->setModel(m_fileSystemModel);
|
||||||
m_listView->setFrameStyle(QFrame::NoFrame);
|
|
||||||
m_listView->setAttribute(Qt::WA_MacShowFocusRect, false);
|
|
||||||
m_listView->setDragEnabled(true);
|
m_listView->setDragEnabled(true);
|
||||||
m_listView->setDragDropMode(QAbstractItemView::DragOnly);
|
m_listView->setDragDropMode(QAbstractItemView::DragOnly);
|
||||||
|
showOnlyFirstColumn(m_listView);
|
||||||
setFocusProxy(m_listView);
|
setFocusProxy(m_listView);
|
||||||
|
|
||||||
auto layout = new QVBoxLayout();
|
auto layout = new QVBoxLayout();
|
||||||
layout->addWidget(m_title);
|
layout->addWidget(m_rootSelector);
|
||||||
layout->addWidget(m_listView);
|
layout->addWidget(m_listView);
|
||||||
m_title->setMargin(5);
|
|
||||||
layout->setSpacing(0);
|
layout->setSpacing(0);
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
@@ -164,13 +138,26 @@ FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent
|
|||||||
|
|
||||||
// connections
|
// connections
|
||||||
connect(m_listView, &QAbstractItemView::activated,
|
connect(m_listView, &QAbstractItemView::activated,
|
||||||
this, &FolderNavigationWidget::slotOpenItem);
|
this, [this](const QModelIndex &index) { openItem(index); });
|
||||||
connect(m_filterHiddenFilesAction, &QAction::toggled,
|
connect(m_filterHiddenFilesAction, &QAction::toggled,
|
||||||
this, &FolderNavigationWidget::setHiddenFilesFilter);
|
this, &FolderNavigationWidget::setHiddenFilesFilter);
|
||||||
connect(m_toggleSync, &QAbstractButton::clicked,
|
connect(m_toggleSync, &QAbstractButton::clicked,
|
||||||
this, &FolderNavigationWidget::toggleAutoSynchronization);
|
this, &FolderNavigationWidget::toggleAutoSynchronization);
|
||||||
connect(m_filterModel, &QAbstractItemModel::layoutChanged,
|
connect(m_rootSelector,
|
||||||
this, &FolderNavigationWidget::ensureCurrentIndex);
|
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||||
|
this,
|
||||||
|
[this](int index) {
|
||||||
|
const auto directory = m_rootSelector->itemData(index).value<Utils::FileName>();
|
||||||
|
m_rootSelector->setToolTip(directory.toString());
|
||||||
|
setRootDirectory(directory);
|
||||||
|
});
|
||||||
|
connect(m_rootSelector,
|
||||||
|
static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
|
||||||
|
this,
|
||||||
|
[this] {
|
||||||
|
if (m_autoSync && Core::EditorManager::currentEditor())
|
||||||
|
selectFile(Core::EditorManager::currentEditor()->document()->filePath());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void FolderNavigationWidget::toggleAutoSynchronization()
|
void FolderNavigationWidget::toggleAutoSynchronization()
|
||||||
@@ -178,6 +165,29 @@ void FolderNavigationWidget::toggleAutoSynchronization()
|
|||||||
setAutoSynchronization(!m_autoSync);
|
setAutoSynchronization(!m_autoSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FolderNavigationWidget::addRootDirectory(const QString &displayName,
|
||||||
|
const Utils::FileName &directory)
|
||||||
|
{
|
||||||
|
m_rootSelector->addItem(displayName, qVariantFromValue(directory));
|
||||||
|
m_rootSelector->setItemData(m_rootSelector->count() - 1,
|
||||||
|
directory.toUserOutput(),
|
||||||
|
Qt::ToolTipRole);
|
||||||
|
if (m_autoSync) // we might find a better root for current selection now
|
||||||
|
setCurrentEditor(Core::EditorManager::currentEditor());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderNavigationWidget::removeRootDirectory(const Utils::FileName &directory)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_rootSelector->count(); ++i) {
|
||||||
|
if (m_rootSelector->itemData(i).value<Utils::FileName>() == directory) {
|
||||||
|
m_rootSelector->removeItem(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_autoSync) // we might need to find a new root for current selection
|
||||||
|
setCurrentEditor(Core::EditorManager::currentEditor());
|
||||||
|
}
|
||||||
|
|
||||||
bool FolderNavigationWidget::autoSynchronization() const
|
bool FolderNavigationWidget::autoSynchronization() const
|
||||||
{
|
{
|
||||||
return m_autoSync;
|
return m_autoSync;
|
||||||
@@ -193,148 +203,95 @@ void FolderNavigationWidget::setAutoSynchronization(bool sync)
|
|||||||
|
|
||||||
if (m_autoSync) {
|
if (m_autoSync) {
|
||||||
connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
|
connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
|
||||||
this, &FolderNavigationWidget::setCurrentFile);
|
this, &FolderNavigationWidget::setCurrentEditor);
|
||||||
setCurrentFile(Core::EditorManager::currentEditor());
|
setCurrentEditor(Core::EditorManager::currentEditor());
|
||||||
} else {
|
} else {
|
||||||
disconnect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
|
disconnect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
|
||||||
this, &FolderNavigationWidget::setCurrentFile);
|
this, &FolderNavigationWidget::setCurrentEditor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FolderNavigationWidget::setCurrentFile(Core::IEditor *editor)
|
void FolderNavigationWidget::setCurrentEditor(Core::IEditor *editor)
|
||||||
{
|
{
|
||||||
if (!editor)
|
if (!editor)
|
||||||
return;
|
return;
|
||||||
|
const Utils::FileName filePath = editor->document()->filePath();
|
||||||
|
// switch to most fitting root
|
||||||
|
const int bestRootIndex = bestRootForFile(filePath);
|
||||||
|
m_rootSelector->setCurrentIndex(bestRootIndex);
|
||||||
|
// select
|
||||||
|
selectFile(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
const QString filePath = editor->document()->filePath().toString();
|
void FolderNavigationWidget::selectFile(const Utils::FileName &filePath)
|
||||||
// Try to find directory of current file
|
{
|
||||||
bool pathOpened = false;
|
const QModelIndex fileIndex = m_fileSystemModel->index(filePath.toString());
|
||||||
if (!filePath.isEmpty()) {
|
if (fileIndex.isValid()) {
|
||||||
const QFileInfo fi(filePath);
|
// TODO This only scrolls to the right position if all directory contents are loaded.
|
||||||
if (fi.exists())
|
// Unfortunately listening to directoryLoaded was still not enough (there might also
|
||||||
pathOpened = setCurrentDirectory(fi.absolutePath());
|
// be some delayed sorting involved?).
|
||||||
|
// Use magic timer for scrolling.
|
||||||
|
m_listView->setCurrentIndex(fileIndex);
|
||||||
|
QTimer::singleShot(200, this, [this, filePath] {
|
||||||
|
const QModelIndex fileIndex = m_fileSystemModel->index(filePath.toString());
|
||||||
|
m_listView->scrollTo(fileIndex);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (!pathOpened) // Default to home.
|
}
|
||||||
setCurrentDirectory(Utils::PathChooser::homePath());
|
|
||||||
|
|
||||||
// Select the current file.
|
void FolderNavigationWidget::setRootDirectory(const Utils::FileName &directory)
|
||||||
if (pathOpened) {
|
{
|
||||||
const QModelIndex fileIndex = m_fileSystemModel->index(filePath);
|
const QModelIndex index = m_fileSystemModel->setRootPath(directory.toString());
|
||||||
if (fileIndex.isValid()) {
|
m_listView->setRootIndex(index);
|
||||||
QItemSelectionModel *selections = m_listView->selectionModel();
|
}
|
||||||
const QModelIndex mainIndex = m_filterModel->mapFromSource(fileIndex);
|
|
||||||
selections->setCurrentIndex(mainIndex, QItemSelectionModel::SelectCurrent
|
int FolderNavigationWidget::bestRootForFile(const Utils::FileName &filePath)
|
||||||
| QItemSelectionModel::Clear);
|
{
|
||||||
m_listView->scrollTo(mainIndex);
|
int index = 0; // Computer is default
|
||||||
|
int commonLength = 0;
|
||||||
|
for (int i = 1; i < m_rootSelector->count(); ++i) {
|
||||||
|
const auto root = m_rootSelector->itemData(i).value<Utils::FileName>();
|
||||||
|
if (filePath.isChildOf(root) && root.length() > commonLength) {
|
||||||
|
index = i;
|
||||||
|
commonLength = root.length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FolderNavigationWidget::setCurrentDirectory(const QString &directory)
|
void FolderNavigationWidget::openItem(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
const QString newDirectory = directory.isEmpty() ? QDir::rootPath() : directory;
|
if (!index.isValid())
|
||||||
if (debug)
|
|
||||||
qDebug() << "setcurdir" << directory << newDirectory;
|
|
||||||
// Set the root path on the model instead of changing the top index
|
|
||||||
// of the view to cause the model to clean out its file watchers.
|
|
||||||
const QModelIndex index = m_fileSystemModel->setRootPath(newDirectory);
|
|
||||||
if (!index.isValid()) {
|
|
||||||
setCurrentTitle(QString(), QString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
QModelIndex oldRootIndex = m_listView->rootIndex();
|
|
||||||
QModelIndex newRootIndex = m_filterModel->mapFromSource(index);
|
|
||||||
m_listView->setRootIndex(newRootIndex);
|
|
||||||
const QDir current(QDir::cleanPath(newDirectory));
|
|
||||||
setCurrentTitle(current.dirName(),
|
|
||||||
QDir::toNativeSeparators(current.absolutePath()));
|
|
||||||
if (oldRootIndex.parent() == newRootIndex) { // cdUp, so select the old directory
|
|
||||||
m_listView->setCurrentIndex(oldRootIndex);
|
|
||||||
m_listView->scrollTo(oldRootIndex, QAbstractItemView::EnsureVisible);
|
|
||||||
}
|
|
||||||
|
|
||||||
return !directory.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString FolderNavigationWidget::currentDirectory() const
|
|
||||||
{
|
|
||||||
return m_fileSystemModel->rootPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderNavigationWidget::slotOpenItem(const QModelIndex &viewIndex)
|
|
||||||
{
|
|
||||||
if (viewIndex.isValid())
|
|
||||||
openItem(m_filterModel->mapToSource(viewIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderNavigationWidget::openItem(const QModelIndex &srcIndex, bool openDirectoryAsProject)
|
|
||||||
{
|
|
||||||
const QString fileName = m_fileSystemModel->fileName(srcIndex);
|
|
||||||
if (fileName == QLatin1String("."))
|
|
||||||
return;
|
return;
|
||||||
if (fileName == QLatin1String("..")) {
|
const QString path = m_fileSystemModel->filePath(index);
|
||||||
// cd up: Special behaviour: The fileInfo of ".." is that of the parent directory.
|
if (m_fileSystemModel->isDir(index)) {
|
||||||
const QString parentPath = m_fileSystemModel->fileInfo(srcIndex).absoluteFilePath();
|
const QFileInfo fi = m_fileSystemModel->fileInfo(index);
|
||||||
setCurrentDirectory(parentPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const QString path = m_fileSystemModel->filePath(srcIndex);
|
|
||||||
if (m_fileSystemModel->isDir(srcIndex)) {
|
|
||||||
const QFileInfo fi = m_fileSystemModel->fileInfo(srcIndex);
|
|
||||||
if (!fi.isReadable() || !fi.isExecutable())
|
if (!fi.isReadable() || !fi.isExecutable())
|
||||||
return;
|
return;
|
||||||
// Try to find project files in directory and open those.
|
// Try to find project files in directory and open those.
|
||||||
if (openDirectoryAsProject) {
|
const QStringList projectFiles = FolderNavigationWidget::projectFilesInDirectory(path);
|
||||||
const QStringList projectFiles = FolderNavigationWidget::projectFilesInDirectory(path);
|
if (!projectFiles.isEmpty())
|
||||||
if (!projectFiles.isEmpty())
|
Core::ICore::instance()->openFiles(projectFiles);
|
||||||
Core::ICore::instance()->openFiles(projectFiles);
|
} else {
|
||||||
return;
|
// Open editor
|
||||||
}
|
Core::EditorManager::openEditor(path);
|
||||||
// Change to directory
|
|
||||||
setCurrentDirectory(path);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// Open file.
|
|
||||||
Core::ICore::instance()->openFiles(QStringList(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderNavigationWidget::setCurrentTitle(QString dirName, const QString &fullPath)
|
|
||||||
{
|
|
||||||
if (dirName.isEmpty())
|
|
||||||
dirName = fullPath;
|
|
||||||
m_title->setText(dirName);
|
|
||||||
m_title->setToolTip(fullPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex FolderNavigationWidget::currentItem() const
|
|
||||||
{
|
|
||||||
const QModelIndex current = m_listView->currentIndex();
|
|
||||||
if (current.isValid())
|
|
||||||
return m_filterModel->mapToSource(current);
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format the text for the "open" action of the context menu according
|
|
||||||
// to the selectect entry
|
|
||||||
static inline QString actionOpenText(const QFileSystemModel *model,
|
|
||||||
const QModelIndex &index)
|
|
||||||
{
|
|
||||||
if (!index.isValid())
|
|
||||||
return FolderNavigationWidget::tr("Open");
|
|
||||||
const QString fileName = model->fileName(index);
|
|
||||||
if (fileName == QLatin1String(".."))
|
|
||||||
return FolderNavigationWidget::tr("Open Parent Folder");
|
|
||||||
return FolderNavigationWidget::tr("Open \"%1\"").arg(fileName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FolderNavigationWidget::contextMenuEvent(QContextMenuEvent *ev)
|
void FolderNavigationWidget::contextMenuEvent(QContextMenuEvent *ev)
|
||||||
{
|
{
|
||||||
QMenu menu;
|
QMenu menu;
|
||||||
// Open current item
|
// Open current item
|
||||||
const QModelIndex current = currentItem();
|
const QModelIndex current = m_listView->currentIndex();
|
||||||
const bool hasCurrentItem = current.isValid();
|
const bool hasCurrentItem = current.isValid();
|
||||||
QAction *actionOpen = menu.addAction(actionOpenText(m_fileSystemModel, current));
|
QAction *actionOpen = nullptr;
|
||||||
actionOpen->setEnabled(hasCurrentItem);
|
if (hasCurrentItem) {
|
||||||
|
const QString fileName = m_fileSystemModel->fileName(current);
|
||||||
|
if (m_fileSystemModel->isDir(current))
|
||||||
|
actionOpen = menu.addAction(tr("Open Project in \"%1\"").arg(fileName));
|
||||||
|
else
|
||||||
|
actionOpen = menu.addAction(tr("Open \"%1\"").arg(fileName));
|
||||||
|
}
|
||||||
|
|
||||||
// we need dummy DocumentModel::Entry with absolute file path in it
|
// we need dummy DocumentModel::Entry with absolute file path in it
|
||||||
// to get EditorManager::addNativeDirAndOpenWithActions() working
|
// to get EditorManager::addNativeDirAndOpenWithActions() working
|
||||||
@@ -344,17 +301,6 @@ void FolderNavigationWidget::contextMenuEvent(QContextMenuEvent *ev)
|
|||||||
fakeEntry.document = &document;
|
fakeEntry.document = &document;
|
||||||
Core::EditorManager::addNativeDirAndOpenWithActions(&menu, &fakeEntry);
|
Core::EditorManager::addNativeDirAndOpenWithActions(&menu, &fakeEntry);
|
||||||
|
|
||||||
const bool isDirectory = hasCurrentItem && m_fileSystemModel->isDir(current);
|
|
||||||
QAction *actionOpenDirectoryAsProject = 0;
|
|
||||||
if (isDirectory && m_fileSystemModel->fileName(current) != QLatin1String("..")) {
|
|
||||||
actionOpenDirectoryAsProject =
|
|
||||||
menu.addAction(tr("Open Project in \"%1\"")
|
|
||||||
.arg(m_fileSystemModel->fileName(current)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open file dialog to choose a path starting from current
|
|
||||||
QAction *actionChooseFolder = menu.addAction(tr("Choose Folder..."));
|
|
||||||
|
|
||||||
QAction *action = menu.exec(ev->globalPos());
|
QAction *action = menu.exec(ev->globalPos());
|
||||||
if (!action)
|
if (!action)
|
||||||
return;
|
return;
|
||||||
@@ -362,12 +308,6 @@ void FolderNavigationWidget::contextMenuEvent(QContextMenuEvent *ev)
|
|||||||
ev->accept();
|
ev->accept();
|
||||||
if (action == actionOpen) { // Handle open file.
|
if (action == actionOpen) { // Handle open file.
|
||||||
openItem(current);
|
openItem(current);
|
||||||
} else if (action == actionOpenDirectoryAsProject) {
|
|
||||||
openItem(current, true);
|
|
||||||
} else if (action == actionChooseFolder) { // Open file dialog
|
|
||||||
const QString newPath = QFileDialog::getExistingDirectory(this, tr("Choose Folder"), currentDirectory());
|
|
||||||
if (!newPath.isEmpty())
|
|
||||||
setCurrentDirectory(newPath);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,17 +327,6 @@ bool FolderNavigationWidget::hiddenFilesFilter() const
|
|||||||
return m_filterHiddenFilesAction->isChecked();
|
return m_filterHiddenFilesAction->isChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FolderNavigationWidget::ensureCurrentIndex()
|
|
||||||
{
|
|
||||||
QModelIndex index = m_listView->currentIndex();
|
|
||||||
if (!index.isValid()
|
|
||||||
|| index.parent() != m_listView->rootIndex()) {
|
|
||||||
index = m_listView->rootIndex().child(0, 0);
|
|
||||||
m_listView->setCurrentIndex(index);
|
|
||||||
}
|
|
||||||
m_listView->scrollTo(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList FolderNavigationWidget::projectFilesInDirectory(const QString &path)
|
QStringList FolderNavigationWidget::projectFilesInDirectory(const QString &path)
|
||||||
{
|
{
|
||||||
QDir dir(path);
|
QDir dir(path);
|
||||||
@@ -410,6 +339,7 @@ QStringList FolderNavigationWidget::projectFilesInDirectory(const QString &path)
|
|||||||
// --------------------FolderNavigationWidgetFactory
|
// --------------------FolderNavigationWidgetFactory
|
||||||
FolderNavigationWidgetFactory::FolderNavigationWidgetFactory()
|
FolderNavigationWidgetFactory::FolderNavigationWidgetFactory()
|
||||||
{
|
{
|
||||||
|
m_instance = this;
|
||||||
setDisplayName(tr("File System"));
|
setDisplayName(tr("File System"));
|
||||||
setPriority(400);
|
setPriority(400);
|
||||||
setId("File System");
|
setId("File System");
|
||||||
@@ -418,8 +348,19 @@ FolderNavigationWidgetFactory::FolderNavigationWidgetFactory()
|
|||||||
|
|
||||||
Core::NavigationView FolderNavigationWidgetFactory::createWidget()
|
Core::NavigationView FolderNavigationWidgetFactory::createWidget()
|
||||||
{
|
{
|
||||||
Core::NavigationView n;
|
|
||||||
auto fnw = new FolderNavigationWidget;
|
auto fnw = new FolderNavigationWidget;
|
||||||
|
for (const DirectoryEntry &root : m_rootDirectories)
|
||||||
|
fnw->addRootDirectory(root.first, root.second);
|
||||||
|
connect(this,
|
||||||
|
&FolderNavigationWidgetFactory::rootDirectoryAdded,
|
||||||
|
fnw,
|
||||||
|
&FolderNavigationWidget::addRootDirectory);
|
||||||
|
connect(this,
|
||||||
|
&FolderNavigationWidgetFactory::rootDirectoryRemoved,
|
||||||
|
fnw,
|
||||||
|
&FolderNavigationWidget::removeRootDirectory);
|
||||||
|
|
||||||
|
Core::NavigationView n;
|
||||||
n.widget = fnw;
|
n.widget = fnw;
|
||||||
auto filter = new QToolButton;
|
auto filter = new QToolButton;
|
||||||
filter->setIcon(Utils::Icons::FILTER.icon());
|
filter->setIcon(Utils::Icons::FILTER.icon());
|
||||||
@@ -450,7 +391,23 @@ void FolderNavigationWidgetFactory::restoreSettings(QSettings *settings, int pos
|
|||||||
fnw->setHiddenFilesFilter(settings->value(baseKey + QLatin1String(".HiddenFilesFilter"), false).toBool());
|
fnw->setHiddenFilesFilter(settings->value(baseKey + QLatin1String(".HiddenFilesFilter"), false).toBool());
|
||||||
fnw->setAutoSynchronization(settings->value(baseKey + QLatin1String(".SyncWithEditor"), true).toBool());
|
fnw->setAutoSynchronization(settings->value(baseKey + QLatin1String(".SyncWithEditor"), true).toBool());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FolderNavigationWidgetFactory::addRootDirectory(const QString &displayName,
|
||||||
|
const Utils::FileName &directory)
|
||||||
|
{
|
||||||
|
m_rootDirectories.append(DirectoryEntry(displayName, directory));
|
||||||
|
emit m_instance->rootDirectoryAdded(displayName, directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderNavigationWidgetFactory::removeRootDirectory(const Utils::FileName &directory)
|
||||||
|
{
|
||||||
|
const int index = Utils::indexOf(m_rootDirectories, [directory](const DirectoryEntry &entry) {
|
||||||
|
return entry.second == directory;
|
||||||
|
});
|
||||||
|
QTC_ASSERT(index >= 0, return);
|
||||||
|
m_rootDirectories.removeAt(index);
|
||||||
|
emit m_instance->rootDirectoryRemoved(directory);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace ProjectExplorer
|
} // namespace ProjectExplorer
|
||||||
|
|
||||||
#include "foldernavigationwidget.moc"
|
|
||||||
|
@@ -29,15 +29,18 @@
|
|||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
namespace Utils { class ListView; }
|
|
||||||
namespace Core { class IEditor; }
|
namespace Core { class IEditor; }
|
||||||
|
|
||||||
|
namespace Utils {
|
||||||
|
class FileName;
|
||||||
|
class NavigationTreeView;
|
||||||
|
}
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QLabel;
|
|
||||||
class QSortFilterProxyModel;
|
|
||||||
class QModelIndex;
|
|
||||||
class QFileSystemModel;
|
|
||||||
class QAction;
|
class QAction;
|
||||||
|
class QComboBox;
|
||||||
|
class QFileSystemModel;
|
||||||
|
class QModelIndex;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace ProjectExplorer {
|
namespace ProjectExplorer {
|
||||||
@@ -58,29 +61,26 @@ public:
|
|||||||
void setAutoSynchronization(bool sync);
|
void setAutoSynchronization(bool sync);
|
||||||
void toggleAutoSynchronization();
|
void toggleAutoSynchronization();
|
||||||
|
|
||||||
private:
|
void addRootDirectory(const QString &displayName, const Utils::FileName &directory);
|
||||||
void setCurrentFile(Core::IEditor *editor);
|
void removeRootDirectory(const Utils::FileName &directory);
|
||||||
void slotOpenItem(const QModelIndex &viewIndex);
|
|
||||||
void setHiddenFilesFilter(bool filter);
|
|
||||||
void ensureCurrentIndex();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void contextMenuEvent(QContextMenuEvent *ev) override;
|
void contextMenuEvent(QContextMenuEvent *ev) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setCurrentTitle(QString dirName, const QString &fullPath);
|
void setHiddenFilesFilter(bool filter);
|
||||||
bool setCurrentDirectory(const QString &directory);
|
void setCurrentEditor(Core::IEditor *editor);
|
||||||
void openItem(const QModelIndex &srcIndex, bool openDirectoryAsProject = false);
|
void selectFile(const Utils::FileName &filePath);
|
||||||
QModelIndex currentItem() const;
|
void setRootDirectory(const Utils::FileName &directory);
|
||||||
QString currentDirectory() const;
|
int bestRootForFile(const Utils::FileName &filePath);
|
||||||
|
void openItem(const QModelIndex &index);
|
||||||
|
|
||||||
Utils::ListView *m_listView;
|
Utils::NavigationTreeView *m_listView = nullptr;
|
||||||
QFileSystemModel *m_fileSystemModel;
|
QFileSystemModel *m_fileSystemModel = nullptr;
|
||||||
QAction *m_filterHiddenFilesAction;
|
QAction *m_filterHiddenFilesAction = nullptr;
|
||||||
QSortFilterProxyModel *m_filterModel;
|
|
||||||
QLabel *m_title;
|
|
||||||
bool m_autoSync = false;
|
bool m_autoSync = false;
|
||||||
QToolButton *m_toggleSync;
|
QToolButton *m_toggleSync = nullptr;
|
||||||
|
QComboBox *m_rootSelector = nullptr;
|
||||||
|
|
||||||
// FolderNavigationWidgetFactory needs private members to build a menu
|
// FolderNavigationWidgetFactory needs private members to build a menu
|
||||||
friend class FolderNavigationWidgetFactory;
|
friend class FolderNavigationWidgetFactory;
|
||||||
@@ -96,6 +96,17 @@ public:
|
|||||||
Core::NavigationView createWidget() override;
|
Core::NavigationView createWidget() override;
|
||||||
void saveSettings(QSettings *settings, int position, QWidget *widget) override;
|
void saveSettings(QSettings *settings, int position, QWidget *widget) override;
|
||||||
void restoreSettings(QSettings *settings, int position, QWidget *widget) override;
|
void restoreSettings(QSettings *settings, int position, QWidget *widget) override;
|
||||||
|
|
||||||
|
static void addRootDirectory(const QString &displayName, const Utils::FileName &directory);
|
||||||
|
static void removeRootDirectory(const Utils::FileName &directory);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void rootDirectoryAdded(const QString &displayName, const Utils::FileName &directory);
|
||||||
|
void rootDirectoryRemoved(const Utils::FileName &directory);
|
||||||
|
|
||||||
|
private:
|
||||||
|
using DirectoryEntry = std::pair<QString, Utils::FileName>;
|
||||||
|
static QVector<DirectoryEntry> m_rootDirectories;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
@@ -30,6 +30,7 @@
|
|||||||
#include "kit.h"
|
#include "kit.h"
|
||||||
#include "buildconfiguration.h"
|
#include "buildconfiguration.h"
|
||||||
#include "deployconfiguration.h"
|
#include "deployconfiguration.h"
|
||||||
|
#include "foldernavigationwidget.h"
|
||||||
#include "projectexplorer.h"
|
#include "projectexplorer.h"
|
||||||
#include "projectnodes.h"
|
#include "projectnodes.h"
|
||||||
#include "editorconfiguration.h"
|
#include "editorconfiguration.h"
|
||||||
@@ -385,6 +386,8 @@ void SessionManager::addProject(Project *pro)
|
|||||||
m_instance, [pro]() { m_instance->projectDisplayNameChanged(pro); });
|
m_instance, [pro]() { m_instance->projectDisplayNameChanged(pro); });
|
||||||
|
|
||||||
emit m_instance->projectAdded(pro);
|
emit m_instance->projectAdded(pro);
|
||||||
|
FolderNavigationWidgetFactory::addRootDirectory(pro->displayName(),
|
||||||
|
pro->projectFilePath().parentDir());
|
||||||
configureEditors(pro);
|
configureEditors(pro);
|
||||||
connect(pro, &Project::fileListChanged, [pro](){ configureEditors(pro); });
|
connect(pro, &Project::fileListChanged, [pro](){ configureEditors(pro); });
|
||||||
}
|
}
|
||||||
@@ -739,6 +742,7 @@ void SessionManager::removeProjects(QList<Project *> remove)
|
|||||||
m_instance, &SessionManager::clearProjectFileCache);
|
m_instance, &SessionManager::clearProjectFileCache);
|
||||||
d->m_projectFileCache.remove(pro);
|
d->m_projectFileCache.remove(pro);
|
||||||
emit m_instance->projectRemoved(pro);
|
emit m_instance->projectRemoved(pro);
|
||||||
|
FolderNavigationWidgetFactory::removeRootDirectory(pro->projectFilePath().parentDir());
|
||||||
delete pro;
|
delete pro;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user