foldernavigationwidget.cpp 12.9 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
con's avatar
con committed
8
**
9
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
hjk's avatar
hjk committed
32

con's avatar
con committed
33 34 35 36 37
#include "foldernavigationwidget.h"
#include "projectexplorer.h"
#include "projectexplorerconstants.h"

#include <coreplugin/icore.h>
38
#include <coreplugin/fileiconprovider.h>
con's avatar
con committed
39 40
#include <coreplugin/filemanager.h>
#include <coreplugin/editormanager/editormanager.h>
41
#include <coreplugin/coreconstants.h>
Robert Loehning's avatar
Robert Loehning committed
42
#include <coreplugin/fileutils.h>
con's avatar
con committed
43 44

#include <utils/pathchooser.h>
45
#include <utils/qtcassert.h>
con's avatar
con committed
46 47

#include <QtCore/QDebug>
48
#include <QtCore/QSize>
49
#include <QtGui/QFileSystemModel>
con's avatar
con committed
50 51
#include <QtGui/QVBoxLayout>
#include <QtGui/QToolButton>
52
#include <QtGui/QPushButton>
53 54 55
#include <QtGui/QLabel>
#include <QtGui/QListView>
#include <QtGui/QSortFilterProxyModel>
56 57 58 59
#include <QtGui/QAction>
#include <QtGui/QMenu>
#include <QtGui/QFileDialog>
#include <QtGui/QContextMenuEvent>
con's avatar
con committed
60

61
enum { debug = 0 };
con's avatar
con committed
62 63

namespace ProjectExplorer {
hjk's avatar
hjk committed
64 65
namespace Internal {

66 67
// Hide the '.' entry.
class DotRemovalFilter : public QSortFilterProxyModel
hjk's avatar
hjk committed
68 69 70
{
    Q_OBJECT
public:
71
    explicit DotRemovalFilter(QObject *parent = 0);
hjk's avatar
hjk committed
72
protected:
73 74
    virtual bool filterAcceptsRow(int source_row, const QModelIndex &parent) const;
private:
75 76 77 78
#if defined(Q_OS_UNIX)
    const QVariant m_root;
    const QVariant m_dotdot;
#endif
79
    const QVariant m_dot;
hjk's avatar
hjk committed
80 81
};

82 83
DotRemovalFilter::DotRemovalFilter(QObject *parent) :
    QSortFilterProxyModel(parent),
84 85 86 87
#if defined(Q_OS_UNIX)
    m_root(QString(QLatin1Char('/'))),
    m_dotdot(QLatin1String("..")),
#endif
88 89 90 91 92 93
    m_dot(QString(QLatin1Char('.')))
{
}

bool DotRemovalFilter::filterAcceptsRow(int source_row, const QModelIndex &parent) const
{
94 95 96 97 98 99
    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;
100 101
}

102
// FolderNavigationModel: Shows path as tooltip.
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
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);
}

con's avatar
con committed
123 124 125 126 127
/*!
  /class FolderNavigationWidget

  Shows a file system folder
  */
hjk's avatar
hjk committed
128 129
FolderNavigationWidget::FolderNavigationWidget(QWidget *parent)
    : QWidget(parent),
130
      m_listView(new QListView(this)),
131
      m_fileSystemModel(new FolderNavigationModel(this)),
132
      m_filterModel(new DotRemovalFilter(this)),
hjk's avatar
hjk committed
133
      m_title(new QLabel(this)),
134
      m_autoSync(false)
con's avatar
con committed
135
{
136
    m_fileSystemModel->setResolveSymlinks(false);
137
    m_fileSystemModel->setIconProvider(Core::FileIconProvider::instance());
138 139 140 141 142 143 144 145
    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);
146
    m_listView->setIconSize(QSize(16,16));
147 148 149 150
    m_listView->setModel(m_filterModel);
    m_listView->setFrameStyle(QFrame::NoFrame);
    m_listView->setAttribute(Qt::WA_MacShowFocusRect, false);
    setFocusProxy(m_listView);
con's avatar
con committed
151 152 153

    QVBoxLayout *layout = new QVBoxLayout();
    layout->addWidget(m_title);
154
    layout->addWidget(m_listView);
