foldernavigationwidget.cpp 21.2 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
con's avatar
con committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
hjk's avatar
hjk committed
25

con's avatar
con committed
26
#include "foldernavigationwidget.h"
27
#include "projectexplorer.h"
28
#include "projectexplorericons.h"
con's avatar
con committed
29

30
#include <coreplugin/actionmanager/command.h>
31
#include <coreplugin/diffservice.h>
32
#include <coreplugin/documentmanager.h>
con's avatar
con committed
33
#include <coreplugin/icore.h>
34
#include <coreplugin/idocument.h>
35
#include <coreplugin/fileiconprovider.h>
con's avatar
con committed
36
#include <coreplugin/editormanager/editormanager.h>
37
#include <coreplugin/editormanager/ieditor.h>
Robert Loehning's avatar
Robert Loehning committed
38
#include <coreplugin/fileutils.h>
39

40 41 42 43
#include <extensionsystem/pluginmanager.h>

#include <texteditor/textdocument.h>

44
#include <utils/algorithm.h>
45
#include <utils/filecrumblabel.h>
46
#include <utils/hostosinfo.h>
47
#include <utils/qtcassert.h>
48
#include <utils/navigationtreeview.h>
Ulf Hermann's avatar
Ulf Hermann committed
49
#include <utils/utilsicons.h>
con's avatar
con committed
50

51 52
#include <QComboBox>
#include <QHeaderView>
53
#include <QScrollBar>
54
#include <QSize>
55
#include <QTimer>
56 57 58 59 60 61
#include <QFileSystemModel>
#include <QVBoxLayout>
#include <QToolButton>
#include <QAction>
#include <QMenu>
#include <QContextMenuEvent>
62 63
#include <QDir>
#include <QFileInfo>
con's avatar
con committed
64

65 66
const int PATH_ROLE = Qt::UserRole;
const int ID_ROLE = Qt::UserRole + 1;
67
const int SORT_ROLE = Qt::UserRole + 2;
68

69 70
const char PROJECTSDIRECTORYROOT_ID[] = "A.Projects";

con's avatar
con committed
71
namespace ProjectExplorer {
hjk's avatar
hjk committed
72 73
namespace Internal {

74
static FolderNavigationWidgetFactory *m_instance = nullptr;
75

76
QVector<FolderNavigationWidgetFactory::RootDirectory>
77
    FolderNavigationWidgetFactory::m_rootDirectories;
78

79 80 81 82 83 84 85 86

static QWidget *createHLine()
{
    auto widget = new QFrame;
    widget->setFrameStyle(QFrame::Plain | QFrame::HLine);
    return widget;
}

87
// FolderNavigationModel: Shows path as tooltip.
88 89 90
class FolderNavigationModel : public QFileSystemModel
{
public:
91
    explicit FolderNavigationModel(QObject *parent = nullptr);
92
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
93
    Qt::DropActions supportedDragActions() const;
94 95
};

96 97
FolderNavigationModel::FolderNavigationModel(QObject *parent) : QFileSystemModel(parent)
{ }
98 99 100 101 102 103 104 105 106

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);
}

107 108 109 110 111
Qt::DropActions FolderNavigationModel::supportedDragActions() const
{
    return Qt::MoveAction;
}

112 113 114 115 116 117 118
static void showOnlyFirstColumn(QTreeView *view)
{
    const int columnCount = view->header()->count();
    for (int i = 1; i < columnCount; ++i)
        view->setColumnHidden(i, true);
}

119 120 121 122 123 124 125 126 127 128 129 130 131
static bool isChildOf(const QModelIndex &index, const QModelIndex &parent)
{
    if (index == parent)
        return true;
    QModelIndex current = index;
    while (current.isValid()) {
        current = current.parent();
        if (current == parent)
            return true;
    }
    return false;
}

con's avatar
con committed
132
/*!
133 134 135
    \class FolderNavigationWidget

    Shows a file system tree, with the root directory selectable from a dropdown.
con's avatar
con committed
136

137 138
    \internal
*/
139
FolderNavigationWidget::FolderNavigationWidget(QWidget *parent) : QWidget(parent),
140
    m_listView(new Utils::NavigationTreeView(this)),
