Newer
Older
/**************************************************************************
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Nokia Corporation (qt-info@nokia.com)
** 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
**************************************************************************/
#include "foldernavigationwidget.h"
#include "projectexplorer.h"
#include "projectexplorerconstants.h"
#include "environment.h"
#include <coreplugin/fileiconprovider.h>
#include <coreplugin/filemanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/coreconstants.h>
#include <utils/qtcassert.h>
#include <utils/unixutils.h>
#include <utils/consoleprocess.h>
#include <QtCore/QProcess>
#include <QtGui/QFileSystemModel>
#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>
// Hide the '.' entry.
class DotRemovalFilter : public QSortFilterProxyModel
explicit DotRemovalFilter(QObject *parent = 0);
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
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);
}
/*!
/class FolderNavigationWidget
Shows a file system folder
*/
FolderNavigationWidget::FolderNavigationWidget(QWidget *parent)
: QWidget(parent),
m_listView(new QListView(this)),
m_fileSystemModel(new FolderNavigationModel(this)),
m_filterModel(new DotRemovalFilter(this)),

Robert Loehning
committed
m_autoSync(false)
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->setModel(m_filterModel);
m_listView->setFrameStyle(QFrame::NoFrame);
m_listView->setAttribute(Qt::WA_MacShowFocusRect, false);
setFocusProxy(m_listView);
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(m_title);
layout->addWidget(m_listView);
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&)));
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;
Core::FileManager *fileManager = Core::ICore::instance()->fileManager();
connect(fileManager, SIGNAL(currentFileChanged(QString)),
this, SLOT(setCurrentFile(QString)));
disconnect(fileManager, SIGNAL(currentFileChanged(QString)),
this, SLOT(setCurrentFile(QString)));
}
}
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());
// Select the current file.
if (pathOpened) {
const QModelIndex fileIndex = m_fileSystemModel->index(filePath);
QItemSelectionModel *selections = m_listView->selectionModel();
const QModelIndex mainIndex = m_filterModel->mapFromSource(fileIndex);
selections->setCurrentIndex(mainIndex, QItemSelectionModel::SelectCurrent
| QItemSelectionModel::Clear);
m_listView->scrollTo(mainIndex);
}
}
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(), current.absolutePath());
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)
{
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();
if (parentPath != QDir::rootPath())
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;
// Open file.
Core::EditorManager *editorManager = Core::EditorManager::instance();
editorManager->openEditor(m_fileSystemModel->filePath(srcIndex));
void FolderNavigationWidget::setCurrentTitle(const QString &dirName, const QString &fullPath)
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
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)
{
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
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
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));
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
}
QString FolderNavigationWidget::msgGraphicalShellAction()
{
#if defined(Q_OS_WIN)
return tr("Show in Explorer...");
#elif defined(Q_OS_MAC)
return tr("Show in Finder...");
#else
return tr("Show containing folder...");
#endif
}
QString FolderNavigationWidget::msgTerminalAction()
{
#ifdef Q_OS_WIN
return tr("Open Command Prompt here...");
#else
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 = Environment::systemEnvironment().searchInPath(QLatin1String("explorer.exe"));
if (explorer.isEmpty()) {
QMessageBox::warning(parent,
tr("Launching Windows Explorer failed"),
tr("Could not find explorer.exe in path to launch Windows Explorer."));
return;
}

Robert Loehning
committed
QString param;
if (!QFileInfo(pathIn).isDir())
param = QLatin1String("/select,");
param += QDir::toNativeSeparators(pathIn);
QProcess::startDetached(explorer, QStringList(param));
#elif defined(Q_OS_MAC)

Robert Loehning
committed
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);

Robert Loehning
committed
const QString folder = fileInfo.absoluteFilePath();
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
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);
// --------------------FolderNavigationWidgetFactory
FolderNavigationWidgetFactory::FolderNavigationWidgetFactory()
{
}
FolderNavigationWidgetFactory::~FolderNavigationWidgetFactory()
{
}
QString FolderNavigationWidgetFactory::displayName() const
QString FolderNavigationWidgetFactory::id() const
{
return QLatin1String("File System");
}
QKeySequence FolderNavigationWidgetFactory::activationSequence() const
{
return QKeySequence(Qt::ALT + Qt::Key_Y);
}
Core::NavigationView FolderNavigationWidgetFactory::createWidget()
{
Core::NavigationView n;
FolderNavigationWidget *ptw = new FolderNavigationWidget;
toggleSync->setIcon(QIcon(QLatin1String(":/core/images/linkicon.png")));
toggleSync->setCheckable(true);
toggleSync->setChecked(ptw->autoSynchronization());
toggleSync->setToolTip(tr("Synchronize with Editor"));
connect(toggleSync, SIGNAL(clicked(bool)), ptw, SLOT(toggleAutoSynchronization()));
} // namespace Internal
} // namespace ProjectExplorer