con's avatar
con committed
155 156 157 158 159 160
    m_title->setMargin(5);
    layout->setSpacing(0);
    layout->setContentsMargins(0, 0, 0, 0);
    setLayout(layout);

    // connections
161 162
    connect(m_listView, SIGNAL(activated(const QModelIndex&)),
            this, SLOT(slotOpenItem(const QModelIndex&)));
con's avatar
con committed
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183

    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;

hjk's avatar
hjk committed
184
    Core::FileManager *fileManager = Core::ICore::instance()->fileManager();
con's avatar
con committed
185
    if (m_autoSync) {
hjk's avatar
hjk committed
186 187
        connect(fileManager, SIGNAL(currentFileChanged(QString)),
                this, SLOT(setCurrentFile(QString)));
con's avatar
con committed
188 189
        setCurrentFile(fileManager->currentFile());
    } else {
hjk's avatar
hjk committed
190 191
        disconnect(fileManager, SIGNAL(currentFileChanged(QString)),
                this, SLOT(setCurrentFile(QString)));
con's avatar
con committed
192 193 194 195 196
    }
}

void FolderNavigationWidget::setCurrentFile(const QString &filePath)
{
197 198 199 200 201 202 203 204 205
    // 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());
con's avatar
con committed
206

207 208 209
    // Select the current file.
    if (pathOpened) {
        const QModelIndex fileIndex = m_fileSystemModel->index(filePath);
con's avatar
con committed
210
        if (fileIndex.isValid()) {
211 212
            QItemSelectionModel *selections = m_listView->selectionModel();
            const QModelIndex mainIndex = m_filterModel->mapFromSource(fileIndex);
con's avatar
con committed
213 214
            selections->setCurrentIndex(mainIndex, QItemSelectionModel::SelectCurrent
                                                 | QItemSelectionModel::Clear);
215
            m_listView->scrollTo(mainIndex);
con's avatar
con committed
216
        }
217 218 219 220 221
    }
}

bool FolderNavigationWidget::setCurrentDirectory(const QString &directory)
{
222 223 224 225 226 227
    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);
228 229 230 231
    if (!index.isValid()) {
        setCurrentTitle(QString(), QString());
        return false;
    }
232
    m_listView->setRootIndex(m_filterModel->mapFromSource(index));
233
    const QDir current(QDir::cleanPath(newDirectory));
234 235
    setCurrentTitle(current.dirName(),
                    QDir::toNativeSeparators(current.absolutePath()));
236
    return !directory.isEmpty();
con's avatar
con committed
237 238
}

239
QString FolderNavigationWidget::currentDirectory() const
con's avatar
con committed
240
{
241
    return m_fileSystemModel->rootPath();
242 243 244 245 246 247 248 249 250 251 252 253 254
}

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;
255 256 257
    if (fileName == QLatin1String("..")) {
        // cd up: Special behaviour: The fileInfo of ".." is that of the parent directory.
        const QString parentPath = m_fileSystemModel->fileInfo(srcIndex).absoluteFilePath();
258
        setCurrentDirectory(parentPath);
259 260 261
        return;
    }
    if (m_fileSystemModel->isDir(srcIndex)) { // Change to directory
262 263 264
        const QFileInfo fi = m_fileSystemModel->fileInfo(srcIndex);
        if (fi.isReadable() && fi.isExecutable())
            setCurrentDirectory(m_fileSystemModel->filePath(srcIndex));
265
        return;
con's avatar
con committed
266
    }
267 268
    // Open file.
    Core::EditorManager *editorManager = Core::EditorManager::instance();
269
    editorManager->openEditor(m_fileSystemModel->filePath(srcIndex), QString(), Core::EditorManager::ModeSwitch);
con's avatar
con committed
270 271
}

272
void FolderNavigationWidget::setCurrentTitle(QString dirName, const QString &fullPath)
con's avatar
con committed
273
{
274 275
    if (dirName.isEmpty())
        dirName = fullPath;
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
    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(".."))
Leena Miettinen's avatar
Leena Miettinen committed
297
        return FolderNavigationWidget::tr("Open Parent Folder");
298 299 300 301 302 303 304 305
    return FolderNavigationWidget::tr("Open \"%1\"").arg(fileName);
}