141 142
    m_fileSystemModel(new FolderNavigationModel(this)),
    m_filterHiddenFilesAction(new QAction(tr("Show Hidden Files"), this)),
143
    m_toggleSync(new QToolButton(this)),
144 145
    m_rootSelector(new QComboBox),
    m_crumbLabel(new Utils::FileCrumbLabel(this))
con's avatar
con committed
146
{
147 148
    setBackgroundRole(QPalette::Base);
    setAutoFillBackground(true);
149
    m_fileSystemModel->setResolveSymlinks(false);
150
    m_fileSystemModel->setIconProvider(Core::FileIconProvider::iconProvider());
151
    QDir::Filters filters = QDir::AllEntries | QDir::NoDotAndDotDot;
152 153
    if (Utils::HostOsInfo::isWindowsHost()) // Symlinked directories can cause file watcher warnings on Win32.
        filters |= QDir::NoSymLinks;
154
    m_fileSystemModel->setFilter(filters);
155
    m_fileSystemModel->setRootPath(QString());
156 157
    m_filterHiddenFilesAction->setCheckable(true);
    setHiddenFilesFilter(false);
158
    m_listView->setIconSize(QSize(16,16));
159
    m_listView->setModel(m_fileSystemModel);
160 161
    m_listView->setDragEnabled(true);
    m_listView->setDragDropMode(QAbstractItemView::DragOnly);
162
    showOnlyFirstColumn(m_listView);
163
    setFocusProxy(m_listView);
con's avatar
con committed
164

165 166 167 168 169 170
    auto selectorWidget = new QWidget(this);
    auto selectorLayout = new QVBoxLayout(selectorWidget);
    selectorWidget->setLayout(selectorLayout);
    selectorLayout->setContentsMargins(0, 0, 0, 0);
    selectorLayout->addWidget(m_rootSelector);

171 172 173 174 175 176
    auto crumbLayout = new QVBoxLayout;
    crumbLayout->setSpacing(0);
    crumbLayout->setContentsMargins(4, 4, 4, 4);
    crumbLayout->addWidget(m_crumbLabel);
    m_crumbLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);

177
    auto layout = new QVBoxLayout();
178
    layout->addWidget(selectorWidget);
179 180
    layout->addLayout(crumbLayout);
    layout->addWidget(createHLine());
181
    layout->addWidget(m_listView);
con's avatar
con committed
182 183 184 185
    layout->setSpacing(0);
    layout->setContentsMargins(0, 0, 0, 0);
    setLayout(layout);

Ulf Hermann's avatar
Ulf Hermann committed
186
    m_toggleSync->setIcon(Utils::Icons::LINK.icon());
187 188 189 190
    m_toggleSync->setCheckable(true);
    m_toggleSync->setToolTip(tr("Synchronize with Editor"));
    setAutoSynchronization(true);

con's avatar
con committed
191
    // connections
192
    connect(m_listView, &QAbstractItemView::activated,
193
            this, [this](const QModelIndex &index) { openItem(index); });
194 195 196 197 198
    connect(m_listView->selectionModel(),
            &QItemSelectionModel::currentChanged,
            this,
            [this](const QModelIndex &current, const QModelIndex &) {
                m_crumbLabel->setPath(
199
                    Utils::FileName::fromString(m_fileSystemModel->filePath(current)));
200 201 202 203 204 205 206 207 208 209 210 211
            });
    connect(m_crumbLabel, &Utils::FileCrumbLabel::pathClicked, [this](const Utils::FileName &path) {
        const QModelIndex rootIndex = m_listView->rootIndex();
        const QModelIndex fileIndex = m_fileSystemModel->index(path.toString());
        if (!isChildOf(fileIndex, rootIndex))
            selectBestRootForFile(path);
        selectFile(path);
    });
    connect(m_filterHiddenFilesAction,
            &QAction::toggled,
            this,
            &FolderNavigationWidget::setHiddenFilesFilter);
