Files
qt-creator/src/plugins/projectexplorer/foldernavigationwidget.cpp

503 lines
17 KiB
C++
Raw Normal View History

/**************************************************************************
2008-12-02 12:01:29 +01:00
**
** This file is part of Qt Creator
**
2010-03-05 11:25:49 +01:00
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
2008-12-02 12:01:29 +01:00
**
** Contact: Nokia Corporation (qt-info@nokia.com)
2008-12-02 12:01:29 +01:00
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
2009-08-14 09:30:56 +02:00
** contact the sales department at http://qt.nokia.com/contact.
2008-12-02 12:01:29 +01:00
**
**************************************************************************/
2008-12-02 16:19:05 +01:00
2008-12-02 12:01:29 +01:00
#include "foldernavigationwidget.h"
#include "projectexplorer.h"
#include "projectexplorerconstants.h"
#include <coreplugin/icore.h>
#include <coreplugin/fileiconprovider.h>
2008-12-02 12:01:29 +01:00
#include <coreplugin/filemanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/coreconstants.h>
2008-12-02 12:01:29 +01:00
#include <utils/environment.h>
2008-12-02 12:01:29 +01:00
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
#include <utils/unixutils.h>
#include <utils/consoleprocess.h>
2008-12-02 12:01:29 +01:00
#include <QtCore/QDebug>
#include <QtCore/QProcess>
#include <QtCore/QSize>
#include <QtGui/QFileSystemModel>
2008-12-02 12:01:29 +01:00
#include <QtGui/QVBoxLayout>
#include <QtGui/QToolButton>
#include <QtGui/QPushButton>
#include <QtGui/QLabel>
#include <QtGui/QListView>
#include <QtGui/QSortFilterProxyModel>
#include <QtGui/QAction>
#include <QtGui/QMenu>
#include <QtGui/QFileDialog>
#include <QtGui/QContextMenuEvent>
#include <QtGui/QMessageBox>
2008-12-02 12:01:29 +01:00
enum { debug = 0 };
2008-12-02 12:01:29 +01:00
namespace ProjectExplorer {
2008-12-02 16:19:05 +01:00
namespace Internal {
// Hide the '.' entry.
class DotRemovalFilter : public QSortFilterProxyModel
2008-12-02 16:19:05 +01:00
{
Q_OBJECT
public:
explicit DotRemovalFilter(QObject *parent = 0);
2008-12-02 16:19:05 +01:00
protected:
virtual bool filterAcceptsRow(int source_row, const QModelIndex &parent) const;
private:
#if defined(Q_OS_UNIX)
const QVariant m_root;
const QVariant m_dotdot;
#endif
const QVariant m_dot;
2008-12-02 16:19:05 +01:00
};
DotRemovalFilter::DotRemovalFilter(QObject *parent) :
QSortFilterProxyModel(parent),
#if defined(Q_OS_UNIX)
m_root(QString(QLatin1Char('/'))),
m_dotdot(QLatin1String("..")),
#endif
m_dot(QString(QLatin1Char('.')))
{
}
bool DotRemovalFilter::filterAcceptsRow(int source_row, const QModelIndex &parent) const
{
const QVariant fileName = sourceModel()->data(parent.child(source_row, 0));
#if defined(Q_OS_UNIX)
if (sourceModel()->data(parent) == m_root && fileName == m_dotdot)
return false;
#endif
return fileName != m_dot;
}
// FolderNavigationModel: Shows path as tooltip.
class FolderNavigationModel : public QFileSystemModel
{
public:
explicit FolderNavigationModel(QObject *parent = 0);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
};
FolderNavigationModel::FolderNavigationModel(QObject *parent) :
QFileSystemModel(parent)
{
}
QVariant FolderNavigationModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::ToolTipRole)
return QDir::toNativeSeparators(QDir::cleanPath(filePath(index)));
else
return QFileSystemModel::data(index, role);
}
2008-12-02 12:01:29 +01:00
/*!
/class FolderNavigationWidget
Shows a file system folder
*/
2009-01-20 17:14:00 +01:00
FolderNavigationWidget::FolderNavigationWidget(QWidget *parent)
: QWidget(parent),
m_listView(new QListView(this)),
m_fileSystemModel(new FolderNavigationModel(this)),
m_filterModel(new DotRemovalFilter(this)),
2009-01-20 17:14:00 +01:00
m_title(new QLabel(this)),
m_autoSync(false)
2008-12-02 12:01:29 +01:00
{
m_fileSystemModel->setResolveSymlinks(false);
m_fileSystemModel->setIconProvider(Core::FileIconProvider::instance());
QDir::Filters filters = QDir::AllDirs | QDir::Files | QDir::Drives
| QDir::Readable| QDir::Writable
| QDir::Executable | QDir::Hidden;
#ifdef Q_OS_WIN // Symlinked directories can cause file watcher warnings on Win32.
filters |= QDir::NoSymLinks;
#endif
m_fileSystemModel->setFilter(filters);
m_filterModel->setSourceModel(m_fileSystemModel);
m_listView->setIconSize(QSize(16,16));
m_listView->setModel(m_filterModel);
m_listView->setFrameStyle(QFrame::NoFrame);
m_listView->setAttribute(Qt::WA_MacShowFocusRect, false);
setFocusProxy(m_listView);
2008-12-02 12:01:29 +01:00
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(m_title);
layout->addWidget(m_listView);
2008-12-02 12:01:29 +01:00
m_title->setMargin(5);
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
setLayout(layout);
// connections
connect(m_listView, SIGNAL(activated(const QModelIndex&)),
this, SLOT(slotOpenItem(const QModelIndex&)));
2008-12-02 12:01:29 +01:00
setAutoSynchronization(true);
}
void FolderNavigationWidget::toggleAutoSynchronization()
{
setAutoSynchronization(!m_autoSync);
}
bool FolderNavigationWidget::autoSynchronization() const
{
return m_autoSync;
}
void FolderNavigationWidget::setAutoSynchronization(bool sync)
{
if (sync == m_autoSync)
return;
m_autoSync = sync;
2009-01-20 17:14:00 +01:00
Core::FileManager *fileManager = Core::ICore::instance()->fileManager();
2008-12-02 12:01:29 +01:00
if (m_autoSync) {
2009-01-20 17:14:00 +01:00
connect(fileManager, SIGNAL(currentFileChanged(QString)),
this, SLOT(setCurrentFile(QString)));
2008-12-02 12:01:29 +01:00
setCurrentFile(fileManager->currentFile());
} else {
2009-01-20 17:14:00 +01:00
disconnect(fileManager, SIGNAL(currentFileChanged(QString)),
this, SLOT(setCurrentFile(QString)));
2008-12-02 12:01:29 +01:00
}
}
void FolderNavigationWidget::setCurrentFile(const QString &filePath)
{
// Try to find directory of current file
bool pathOpened = false;
if (!filePath.isEmpty()) {
const QFileInfo fi(filePath);
if (fi.exists())
pathOpened = setCurrentDirectory(fi.absolutePath());
}
if (!pathOpened) // Default to home.
setCurrentDirectory(Utils::PathChooser::homePath());
2008-12-02 12:01:29 +01:00
// Select the current file.
if (pathOpened) {
const QModelIndex fileIndex = m_fileSystemModel->index(filePath);
2008-12-02 12:01:29 +01:00
if (fileIndex.isValid()) {
QItemSelectionModel *selections = m_listView->selectionModel();
const QModelIndex mainIndex = m_filterModel->mapFromSource(fileIndex);
2008-12-02 12:01:29 +01:00
selections->setCurrentIndex(mainIndex, QItemSelectionModel::SelectCurrent
| QItemSelectionModel::Clear);
m_listView->scrollTo(mainIndex);
2008-12-02 12:01:29 +01:00
}
}
}
bool FolderNavigationWidget::setCurrentDirectory(const QString &directory)
{
const QString newDirectory = directory.isEmpty() ? QDir::rootPath() : directory;
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;
}
m_listView->setRootIndex(m_filterModel->mapFromSource(index));
const QDir current(QDir::cleanPath(newDirectory));
setCurrentTitle(current.dirName(),
QDir::toNativeSeparators(current.absolutePath()));
return !directory.isEmpty();
2008-12-02 12:01:29 +01:00
}
QString FolderNavigationWidget::currentDirectory() const
2008-12-02 12:01:29 +01:00
{
return m_fileSystemModel->rootPath();
}
void FolderNavigationWidget::slotOpenItem(const QModelIndex &viewIndex)
{
if (viewIndex.isValid())
openItem(m_filterModel->mapToSource(viewIndex));
}
void FolderNavigationWidget::openItem(const QModelIndex &srcIndex)
{
const QString fileName = m_fileSystemModel->fileName(srcIndex);
if (fileName == QLatin1String("."))
return;
if (fileName == QLatin1String("..")) {
// cd up: Special behaviour: The fileInfo of ".." is that of the parent directory.
const QString parentPath = m_fileSystemModel->fileInfo(srcIndex).absoluteFilePath();
setCurrentDirectory(parentPath);
return;
}
if (m_fileSystemModel->isDir(srcIndex)) { // Change to directory
const QFileInfo fi = m_fileSystemModel->fileInfo(srcIndex);
if (fi.isReadable() && fi.isExecutable())
setCurrentDirectory(m_fileSystemModel->filePath(srcIndex));
return;
2008-12-02 12:01:29 +01:00
}
// Open file.
Core::EditorManager *editorManager = Core::EditorManager::instance();
editorManager->openEditor(m_fileSystemModel->filePath(srcIndex), QString(), Core::EditorManager::ModeSwitch);
2008-12-02 12:01:29 +01:00
}
void FolderNavigationWidget::setCurrentTitle(QString dirName, const QString &fullPath)
2008-12-02 12:01:29 +01:00
{
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(".."))
2010-09-21 17:17:54 +02:00
return FolderNavigationWidget::tr("Open Parent Folder");
return FolderNavigationWidget::tr("Open \"%1\"").arg(fileName);
}
void FolderNavigationWidget::contextMenuEvent(QContextMenuEvent *ev)
{
QMenu menu;
// Open current item
const QModelIndex current = currentItem();
const bool hasCurrentItem = current.isValid();
QAction *actionOpen = menu.addAction(actionOpenText(m_fileSystemModel, current));
actionOpen->setEnabled(hasCurrentItem);
// Explorer & teminal
QAction *actionExplorer = menu.addAction(msgGraphicalShellAction());
actionExplorer->setEnabled(hasCurrentItem);
QAction *actionTerminal = menu.addAction(msgTerminalAction());
actionTerminal->setEnabled(hasCurrentItem);
// open with...
if (!m_fileSystemModel->isDir(current)) {
QMenu *openWith = menu.addMenu(tr("Open with"));
ProjectExplorerPlugin::populateOpenWithMenu(openWith,
m_fileSystemModel->filePath(current));
}
// Open file dialog to choose a path starting from current
2010-09-21 17:17:54 +02:00
QAction *actionChooseFolder = menu.addAction(tr("Choose Folder..."));
QAction *action = menu.exec(ev->globalPos());
if (!action)
return;
ev->accept();
if (action == actionOpen) { // Handle open file.
openItem(current);
return;
}
if (action == actionChooseFolder) { // Open file dialog
2010-09-21 17:17:54 +02:00
const QString newPath = QFileDialog::getExistingDirectory(this, tr("Choose Folder"), currentDirectory());
if (!newPath.isEmpty())
setCurrentDirectory(newPath);
return;
}
if (action == actionTerminal) {
openTerminal(m_fileSystemModel->filePath(current));
return;
}
if (action == actionExplorer) {
showInGraphicalShell(this, m_fileSystemModel->filePath(current));
return;
}
ProjectExplorerPlugin::openEditorFromAction(action,
m_fileSystemModel->filePath(current));
}
QString FolderNavigationWidget::msgGraphicalShellAction()
{
#if defined(Q_OS_WIN)
return tr("Show in Explorer...");
#elif defined(Q_OS_MAC)
return tr("Show in Finder...");
#else
2010-09-21 17:17:54 +02:00
return tr("Show Containing Folder...");
#endif
}
QString FolderNavigationWidget::msgTerminalAction()
{
#ifdef Q_OS_WIN
2010-09-21 17:17:54 +02:00
return tr("Open Command Prompt Here...");
#else
2010-09-21 17:17:54 +02:00
return tr("Open Terminal Here...");
#endif
}
// Show error with option to open settings.
static inline void showGraphicalShellError(QWidget *parent,
const QString &app,
const QString &error)
{
const QString title = FolderNavigationWidget::tr("Launching a file browser failed");
const QString msg = FolderNavigationWidget::tr("Unable to start the file manager:\n\n%1\n\n").arg(app);
QMessageBox mbox(QMessageBox::Warning, title, msg, QMessageBox::Close, parent);
if (!error.isEmpty())
mbox.setDetailedText(FolderNavigationWidget::tr("'%1' returned the following error:\n\n%2").arg(app, error));
QAbstractButton *settingsButton = mbox.addButton(FolderNavigationWidget::tr("Settings..."), QMessageBox::ActionRole);
mbox.exec();
if (mbox.clickedButton() == settingsButton)
Core::ICore::instance()->showOptionsDialog(QLatin1String(Core::Constants::SETTINGS_CATEGORY_CORE),
QLatin1String(Core::Constants::SETTINGS_ID_ENVIRONMENT));
}
void FolderNavigationWidget::showInGraphicalShell(QWidget *parent, const QString &pathIn)
{
// Mac, Windows support folder or file.
#if defined(Q_OS_WIN)
const QString explorer = Utils::Environment::systemEnvironment().searchInPath(QLatin1String("explorer.exe"));
if (explorer.isEmpty()) {
QMessageBox::warning(parent,
2010-09-21 17:17:54 +02:00
tr("Launching Windows Explorer Failed"),
tr("Could not find explorer.exe in path to launch Windows Explorer."));
return;
}
QString param;
if (!QFileInfo(pathIn).isDir())
param = QLatin1String("/select,");
param += QDir::toNativeSeparators(pathIn);
QProcess::startDetached(explorer, QStringList(param));
#elif defined(Q_OS_MAC)
Q_UNUSED(parent)
QStringList scriptArgs;
scriptArgs << QLatin1String("-e")
<< QString::fromLatin1("tell application \"Finder\" to reveal POSIX file \"%1\"")
.arg(pathIn);
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
scriptArgs.clear();
scriptArgs << QLatin1String("-e")
<< QLatin1String("tell application \"Finder\" to activate");
QProcess::execute("/usr/bin/osascript", scriptArgs);
#else
// we cannot select a file here, because no file browser really supports it...
const QFileInfo fileInfo(pathIn);
const QString folder = fileInfo.absoluteFilePath();
const QString app = Utils::UnixUtils::fileBrowser(Core::ICore::instance()->settings());
QProcess browserProc;
const QString browserArgs = Utils::UnixUtils::substituteFileBrowserParameters(app, folder);
if (debug)
qDebug() << browserArgs;
bool success = browserProc.startDetached(browserArgs);
const QString error = QString::fromLocal8Bit(browserProc.readAllStandardError());
success = success && error.isEmpty();
if (!success)
showGraphicalShellError(parent, app, error);
#endif
}
void FolderNavigationWidget::openTerminal(const QString &path)
{
// Get terminal application
#ifdef Q_OS_WIN
const QString terminalEmulator = QString::fromLocal8Bit(qgetenv("COMSPEC"));
const QStringList args; // none
#else
QStringList args = Utils::ConsoleProcess::terminalEmulator(
Core::ICore::instance()->settings()).split(QLatin1Char(' '));
const QString terminalEmulator = args.takeFirst();
const QString shell = QString::fromLocal8Bit(qgetenv("SHELL"));
args.append(shell);
#endif
// Launch terminal with working directory set.
const QFileInfo fileInfo(path);
const QString pwd = QDir::toNativeSeparators(fileInfo.isDir() ?
fileInfo.absoluteFilePath() :
fileInfo.absolutePath());
QProcess::startDetached(terminalEmulator, args, pwd);
2008-12-02 12:01:29 +01:00
}
// --------------------FolderNavigationWidgetFactory
2009-01-20 17:14:00 +01:00
FolderNavigationWidgetFactory::FolderNavigationWidgetFactory()
2008-12-02 12:01:29 +01:00
{
}
FolderNavigationWidgetFactory::~FolderNavigationWidgetFactory()
{
}
QString FolderNavigationWidgetFactory::displayName() const
2008-12-02 12:01:29 +01:00
{
return tr("File System");
}
int FolderNavigationWidgetFactory::priority() const
{
return 400;
}
QString FolderNavigationWidgetFactory::id() const
{
return QLatin1String("File System");
}
QKeySequence FolderNavigationWidgetFactory::activationSequence() const
2008-12-02 12:01:29 +01:00
{
return QKeySequence(Qt::ALT + Qt::Key_Y);
}
Core::NavigationView FolderNavigationWidgetFactory::createWidget()
{
Core::NavigationView n;
2009-01-20 17:14:00 +01:00
FolderNavigationWidget *ptw = new FolderNavigationWidget;
2008-12-02 12:01:29 +01:00
n.widget = ptw;
QToolButton *toggleSync = new QToolButton;
toggleSync->setIcon(QIcon(QLatin1String(Core::Constants::ICON_LINK)));
2008-12-02 12:01:29 +01:00
toggleSync->setCheckable(true);
toggleSync->setChecked(ptw->autoSynchronization());
toggleSync->setToolTip(tr("Synchronize with Editor"));
connect(toggleSync, SIGNAL(clicked(bool)), ptw, SLOT(toggleAutoSynchronization()));
n.dockToolBarWidgets << toggleSync;
2008-12-02 12:01:29 +01:00
return n;
}
} // namespace Internal
} // namespace ProjectExplorer
2008-12-02 12:01:29 +01:00
#include "foldernavigationwidget.moc"