void FolderNavigationWidget::contextMenuEvent(QContextMenuEvent *ev)
{
    QMenu menu;
    // Open current item
    const QModelIndex current = currentItem();
306
    const bool hasCurrentItem = current.isValid();
307
    QAction *actionOpen = menu.addAction(actionOpenText(m_fileSystemModel, current));
308 309
    actionOpen->setEnabled(hasCurrentItem);
    // Explorer & teminal
Robert Loehning's avatar
Robert Loehning committed
310
    QAction *actionExplorer = menu.addAction(Core::Internal::FileUtils::msgGraphicalShellAction());
311
    actionExplorer->setEnabled(hasCurrentItem);
Robert Loehning's avatar
Robert Loehning committed
312
    QAction *actionTerminal = menu.addAction(Core::Internal::FileUtils::msgTerminalAction());
313
    actionTerminal->setEnabled(hasCurrentItem);
314 315 316 317 318 319 320 321

    // open with...
    if (!m_fileSystemModel->isDir(current)) {
        QMenu *openWith = menu.addMenu(tr("Open with"));
        ProjectExplorerPlugin::populateOpenWithMenu(openWith,
                                                    m_fileSystemModel->filePath(current));
    }

322
    // Open file dialog to choose a path starting from current
Leena Miettinen's avatar
Leena Miettinen committed
323
    QAction *actionChooseFolder = menu.addAction(tr("Choose Folder..."));
324 325 326 327 328 329 330 331 332 333 334

    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
Leena Miettinen's avatar
Leena Miettinen committed
335
        const QString newPath = QFileDialog::getExistingDirectory(this, tr("Choose Folder"), currentDirectory());
336 337
        if (!newPath.isEmpty())
            setCurrentDirectory(newPath);
338 339 340
        return;
    }
    if (action == actionTerminal) {
Robert Loehning's avatar
Robert Loehning committed
341
        Core::Internal::FileUtils::openTerminal(m_fileSystemModel->filePath(current));
342 343 344
        return;
    }
    if (action == actionExplorer) {
Robert Loehning's avatar
Robert Loehning committed
345
        Core::Internal::FileUtils::showInGraphicalShell(this, m_fileSystemModel->filePath(current));
346 347
        return;
    }
348 349
    ProjectExplorerPlugin::openEditorFromAction(action,
                                                m_fileSystemModel->filePath(current));
350 351
}

352
// --------------------FolderNavigationWidgetFactory
hjk's avatar
hjk committed
353
FolderNavigationWidgetFactory::FolderNavigationWidgetFactory()
con's avatar
con committed
354 355 356 357 358 359 360
{
}

FolderNavigationWidgetFactory::~FolderNavigationWidgetFactory()
{
}

361
QString FolderNavigationWidgetFactory::displayName() const
con's avatar
con committed
362 363 364 365
{
    return tr("File System");
}

366 367 368 369 370
int FolderNavigationWidgetFactory::priority() const
{
    return 400;
}

371 372 373 374 375 376
QString FolderNavigationWidgetFactory::id() const
{
    return QLatin1String("File System");
}

QKeySequence FolderNavigationWidgetFactory::activationSequence() const
con's avatar
con committed
377 378 379 380 381 382 383
{
    return QKeySequence(Qt::ALT + Qt::Key_Y);
}

Core::NavigationView FolderNavigationWidgetFactory::createWidget()
{
    Core::NavigationView n;
hjk's avatar
hjk committed
384
    FolderNavigationWidget *ptw = new FolderNavigationWidget;
con's avatar
con committed
385 386
    n.widget = ptw;
    QToolButton *toggleSync = new QToolButton;
387
    toggleSync->setIcon(QIcon(QLatin1String(Core::Constants::ICON_LINK)));
con's avatar
con committed
388 389 390 391
    toggleSync->setCheckable(true);
    toggleSync->setChecked(ptw->autoSynchronization());
    toggleSync->setToolTip(tr("Synchronize with Editor"));
    connect(toggleSync, SIGNAL(clicked(bool)), ptw, SLOT(toggleAutoSynchronization()));
Roberto Raggi's avatar
Roberto Raggi committed
392
    n.dockToolBarWidgets << toggleSync;
con's avatar
con committed
393 394 395
    return n;
}

396 397 398
} // namespace Internal
} // namespace ProjectExplorer

con's avatar
con committed
399
#include "foldernavigationwidget.moc"