212 213
    connect(m_toggleSync, &QAbstractButton::clicked,
            this, &FolderNavigationWidget::toggleAutoSynchronization);
214 215 216 217 218 219 220
    connect(m_rootSelector,
            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);
221 222 223 224
                const QModelIndex rootIndex = m_listView->rootIndex();
                const QModelIndex fileIndex = m_listView->currentIndex();
                if (!isChildOf(fileIndex, rootIndex))
                    selectFile(directory);
225
            });
con's avatar
con committed
226 227 228 229 230 231 232
}

void FolderNavigationWidget::toggleAutoSynchronization()
{
    setAutoSynchronization(!m_autoSync);
}

233 234 235 236 237 238 239 240 241
static bool itemLessThan(QComboBox *combo,
                         int index,
                         const FolderNavigationWidgetFactory::RootDirectory &directory)
{
    return combo->itemData(index, SORT_ROLE).toInt() < directory.sortValue
           || (combo->itemData(index, SORT_ROLE).toInt() == directory.sortValue
               && combo->itemData(index, Qt::DisplayRole).toString() < directory.displayName);
}

242
void FolderNavigationWidget::insertRootDirectory(
243
    const FolderNavigationWidgetFactory::RootDirectory &directory)
244
{
245 246 247 248 249 250
    // Find existing. Do not remove yet, to not mess up the current selection.
    int previousIndex = 0;
    while (previousIndex < m_rootSelector->count()
           && m_rootSelector->itemData(previousIndex, ID_ROLE).toString() != directory.id)
        ++previousIndex;
    // Insert sorted.
251
    int index = 0;
252
    while (index < m_rootSelector->count() && itemLessThan(m_rootSelector, index, directory))
253
        ++index;
254 255 256
    m_rootSelector->insertItem(index, directory.displayName);
    if (index <= previousIndex) // item was inserted, update previousIndex
        ++previousIndex;
257 258
    m_rootSelector->setItemData(index, qVariantFromValue(directory.path), PATH_ROLE);
    m_rootSelector->setItemData(index, directory.id, ID_ROLE);
259
    m_rootSelector->setItemData(index, directory.sortValue, SORT_ROLE);
260
    m_rootSelector->setItemData(index, directory.path.toUserOutput(), Qt::ToolTipRole);
261
    m_rootSelector->setItemIcon(index, directory.icon);
262 263 264 265
    if (m_rootSelector->currentIndex() == previousIndex)
        m_rootSelector->setCurrentIndex(index);
    if (previousIndex < m_rootSelector->count())
        m_rootSelector->removeItem(previousIndex);
266 267 268 269
    if (m_autoSync) // we might find a better root for current selection now
        setCurrentEditor(Core::EditorManager::currentEditor());
}

270
void FolderNavigationWidget::removeRootDirectory(const QString &id)
271 272
{
    for (int i = 0; i < m_rootSelector->count(); ++i) {
273
        if (m_rootSelector->itemData(i, ID_ROLE).toString() == id) {
274 275 276 277 278 279 280 281
            m_rootSelector->removeItem(i);
            break;
        }
    }
    if (m_autoSync) // we might need to find a new root for current selection
        setCurrentEditor(Core::EditorManager::currentEditor());
}

con's avatar
con committed
282 283 284 285 286 287 288
bool FolderNavigationWidget::autoSynchronization() const
{
    return m_autoSync;
}

void FolderNavigationWidget::setAutoSynchronization(bool sync)
{
289
    m_toggleSync->setChecked(sync);
con's avatar
con committed
290 291 292 293 294 295
    if (sync == m_autoSync)
        return;

    m_autoSync = sync;

    if (m_autoSync) {
296
        connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
297 298
                this, &FolderNavigationWidget::setCurrentEditor);
        setCurrentEditor(Core::EditorManager::currentEditor());
con's avatar
con committed
299
    } else {
300
        disconnect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
301
                this, &FolderNavigationWidget::setCurrentEditor);
con's avatar
con committed
302 303 304
    }
}

