diff --git a/src/tiled/mainwindow.cpp b/src/tiled/mainwindow.cpp index b6c29ac22a0..578045d9586 100644 --- a/src/tiled/mainwindow.cpp +++ b/src/tiled/mainwindow.cpp @@ -98,6 +98,7 @@ #include #endif +#include "projectdock.h" #include "qtcompat_p.h" using namespace Tiled; @@ -198,6 +199,7 @@ MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) , mUi(new Ui::MainWindow) , mActionHandler(new MapDocumentActionHandler(this)) , mConsoleDock(new ConsoleDock(this)) + , mProjectDock(new ProjectDock(this)) , mIssuesDock(new IssuesDock(this)) , mObjectTypesEditor(new ObjectTypesEditor(this)) , mAutomappingManager(new AutomappingManager(this)) @@ -339,6 +341,7 @@ MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) ActionManager::registerAction(undoAction, "Undo"); ActionManager::registerAction(redoAction, "Redo"); + addDockWidget(Qt::LeftDockWidgetArea, mProjectDock); addDockWidget(Qt::BottomDockWidgetArea, mConsoleDock); addDockWidget(Qt::BottomDockWidgetArea, mIssuesDock); tabifyDockWidget(mConsoleDock, mIssuesDock); @@ -461,7 +464,7 @@ MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) mLayerMenu->addSeparator(); mLayerMenu->addAction(mActionHandler->actionLayerProperties()); - menuBar()->insertMenu(mUi->menuHelp->menuAction(), mLayerMenu); + menuBar()->insertMenu(mUi->menuProject->menuAction(), mLayerMenu); ActionManager::registerMenu(mLayerMenu, "Layer"); ActionManager::registerMenu(mNewLayerMenu, "NewLayer"); @@ -564,6 +567,12 @@ MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) connect(mUi->actionTilesetProperties, &QAction::triggered, this, &MainWindow::editTilesetProperties); + connect(mUi->actionOpenProject, &QAction::triggered, mProjectDock, &ProjectDock::openProject); + connect(mUi->actionSaveProjectAs, &QAction::triggered, mProjectDock, &ProjectDock::saveProjectAs); + connect(mUi->actionCloseProject, &QAction::triggered, mProjectDock, &ProjectDock::closeProject); + connect(mUi->actionAddFolderToProject, &QAction::triggered, mProjectDock, &ProjectDock::addFolderToProject); + connect(mUi->actionRefreshProjectFolders, &QAction::triggered, mProjectDock, &ProjectDock::refreshProjectFolders); + connect(mUi->actionDocumentation, &QAction::triggered, this, &MainWindow::openDocumentation); connect(mUi->actionForum, &QAction::triggered, this, &MainWindow::openForum); connect(mUi->actionDonate, &QAction::triggered, this, &MainWindow::showDonationDialog); @@ -584,6 +593,8 @@ MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) mUi->menuRecentFiles->insertSeparator(mUi->actionClearRecentFiles); mUi->menuRecentFiles->setToolTipsVisible(true); + connect(mProjectDock, &ProjectDock::projectFileNameChanged, this, &MainWindow::updateWindowTitle); + setThemeIcon(mUi->menuNew, "document-new"); setThemeIcon(mUi->actionOpen, "document-open"); setThemeIcon(mUi->menuRecentFiles, "document-open-recent"); @@ -604,6 +615,11 @@ MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) setThemeIcon(mUi->actionFitInView, "zoom-fit-best"); setThemeIcon(mUi->actionResizeMap, "document-page-setup"); setThemeIcon(mUi->actionMapProperties, "document-properties"); + setThemeIcon(mUi->actionOpenProject, "document-open"); + setThemeIcon(mUi->actionSaveProjectAs, "document-save-as"); + setThemeIcon(mUi->actionCloseProject, "window-close"); + setThemeIcon(mUi->actionAddFolderToProject, "folder-new"); + setThemeIcon(mUi->actionRefreshProjectFolders, "view-refresh"); setThemeIcon(mUi->actionDocumentation, "help-contents"); setThemeIcon(mUi->actionAbout, "help-about"); @@ -1674,12 +1690,18 @@ void MainWindow::readSettings() void MainWindow::updateWindowTitle() { + QString projectName = mProjectDock->projectFileName(); + if (!projectName.isEmpty()) { + projectName = QFileInfo(projectName).completeBaseName(); + projectName = QString(QLatin1String(" (%1)")).arg(projectName); + } + if (Document *document = mDocumentManager->currentDocument()) { - setWindowTitle(tr("[*]%1").arg(document->displayName())); + setWindowTitle(tr("[*]%1%2").arg(document->displayName(), projectName)); setWindowFilePath(document->fileName()); setWindowModified(document->isModified()); } else { - setWindowTitle(QString()); + setWindowTitle(projectName); setWindowFilePath(QString()); setWindowModified(false); } diff --git a/src/tiled/mainwindow.h b/src/tiled/mainwindow.h index 5e4dabbd2c3..196d23ac94c 100644 --- a/src/tiled/mainwindow.h +++ b/src/tiled/mainwindow.h @@ -57,6 +57,7 @@ class MapEditor; class MapScene; class MapView; class ObjectTypesEditor; +class ProjectDock; class TilesetDocument; class TilesetEditor; class Zoomable; @@ -205,6 +206,7 @@ class MainWindow : public QMainWindow Zoomable *mZoomable = nullptr; MapDocumentActionHandler *mActionHandler; ConsoleDock *mConsoleDock; + ProjectDock *mProjectDock; IssuesDock *mIssuesDock; ObjectTypesEditor *mObjectTypesEditor; QSettings mSettings; diff --git a/src/tiled/mainwindow.ui b/src/tiled/mainwindow.ui index 68c0acbab14..26f72efce76 100644 --- a/src/tiled/mainwindow.ui +++ b/src/tiled/mainwindow.ui @@ -177,11 +177,23 @@ + + + &Project + + + + + + + + + @@ -655,6 +667,36 @@ Community Forum ↗ + + + &Open Project... + + + + + &Close Project + + + + + Clear Recent Projects + + + + + Add Folder to Project... + + + + + Save Project As... + + + + + Refresh Folders + + diff --git a/src/tiled/mapsdock.cpp b/src/tiled/mapsdock.cpp index 6ae2093ef41..0f2049c9a3a 100644 --- a/src/tiled/mapsdock.cpp +++ b/src/tiled/mapsdock.cpp @@ -29,11 +29,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include diff --git a/src/tiled/mapsdock.h b/src/tiled/mapsdock.h index aea7debc4c5..a54fbcb8723 100644 --- a/src/tiled/mapsdock.h +++ b/src/tiled/mapsdock.h @@ -24,10 +24,7 @@ #include class QFileSystemModel; -class QLabel; class QLineEdit; -class QModelIndex; -class QTreeView; namespace Tiled { @@ -69,10 +66,11 @@ class MapsView : public QTreeView */ QSize sizeHint() const override; - void mousePressEvent(QMouseEvent *event) override; - QFileSystemModel *model() const { return mFileSystemModel; } +protected: + void mousePressEvent(QMouseEvent *event) override; + private: void onMapsDirectoryChanged(); void onActivated(const QModelIndex &index); diff --git a/src/tiled/project.cpp b/src/tiled/project.cpp new file mode 100644 index 00000000000..93367d0b521 --- /dev/null +++ b/src/tiled/project.cpp @@ -0,0 +1,178 @@ +/* + * project.cpp + * Copyright 2019, Thorbjørn Lindeijer + * + * This file is part of Tiled. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "project.h" + +#include "fileformat.h" +#include "pluginmanager.h" +#include "savefile.h" +#include "utils.h" + +#include +#include +#include +#include + +namespace Tiled { + +static QString relative(const QDir &dir, const QString &fileName) +{ + QString rel = dir.relativeFilePath(fileName); + return rel.isEmpty() ? QString(QLatin1String(".")) : rel; +} + +Project::Project() +{ + updateNameFilters(); +} + +bool Project::save(const QString &fileName) +{ + QJsonObject project; + + const QDir dir = QFileInfo(fileName).dir(); + + QJsonArray folders; + + for (auto &folder : mFolders) + folders.append(relative(dir, folder->filePath)); + + project.insert(QLatin1String("folders"), folders); + + QJsonDocument document(project); + + SaveFile file(fileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + return false; + + file.device()->write(document.toJson()); + if (!file.commit()) + return false; + + mFileName = fileName; + return true; +} + +bool Project::load(const QString &fileName) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + return false; + + QJsonParseError error; + QByteArray json = file.readAll(); + QJsonDocument document(QJsonDocument::fromJson(json, &error)); + if (error.error != QJsonParseError::NoError) + return false; + + mFolders.clear(); + mFileName = fileName; + + const QDir dir = QFileInfo(fileName).dir(); + + QJsonObject project = document.object(); + + const QJsonArray folders = project.value(QLatin1String("folders")).toArray(); + + for (const QJsonValue &folderValue : folders) { + const QString filePath = QDir::cleanPath(dir.absoluteFilePath(folderValue.toString())); + mFolders.push_back(std::make_unique(filePath)); + } + + refreshFolders(); + + return true; +} + +void Project::clear() +{ + mFileName.clear(); + mFolders.clear(); +} + +void Project::addFolder(const QString &folder) +{ + auto entry = std::make_unique(folder); + mVisitedFolders.clear(); + refreshFolder(*entry); + mFolders.push_back(std::move(entry)); +} + +void Project::refreshFolders() +{ + // TODO: This process should run in a thread (potentially one job for each folder) + + for (auto &folder : mFolders) { + // same child folders are allowed in each top-level folder + mVisitedFolders.clear(); + refreshFolder(*folder); + } +} + +void Project::updateNameFilters() +{ + QStringList nameFilters; + + const auto fileFormats = PluginManager::objects(); + for (FileFormat *format : fileFormats) { + if (!(format->capabilities() & FileFormat::Read)) + continue; + + const QString filter = format->nameFilter(); + nameFilters.append(Utils::cleanFilterList(filter)); + } + + if (mNameFilters != nameFilters) { + mNameFilters = nameFilters; + refreshFolders(); + } +} + +void Project::refreshFolder(FolderEntry &folder) +{ + // erase previously found entries + folder.entries.clear(); + + const auto list = QDir(folder.filePath).entryInfoList(mNameFilters, + QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot, + QDir::Name | QDir::LocaleAware | QDir::DirsFirst); + + for (const auto &fileInfo : list) { + auto entry = std::make_unique(fileInfo.filePath(), &folder); + + if (fileInfo.isDir()) { + const QString canonicalPath = fileInfo.canonicalFilePath(); + + // prevent potential endless symlink loop + if (!mVisitedFolders.contains(canonicalPath)) { + mVisitedFolders.insert(canonicalPath); + refreshFolder(*entry); + } + + // Leave out empty directories + if (entry->entries.empty()) + continue; + } + + folder.entries.push_back(std::move(entry)); + } +} + +} // namespace Tiled diff --git a/src/tiled/project.h b/src/tiled/project.h new file mode 100644 index 00000000000..e6d0bb7299c --- /dev/null +++ b/src/tiled/project.h @@ -0,0 +1,81 @@ +/* + * project.h + * Copyright 2019, Thorbjørn Lindeijer + * + * This file is part of Tiled. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include +#include + +#include +#include + +namespace Tiled { + +struct FolderEntry +{ + explicit FolderEntry(const QString &filePath, FolderEntry *parent = nullptr) + : filePath(filePath) + , parent(parent) + {} + + QString filePath; + std::vector> entries; + FolderEntry *parent = nullptr; +}; + +class Project +{ +public: + Project(); + + QString fileName() const; + bool save(const QString &fileName); + bool load(const QString &fileName); + void clear(); + + void addFolder(const QString &folder); + void refreshFolders(); + + const std::vector>* folders() const; + +private: + void updateNameFilters(); + + void refreshFolder(FolderEntry &folder); + + QString mFileName; + std::vector> mFolders; + + QStringList mNameFilters; + QSet mVisitedFolders; +}; + + +inline QString Project::fileName() const +{ + return mFileName; +} + +inline const std::vector > *Project::folders() const +{ + return &mFolders; +} + +} // namespace Tiled diff --git a/src/tiled/projectdock.cpp b/src/tiled/projectdock.cpp new file mode 100644 index 00000000000..426bca89e98 --- /dev/null +++ b/src/tiled/projectdock.cpp @@ -0,0 +1,209 @@ +/* + * projectdock.cpp + * Copyright 2019, Thorbjørn Lindeijer + * + * This file is part of Tiled. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "projectdock.h" + +#include "documentmanager.h" +#include "preferences.h" +#include "projectmodel.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include + +using namespace Tiled; + +static const char * const LAST_PROJECT_KEY = "Project/LastProject"; + +ProjectDock::ProjectDock(QWidget *parent) + : QDockWidget(parent) + , mProjectView(new ProjectView) +{ + setObjectName(QLatin1String("ProjectDock")); + + auto widget = new QWidget(this); + auto layout = new QVBoxLayout(widget); + layout->setMargin(0); + + // Reopen last used project + const auto prefs = Preferences::instance(); + const auto settings = prefs->settings(); + const auto lastProjectFileName = settings->value(QLatin1String(LAST_PROJECT_KEY)).toString(); + if (prefs->openLastFilesOnStartup() && !lastProjectFileName.isEmpty()) + mProject.load(lastProjectFileName); + + auto projectModel = new ProjectModel(this); + projectModel->setFolders(mProject.folders()); + + mProjectView->setModel(projectModel); + + layout->addWidget(mProjectView); + + setWidget(widget); + retranslateUi(); + + connect(this, &ProjectDock::projectFileNameChanged, [this] { + Preferences::instance()->settings()->setValue(QLatin1String(LAST_PROJECT_KEY), projectFileName()); + }); +} + +void ProjectDock::openProject() +{ + const QString projectFilesFilter = tr("Tiled Projects (*.tiled-project)"); + const QString fileName = QFileDialog::getOpenFileName(window(), + tr("Open Project"), + mProject.fileName(), + projectFilesFilter, + nullptr); + if (fileName.isEmpty()) + return; + + Project project; + + if (!project.load(fileName)) { + QMessageBox::critical(window(), + tr("Error Opening Project"), + tr("An error occurred while opening the project.")); + return; + } + + mProjectView->model()->setFolders(nullptr); + std::swap(mProject, project); + mProjectView->model()->setFolders(mProject.folders()); + + emit projectFileNameChanged(); +} + +void ProjectDock::saveProjectAs() +{ + const QString projectFilesFilter = tr("Tiled Projects (*.tiled-project)"); + const QString fileName = QFileDialog::getSaveFileName(window(), + tr("Save Project As"), + mProject.fileName(), + projectFilesFilter, + nullptr); + if (fileName.isEmpty()) + return; + + if (!mProject.save(fileName)) { + QMessageBox::critical(window(), + tr("Error Saving Project"), + tr("An error occurred while saving the project.")); + } + + emit projectFileNameChanged(); +} + +void ProjectDock::closeProject() +{ + mProjectView->model()->setFolders(nullptr); + mProject.clear(); + + emit projectFileNameChanged(); +} + +void ProjectDock::addFolderToProject() +{ + const QString folder = QFileDialog::getExistingDirectory(window(), + tr("Choose Folder"), + QFileInfo(mProject.fileName()).path()); + + if (folder.isEmpty()) + return; + + mProject.addFolder(folder); + // FIXME: Should just add the new top-level row, not trigger complete reset + mProjectView->model()->setFolders(mProject.folders()); + + if (!mProject.fileName().isEmpty()) + mProject.save(mProject.fileName()); +} + +void ProjectDock::refreshProjectFolders() +{ + mProjectView->model()->setFolders(nullptr); + mProject.refreshFolders(); + mProjectView->model()->setFolders(mProject.folders()); +} + +void ProjectDock::changeEvent(QEvent *e) +{ + QDockWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + retranslateUi(); + break; + default: + break; + } +} + +void ProjectDock::retranslateUi() +{ + setWindowTitle(tr("Project")); +} + +///// ///// ///// ///// ///// + +ProjectView::ProjectView(QWidget *parent) + : QTreeView(parent) +{ + setHeaderHidden(true); + setUniformRowHeights(true); + setDragEnabled(true); + setDefaultDropAction(Qt::MoveAction); + + connect(this, &QAbstractItemView::activated, + this, &ProjectView::onActivated); +} + +QSize ProjectView::sizeHint() const +{ + return Utils::dpiScaled(QSize(130, 100)); +} + +void ProjectView::setModel(QAbstractItemModel *model) +{ + mProjectModel = qobject_cast(model); + Q_ASSERT(mProjectModel); + QTreeView::setModel(model); +} + +void ProjectView::mousePressEvent(QMouseEvent *event) +{ + QModelIndex index = indexAt(event->pos()); + if (index.isValid()) { + // Prevent drag-and-drop starting when clicking on an unselected item. + setDragEnabled(selectionModel()->isSelected(index)); + } + + QTreeView::mousePressEvent(event); +} + +void ProjectView::onActivated(const QModelIndex &index) +{ + const QString path = model()->filePath(index); + if (!QFileInfo(path).isDir()) + DocumentManager::instance()->openFile(path); +} diff --git a/src/tiled/projectdock.h b/src/tiled/projectdock.h new file mode 100644 index 00000000000..b051034aafc --- /dev/null +++ b/src/tiled/projectdock.h @@ -0,0 +1,97 @@ +/* + * projectdock.h + * Copyright 2019, Thorbjørn Lindeijer + * + * This file is part of Tiled. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include "project.h" + +#include +#include + +class QFileSystemModel; + +namespace Tiled { + +class ProjectView; +class ProjectModel; + +class ProjectDock : public QDockWidget +{ + Q_OBJECT + +public: + ProjectDock(QWidget *parent = nullptr); + + QString projectFileName() const; + + void openProject(); + void saveProjectAs(); + void closeProject(); + void addFolderToProject(); + void refreshProjectFolders(); + +signals: + void projectFileNameChanged(); + +protected: + void changeEvent(QEvent *e) override; + +private: + void retranslateUi(); + + Project mProject; + ProjectView *mProjectView; +}; + +/** + * Shows the list of files in a project. + */ +class ProjectView : public QTreeView +{ + Q_OBJECT + +public: + ProjectView(QWidget *parent = nullptr); + + /** + * Returns a sensible size hint. + */ + QSize sizeHint() const override; + + void setModel(QAbstractItemModel *model) override; + + ProjectModel *model() const { return mProjectModel; } + +protected: + void mousePressEvent(QMouseEvent *event) override; + +private: + void onActivated(const QModelIndex &index); + + ProjectModel *mProjectModel; +}; + + +inline QString ProjectDock::projectFileName() const +{ + return mProject.fileName(); +} + +} // namespace Tiled diff --git a/src/tiled/projectmodel.cpp b/src/tiled/projectmodel.cpp new file mode 100644 index 00000000000..4bebace70ef --- /dev/null +++ b/src/tiled/projectmodel.cpp @@ -0,0 +1,115 @@ +/* + * projectmodel.cpp + * Copyright 2019, Thorbjørn Lindeijer + * + * This file is part of Tiled. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "projectmodel.h" + +#include "containerhelpers.h" + +#include + +namespace Tiled { + +ProjectModel::ProjectModel(QObject *parent) + : QAbstractItemModel(parent) +{ + mFileIconProvider.setOptions(QFileIconProvider::DontUseCustomDirectoryIcons); +} + +void ProjectModel::setFolders(const std::vector > *folders) +{ + beginResetModel(); + mFolders = folders; + endResetModel(); +} + +QString ProjectModel::filePath(const QModelIndex &index) const +{ + if (!index.isValid()) + return QString(); + + FolderEntry *entry = static_cast(index.internalPointer()); + return entry->filePath; +} + +QModelIndex ProjectModel::index(int row, int column, const QModelIndex &parent) const +{ + if (parent.isValid()) { + FolderEntry *entry = static_cast(parent.internalPointer()); + if (row < int(entry->entries.size())) + return createIndex(row, column, entry->entries.at(row).get()); + } else { + if (mFolders && row < int(mFolders->size())) + return createIndex(row, column, mFolders->at(row).get()); + } + + return QModelIndex(); +} + +QModelIndex ProjectModel::parent(const QModelIndex &index) const +{ + FolderEntry *entry = static_cast(index.internalPointer()); + return indexForEntry(entry->parent); +} + +int ProjectModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) + return mFolders ? mFolders->size() : 0; + + FolderEntry *entry = static_cast(parent.internalPointer()); + return entry->entries.size(); +} + +int ProjectModel::columnCount(const QModelIndex &) const +{ + return 1; +} + +QVariant ProjectModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + FolderEntry *entry = static_cast(index.internalPointer()); + switch (role) { + case Qt::DisplayRole: + return QFileInfo(entry->filePath).fileName(); + case Qt::DecorationRole: + return mFileIconProvider.icon(QFileInfo(entry->filePath)); + case Qt::ToolTipRole: + return entry->filePath; + } + + return QVariant(); +} + +QModelIndex ProjectModel::indexForEntry(FolderEntry *entry) const +{ + if (!entry) + return QModelIndex(); + + const std::vector> *container = entry->parent ? &entry->parent->entries : mFolders; + auto it = std::find_if(container->begin(), container->end(), [entry] (const std::unique_ptr &value) { return value.get() == entry; }); + + Q_ASSERT(it != container->end()); + return createIndex(std::distance(container->begin(), it), 0, entry); +} + +} // namespace Tiled diff --git a/src/tiled/projectmodel.h b/src/tiled/projectmodel.h new file mode 100644 index 00000000000..f189d7288fb --- /dev/null +++ b/src/tiled/projectmodel.h @@ -0,0 +1,57 @@ +/* + * projectmodel.h + * Copyright 2019, Thorbjørn Lindeijer + * + * This file is part of Tiled. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include "project.h" + +#include +#include + +namespace Tiled { + +class ProjectModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit ProjectModel(QObject *parent = nullptr); + + void setFolders(const std::vector> *folders); + + QString filePath(const QModelIndex &index) const; + + QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &index) const override; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + +private: + QModelIndex indexForEntry(FolderEntry *entry) const; + + const std::vector> *mFolders = nullptr; + QFileIconProvider mFileIconProvider; +}; + +} // namespace Tiled diff --git a/src/tiled/tiled.pro b/src/tiled/tiled.pro index dfa97a0da90..1581d4ee1c1 100644 --- a/src/tiled/tiled.pro +++ b/src/tiled/tiled.pro @@ -185,6 +185,9 @@ SOURCES += aboutdialog.cpp \ pluginlistmodel.cpp \ pointhandle.cpp \ preferences.cpp \ + project.cpp \ + projectdock.cpp \ + projectmodel.cpp \ preferencesdialog.cpp \ propertiesdock.cpp \ propertybrowser.cpp \ @@ -412,6 +415,9 @@ HEADERS += aboutdialog.h \ pointhandle.h \ preferencesdialog.h \ preferences.h \ + project.h \ + projectdock.h \ + projectmodel.h \ propertiesdock.h \ propertybrowser.h \ raiselowerhelper.h \ diff --git a/src/tiled/tiled.qbs b/src/tiled/tiled.qbs index 011b6add355..439ea2848a2 100644 --- a/src/tiled/tiled.qbs +++ b/src/tiled/tiled.qbs @@ -369,6 +369,12 @@ QtGuiApplication { "preferencesdialog.h", "preferencesdialog.ui", "preferences.h", + "project.cpp", + "project.h", + "projectdock.cpp", + "projectdock.h", + "projectmodel.cpp", + "projectmodel.h", "propertiesdock.cpp", "propertiesdock.h", "propertybrowser.cpp",