305
void FolderNavigationWidget::setCurrentEditor(Core::IEditor *editor)
con's avatar
con committed
306
{
307
    if (!editor || editor->document()->filePath().isEmpty() || editor->document()->isTemporary())
308
        return;
309
    const Utils::FileName filePath = editor->document()->filePath();
310 311 312 313 314 315
    selectBestRootForFile(filePath);
    selectFile(filePath);
}

void FolderNavigationWidget::selectBestRootForFile(const Utils::FileName &filePath)
{
316 317
    const int bestRootIndex = bestRootForFile(filePath);
    m_rootSelector->setCurrentIndex(bestRootIndex);
318 319
}

320
void FolderNavigationWidget::selectFile(const Utils::FileName &filePath)
321
{
322
    const QModelIndex fileIndex = m_fileSystemModel->index(filePath.toString());
323
    if (fileIndex.isValid() || filePath.isEmpty() /* Computer root */) {
324 325 326 327 328 329 330
        // TODO This only scrolls to the right position if all directory contents are loaded.
        // Unfortunately listening to directoryLoaded was still not enough (there might also
        // 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());
331 332 333 334 335 336
            if (fileIndex == m_listView->rootIndex()) {
                m_listView->horizontalScrollBar()->setValue(0);
                m_listView->verticalScrollBar()->setValue(0);
            } else {
                m_listView->scrollTo(fileIndex);
            }
337
        });
338
    }
con's avatar
con committed
339 340
}

341
void FolderNavigationWidget::setRootDirectory(const Utils::FileName &directory)
con's avatar
con committed
342
{
343 344
    const QModelIndex index = m_fileSystemModel->setRootPath(directory.toString());
    m_listView->setRootIndex(index);
345 346
}

347
int FolderNavigationWidget::bestRootForFile(const Utils::FileName &filePath)
348
{
349 350 351 352 353 354 355 356 357 358
    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;
359 360
}

361
void FolderNavigationWidget::openItem(const QModelIndex &index)
362
{
363 364 365 366
    QTC_ASSERT(index.isValid(), return);
    // signal "activate" is also sent when double-clicking folders
    // but we don't want to do anything in that case
    if (m_fileSystemModel->isDir(index))
367
        return;
368
    const QString path = m_fileSystemModel->filePath(index);
369 370 371
    Core::EditorManager::openEditor(path);
}

372
QStringList FolderNavigationWidget::projectsInDirectory(const QModelIndex &index) const
373
{
374
    QTC_ASSERT(index.isValid() && m_fileSystemModel->isDir(index), return {});
375 376
    const QFileInfo fi = m_fileSystemModel->fileInfo(index);
    if (!fi.isReadable() || !fi.isExecutable())
377
        return {};
378 379
    const QString path = m_fileSystemModel->filePath(index);
    // Try to find project files in directory and open those.
380 381 382 383 384 385
    return FolderNavigationWidget::projectFilesInDirectory(path);
}

void FolderNavigationWidget::openProjectsInDirectory(const QModelIndex &index)
{
    const QStringList projectFiles = projectsInDirectory(index);
386 387
    if (!projectFiles.isEmpty())
        Core::ICore::instance()->openFiles(projectFiles);
388 389 390 391 392 393
}

void FolderNavigationWidget::contextMenuEvent(QContextMenuEvent *ev)
{
    QMenu menu;
    // Open current item
394
    const QModelIndex current = m_listView->currentIndex();
395
    const bool hasCurrentItem = current.isValid();
396 397
    QAction *actionOpenFile = nullptr;
    QAction *actionOpenProjects = nullptr;
398
    QAction *actionOpenAsProject = nullptr;
399
    const bool isDir = m_fileSystemModel->isDir(current);
400 401 402
    const Utils::FileName filePath = hasCurrentItem ? Utils::FileName::fromString(
                                                          m_fileSystemModel->filePath(current))
                                                    : Utils::FileName();
403 404
    if (hasCurrentItem) {
        const QString fileName = m_fileSystemModel->fileName(current);
405
        if (isDir) {
406
            actionOpenProjects = menu.addAction(tr("Open Project in \"%1\"").arg(fileName));
407 408
            if (projectsInDirectory(current).isEmpty())
                actionOpenProjects->setEnabled(false);
409
        } else {
410
            actionOpenFile = menu.addAction(tr("Open \"%1\"").arg(fileName));
411 412 413
            if (ProjectExplorerPlugin::isProjectFile(Utils::FileName::fromString(fileName)))
                actionOpenAsProject = menu.addAction(tr("Open Project \"%1\"").arg(fileName));
        }
414
    }
415 416 417 418 419

    // we need dummy DocumentModel::Entry with absolute file path in it
    // to get EditorManager::addNativeDirAndOpenWithActions() working
    Core::DocumentModel::Entry fakeEntry;
    Core::IDocument document;
420
    document.setFilePath(filePath);
421 422 423
    fakeEntry.document = &document;
    Core::EditorManager::addNativeDirAndOpenWithActions(&menu, &fakeEntry);

424
    if (hasCurrentItem && !isDir) {
425
        if (Core::DiffService::instance()) {
426 427 428 429 430 431 432
            menu.addAction(
                TextEditor::TextDocument::createDiffAgainstCurrentFileAction(&menu, [filePath]() {
                    return filePath;
                }));
        }
    }

433 434 435 436 437
    QAction *action = menu.exec(ev->globalPos());
    if (!action)
        return;

    ev->accept();
438
    if (action == actionOpenFile)
439
        openItem(current);
440 441
    else if (action == actionOpenAsProject)
        ProjectExplorerPlugin::openProject(filePath.toString());
442 443
    else if (action == actionOpenProjects)
        openProjectsInDirectory(current);
444 445
}

446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
void FolderNavigationWidget::setHiddenFilesFilter(bool filter)
{
    QDir::Filters filters = m_fileSystemModel->filter();
    if (filter)
        filters |= QDir::Hidden;
    else
        filters &= ~(QDir::Hidden);
    m_fileSystemModel->setFilter(filters);
    m_filterHiddenFilesAction->setChecked(filter);
}

bool FolderNavigationWidget::hiddenFilesFilter() const
{
    return m_filterHiddenFilesAction->isChecked();
}

462 463 464 465 466 467 468 469 470
QStringList FolderNavigationWidget::projectFilesInDirectory(const QString &path)
{
    QDir dir(path);
    QStringList projectFiles;
    foreach (const QFileInfo &i, dir.entryInfoList(ProjectExplorerPlugin::projectFileGlobs(), QDir::Files))
        projectFiles.append(i.absoluteFilePath());
    return projectFiles;
}

471
// --------------------FolderNavigationWidgetFactory
hjk's avatar
hjk committed
472
FolderNavigationWidgetFactory::FolderNavigationWidgetFactory()
con's avatar
con committed
473
{
474
    m_instance = this;
475 476 477 478
    setDisplayName(tr("File System"));
    setPriority(400);
    setId("File System");
    setActivationSequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+Y") : tr("Alt+Y")));
479 480 481
    insertRootDirectory({QLatin1String("A.Computer"),
                         0 /*sortValue*/,
                         FolderNavigationWidget::tr("Computer"),
482 483
                         Utils::FileName(),
                         Icons::DESKTOP_DEVICE_SMALL.icon()});
484
    insertRootDirectory({QLatin1String("A.Home"),
485
                         10 /*sortValue*/,
486
                         FolderNavigationWidget::tr("Home"),
487 488
                         Utils::FileName::fromString(QDir::homePath()),
                         Utils::Icons::HOME.icon()});
489 490 491 492 493
    updateProjectsDirectoryRoot();
    connect(Core::DocumentManager::instance(),
            &Core::DocumentManager::projectsDirectoryChanged,
            this,
            &FolderNavigationWidgetFactory::updateProjectsDirectoryRoot);
con's avatar
con committed
494 495 496 497
}

Core::NavigationView FolderNavigationWidgetFactory::createWidget()
{
498
    auto fnw = new FolderNavigationWidget;
499
    for (const RootDirectory &root : m_rootDirectories)
500
        fnw->insertRootDirectory(root);
501 502 503
    connect(this,
            &FolderNavigationWidgetFactory::rootDirectoryAdded,
            fnw,
504
            &FolderNavigationWidget::insertRootDirectory);
505 506 507 508 509 510
    connect(this,
            &FolderNavigationWidgetFactory::rootDirectoryRemoved,
            fnw,
            &FolderNavigationWidget::removeRootDirectory);

    Core::NavigationView n;
511
    n.widget = fnw;
512
    auto filter = new QToolButton;
Ulf Hermann's avatar
Ulf Hermann committed
513
    filter->setIcon(Utils::Icons::FILTER.icon());
514 515 516
    filter->setToolTip(tr("Filter Files"));
    filter->setPopupMode(QToolButton::InstantPopup);
    filter->setProperty("noArrow", true);
517
    auto filterMenu = new QMenu(filter);
518 519 520
    filterMenu->addAction(fnw->m_filterHiddenFilesAction);
    filter->setMenu(filterMenu);
    n.dockToolBarWidgets << filter << fnw->m_toggleSync;
con's avatar
con committed
521 522 523
    return n;
}

Serhii Moroz's avatar
Serhii Moroz committed
524
void FolderNavigationWidgetFactory::saveSettings(QSettings *settings, int position, QWidget *widget)
525
{
526
    auto fnw = qobject_cast<FolderNavigationWidget *>(widget);
527 528 529 530 531 532
    QTC_ASSERT(fnw, return);
    const QString baseKey = QLatin1String("FolderNavigationWidget.") + QString::number(position);
    settings->setValue(baseKey + QLatin1String(".HiddenFilesFilter"), fnw->hiddenFilesFilter());
    settings->setValue(baseKey + QLatin1String(".SyncWithEditor"), fnw->autoSynchronization());
}

Serhii Moroz's avatar
Serhii Moroz committed
533
void FolderNavigationWidgetFactory::restoreSettings(QSettings *settings, int position, QWidget *widget)
534
{
535
    auto fnw = qobject_cast<FolderNavigationWidget *>(widget);
536 537 538 539 540
    QTC_ASSERT(fnw, return);
    const QString baseKey = QLatin1String("FolderNavigationWidget.") + QString::number(position);
    fnw->setHiddenFilesFilter(settings->value(baseKey + QLatin1String(".HiddenFilesFilter"), false).toBool());
    fnw->setAutoSynchronization(settings->value(baseKey +  QLatin1String(".SyncWithEditor"), true).toBool());
}
541

542
void FolderNavigationWidgetFactory::insertRootDirectory(const RootDirectory &directory)
543
{
544 545 546
    const int index = rootIndex(directory.id);
    if (index < 0)
        m_rootDirectories.append(directory);
547 548
    else
        m_rootDirectories[index] = directory;
549
    emit m_instance->rootDirectoryAdded(directory);
550 551
}

552
void FolderNavigationWidgetFactory::removeRootDirectory(const QString &id)
553
{
554
    const int index = rootIndex(id);
555
    QTC_ASSERT(index >= 0, return );
556
    m_rootDirectories.removeAt(index);
557
    emit m_instance->rootDirectoryRemoved(id);
558 559
}

560 561 562 563 564 565 566 567 568
int FolderNavigationWidgetFactory::rootIndex(const QString &id)
{
    return Utils::indexOf(m_rootDirectories,
                          [id](const RootDirectory &entry) { return entry.id == id; });
}

void FolderNavigationWidgetFactory::updateProjectsDirectoryRoot()
{
    insertRootDirectory({QLatin1String(PROJECTSDIRECTORYROOT_ID),
569
                         20 /*sortValue*/,
570
                         FolderNavigationWidget::tr("Projects"),
571 572
                         Core::DocumentManager::projectsDirectory(),
                         Utils::Icons::PROJECT.icon()});
573 574
}

575 576
} // namespace Internal
} // namespace ProjectExplorer