diff --git a/interface/src/ScriptsModel.cpp b/interface/src/ScriptsModel.cpp index d178397fc2..0c711cd940 100644 --- a/interface/src/ScriptsModel.cpp +++ b/interface/src/ScriptsModel.cpp @@ -11,7 +11,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include #include #include #include @@ -32,22 +31,21 @@ static const QString IS_TRUNCATED_NAME = "IsTruncated"; static const QString CONTAINER_NAME = "Contents"; static const QString KEY_NAME = "Key"; -TreeNodeBase::TreeNodeBase(TreeNodeFolder* parent, TreeNodeType type) : +TreeNodeBase::TreeNodeBase(TreeNodeFolder* parent, const QString& name, TreeNodeType type) : _parent(parent), + _name(name), _type(type) { }; -TreeNodeScript::TreeNodeScript(const QString& filename, const QString& fullPath, ScriptOrigin origin) : - TreeNodeBase(NULL, TREE_NODE_TYPE_SCRIPT), - _filename(filename), +TreeNodeScript::TreeNodeScript(const QString& localPath, const QString& fullPath, ScriptOrigin origin) : + TreeNodeBase(NULL, localPath.split("/").last(), TREE_NODE_TYPE_SCRIPT), + _localPath(localPath), _fullPath(fullPath), - _origin(origin) -{ + _origin(origin) { }; TreeNodeFolder::TreeNodeFolder(const QString& foldername, TreeNodeFolder* parent) : - TreeNodeBase(parent, TREE_NODE_TYPE_FOLDER), - _foldername(foldername) { + TreeNodeBase(parent, foldername, TREE_NODE_TYPE_FOLDER) { }; ScriptsModel::ScriptsModel(QObject* parent) : @@ -57,7 +55,6 @@ ScriptsModel::ScriptsModel(QObject* parent) : _fsWatcher(), _treeNodes() { - _localDirectory.setFilter(QDir::Files | QDir::Readable); _localDirectory.setNameFilters(QStringList("*.js")); @@ -70,46 +67,56 @@ ScriptsModel::ScriptsModel(QObject* parent) : reloadRemoteFiles(); } +TreeNodeBase* ScriptsModel::getTreeNodeFromIndex(const QModelIndex& index) const { + if (index.isValid()) { + return static_cast(index.internalPointer()); + } + return NULL; +} + QModelIndex ScriptsModel::index(int row, int column, const QModelIndex& parent) const { - return QModelIndex(); + if (row < 0 || column < 0) { + return QModelIndex(); + } + return createIndex(row, column, getFolderNodes(static_cast(getTreeNodeFromIndex(parent))).at(row)); } QModelIndex ScriptsModel::parent(const QModelIndex& child) const { - return QModelIndex(); + TreeNodeFolder* parent = (static_cast(child.internalPointer()))->getParent(); + if (!parent) { + return QModelIndex(); + } + TreeNodeFolder* grandParent = parent->getParent(); + int row = getFolderNodes(grandParent).indexOf(parent); + return createIndex(row, 0, parent); } QVariant ScriptsModel::data(const QModelIndex& index, int role) const { - /*const QList* files = NULL; - int row = 0; - bool isLocal = index.row() < _localFiles.size(); - if (isLocal) { - files = &_localFiles; - row = index.row(); - } else { - files = &_remoteFiles; - row = index.row() - _localFiles.size(); + TreeNodeBase* node = getTreeNodeFromIndex(index); + if (node->getType() == TREE_NODE_TYPE_SCRIPT) { + TreeNodeScript* script = static_cast(node); + if (role == Qt::DisplayRole) { + return QVariant(script->getName() + (script->getOrigin() == SCRIPT_ORIGIN_LOCAL ? " (local)" : "")); + } else if (role == ScriptPath) { + return QVariant(script->getFullPath()); + } + } else if (node->getType() == TREE_NODE_TYPE_FOLDER) { + TreeNodeFolder* folder = static_cast(node); + if (role == Qt::DisplayRole) { + return QVariant(folder->getName()); + } } - - if (role == Qt::DisplayRole) { - return QVariant((*files)[row]->getFilename() + (isLocal ? " (local)" : "")); - } else if (role == ScriptPath) { - return QVariant((*files)[row]->getFullPath()); - }*/ return QVariant(); } int ScriptsModel::rowCount(const QModelIndex& parent) const { - if (parent.isValid()) { - return 0; - } - return 0;// _localFiles.length() + _remoteFiles.length(); + return getFolderNodes(static_cast(getTreeNodeFromIndex(parent))).count(); } int ScriptsModel::columnCount(const QModelIndex& parent) const { return 1; } - void ScriptsModel::updateScriptsLocation(const QString& newPath) { _fsWatcher.removePath(_localDirectory.absolutePath()); @@ -129,7 +136,9 @@ void ScriptsModel::reloadRemoteFiles() { _loadingScripts = true; for (int i = _treeNodes.size() - 1; i >= 0; i--) { TreeNodeBase* node = _treeNodes.at(i); - if (typeid(*node) == typeid(TreeNodeScript) && static_cast(node)->getOrigin() == SCRIPT_ORIGIN_REMOTE) { + if (node->getType() == TREE_NODE_TYPE_SCRIPT && + static_cast(node)->getOrigin() == SCRIPT_ORIGIN_REMOTE) + { delete node; _treeNodes.removeAt(i); } @@ -229,7 +238,6 @@ void ScriptsModel::reloadLocalFiles() { for (int i = _treeNodes.size() - 1; i >= 0; i--) { TreeNodeBase* node = _treeNodes.at(i); - qDebug() << "deleting local " << i << " " << typeid(*node).name(); if (node->getType() == TREE_NODE_TYPE_SCRIPT && static_cast(node)->getOrigin() == SCRIPT_ORIGIN_LOCAL) { @@ -251,7 +259,6 @@ void ScriptsModel::reloadLocalFiles() { void ScriptsModel::rebuildTree() { for (int i = _treeNodes.size() - 1; i >= 0; i--) { - if (_treeNodes.at(i)->getType() == TREE_NODE_TYPE_FOLDER) { delete _treeNodes.at(i); _treeNodes.removeAt(i); @@ -260,18 +267,37 @@ void ScriptsModel::rebuildTree() { QHash folders; for (int i = 0; i < _treeNodes.size(); i++) { TreeNodeBase* node = _treeNodes.at(i); - qDebug() << "blup" << i << typeid(*node).name(); - if (typeid(*node) == typeid(TreeNodeScript)) { + if (node->getType() == TREE_NODE_TYPE_SCRIPT) { TreeNodeScript* script = static_cast(node); TreeNodeFolder* parent = NULL; QString hash; - QStringList pathList = script->getFilename().split(tr("/")); + QStringList pathList = script->getLocalPath().split(tr("/")); + pathList.removeLast(); QStringList::const_iterator pathIterator; for (pathIterator = pathList.constBegin(); pathIterator != pathList.constEnd(); ++pathIterator) { - hash.append("/" + *pathIterator); - qDebug() << hash; + hash.append(*pathIterator + "/"); + if (!folders.contains(hash)) { + folders[hash] = new TreeNodeFolder(*pathIterator, parent); + } + parent = folders[hash]; } + script->setParent(parent); } } + QHash::const_iterator folderIterator; + for (folderIterator = folders.constBegin(); folderIterator != folders.constEnd(); ++folderIterator) { + _treeNodes.append(*folderIterator); + } folders.clear(); } + +QList ScriptsModel::getFolderNodes(TreeNodeFolder* parent) const { + QList result; + for (int i = 0; i < _treeNodes.size(); i++) { + TreeNodeBase* node = _treeNodes.at(i); + if (node->getParent() == parent) { + result.append(node); + } + } + return result; +} diff --git a/interface/src/ScriptsModel.h b/interface/src/ScriptsModel.h index bb4883e399..549f87c0d7 100644 --- a/interface/src/ScriptsModel.h +++ b/interface/src/ScriptsModel.h @@ -31,28 +31,29 @@ enum TreeNodeType { class TreeNodeBase { public: - const TreeNodeFolder* getParent() { return _parent; } + TreeNodeFolder* getParent() const { return _parent; } void setParent(TreeNodeFolder* parent) { _parent = parent; } TreeNodeType getType() { return _type; } + const QString& getName() { return _name; }; private: TreeNodeFolder* _parent; TreeNodeType _type; protected: - TreeNodeBase(TreeNodeFolder* parent, TreeNodeType type); + QString _name; + TreeNodeBase(TreeNodeFolder* parent, const QString& name, TreeNodeType type); }; class TreeNodeScript : public TreeNodeBase { public: - TreeNodeScript(const QString& filename, const QString& fullPath, ScriptOrigin origin); - - const QString& getFilename() { return _filename; }; + TreeNodeScript(const QString& localPath, const QString& fullPath, ScriptOrigin origin); + const QString& getLocalPath() { return _localPath; } const QString& getFullPath() { return _fullPath; }; const ScriptOrigin getOrigin() { return _origin; }; private: - QString _filename; + QString _localPath; QString _fullPath; ScriptOrigin _origin; }; @@ -60,8 +61,6 @@ private: class TreeNodeFolder : public TreeNodeBase { public: TreeNodeFolder(const QString& foldername, TreeNodeFolder* parent); -private: - QString _foldername; }; class ScriptsModel : public QAbstractItemModel { @@ -70,9 +69,11 @@ public: ScriptsModel(QObject* parent = NULL); QModelIndex index(int row, int column, const QModelIndex& parent) const; QModelIndex parent(const QModelIndex& child) const; - virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; - virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; - virtual int columnCount(const QModelIndex& parent = QModelIndex()) const; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + int rowCount(const QModelIndex& parent = QModelIndex()) const; + int columnCount(const QModelIndex& parent = QModelIndex()) const; + TreeNodeBase* getTreeNodeFromIndex(const QModelIndex& index) const; + QList getFolderNodes(TreeNodeFolder* parent) const; enum Role { ScriptPath = Qt::UserRole, diff --git a/interface/src/ScriptsModelFilter.cpp b/interface/src/ScriptsModelFilter.cpp new file mode 100644 index 0000000000..1879e547c0 --- /dev/null +++ b/interface/src/ScriptsModelFilter.cpp @@ -0,0 +1,44 @@ +// +// ScriptsModelFilter.cpp +// interface/src +// +// Created by Thijs Wenker on 01/11/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ScriptsModelFilter.h" + +ScriptsModelFilter::ScriptsModelFilter(QObject *parent) : + QSortFilterProxyModel(parent) { +} + +bool ScriptsModelFilter::lessThan(const QModelIndex& left, const QModelIndex& right) const { + ScriptsModel* scriptsModel = static_cast(sourceModel()); + TreeNodeBase* leftNode = scriptsModel->getTreeNodeFromIndex(left); + TreeNodeBase* rightNode = scriptsModel->getTreeNodeFromIndex(right); + if (leftNode->getType() != rightNode->getType()) { + return leftNode->getType() == TREE_NODE_TYPE_FOLDER; + } + return leftNode->getName() < rightNode->getName(); +} + +bool ScriptsModelFilter::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const { + if (!filterRegExp().isEmpty()) { + ScriptsModel* scriptsModel = static_cast(sourceModel()); + TreeNodeBase* node = scriptsModel->getFolderNodes( + static_cast(scriptsModel->getTreeNodeFromIndex(sourceParent))).at(sourceRow); + QModelIndex sourceIndex = sourceModel()->index(sourceRow, this->filterKeyColumn(), sourceParent); + if (node->getType() == TREE_NODE_TYPE_FOLDER) { + int rows = scriptsModel->rowCount(sourceIndex); + for (int i = 0; i < rows; i++) { + if (filterAcceptsRow(i, sourceIndex)) { + return true; + } + } + } + } + return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); +} diff --git a/interface/src/ScriptsModelFilter.h b/interface/src/ScriptsModelFilter.h new file mode 100644 index 0000000000..7b7cdd974e --- /dev/null +++ b/interface/src/ScriptsModelFilter.h @@ -0,0 +1,27 @@ +// +// ScriptsModelFilter.h +// interface/src +// +// Created by Thijs Wenker on 01/11/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ScriptsModelFilter_h +#define hifi_ScriptsModelFilter_h + +#include "ScriptsModel.h" +#include + +class ScriptsModelFilter : public QSortFilterProxyModel { + Q_OBJECT +public: + ScriptsModelFilter(QObject *parent = NULL); +protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const; + bool lessThan(const QModelIndex& left, const QModelIndex& right) const; +}; + +#endif // hifi_ScriptsModelFilter_h diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp index f56c6e32af..d577859ebf 100644 --- a/interface/src/ui/RunningScriptsWidget.cpp +++ b/interface/src/ui/RunningScriptsWidget.cpp @@ -33,7 +33,7 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) : Qt::WindowCloseButtonHint), ui(new Ui::RunningScriptsWidget), _signalMapper(this), - _proxyModel(this), + _scriptsModelFilter(this), _scriptsModel(this) { ui->setupUi(this); @@ -41,16 +41,16 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) : ui->filterLineEdit->installEventFilter(this); - connect(&_proxyModel, &QSortFilterProxyModel::modelReset, + connect(&_scriptsModelFilter, &QSortFilterProxyModel::modelReset, this, &RunningScriptsWidget::selectFirstInList); QString shortcutText = Menu::getInstance()->getActionForOption(MenuOption::ReloadAllScripts)->shortcut().toString(QKeySequence::NativeText); ui->tipLabel->setText("Tip: Use " + shortcutText + " to reload all scripts."); - _proxyModel.setSourceModel(&_scriptsModel); - _proxyModel.sort(0, Qt::AscendingOrder); - _proxyModel.setDynamicSortFilter(true); - ui->scriptTreeView->setModel(&_proxyModel); + _scriptsModelFilter.setSourceModel(&_scriptsModel); + _scriptsModelFilter.sort(0, Qt::AscendingOrder); + _scriptsModelFilter.setDynamicSortFilter(true); + ui->scriptTreeView->setModel(&_scriptsModelFilter); connect(ui->filterLineEdit, &QLineEdit::textChanged, this, &RunningScriptsWidget::updateFileFilter); connect(ui->scriptTreeView, &QTreeView::doubleClicked, this, &RunningScriptsWidget::loadScriptFromList); @@ -70,12 +70,12 @@ RunningScriptsWidget::~RunningScriptsWidget() { void RunningScriptsWidget::updateFileFilter(const QString& filter) { QRegExp regex("^.*" + QRegExp::escape(filter) + ".*$", Qt::CaseInsensitive); - _proxyModel.setFilterRegExp(regex); + _scriptsModelFilter.setFilterRegExp(regex); selectFirstInList(); } void RunningScriptsWidget::loadScriptFromList(const QModelIndex& index) { - QVariant scriptFile = _proxyModel.data(index, ScriptsModel::ScriptPath); + QVariant scriptFile = _scriptsModelFilter.data(index, ScriptsModel::ScriptPath); Application::getInstance()->loadScript(scriptFile.toString()); } @@ -165,8 +165,8 @@ void RunningScriptsWidget::showEvent(QShowEvent* event) { } void RunningScriptsWidget::selectFirstInList() { - if (_proxyModel.rowCount() > 0) { - ui->scriptTreeView->setCurrentIndex(_proxyModel.index(0, 0)); + if (_scriptsModelFilter.rowCount() > 0) { + ui->scriptTreeView->setCurrentIndex(_scriptsModelFilter.index(0, 0)); } } diff --git a/interface/src/ui/RunningScriptsWidget.h b/interface/src/ui/RunningScriptsWidget.h index 69833a890b..3ec5590dee 100644 --- a/interface/src/ui/RunningScriptsWidget.h +++ b/interface/src/ui/RunningScriptsWidget.h @@ -18,6 +18,7 @@ #include #include "ScriptsModel.h" +#include "ScriptsModelFilter.h" #include "ScriptsTableWidget.h" namespace Ui { @@ -54,7 +55,7 @@ private slots: private: Ui::RunningScriptsWidget* ui; QSignalMapper _signalMapper; - QSortFilterProxyModel _proxyModel; + ScriptsModelFilter _scriptsModelFilter; ScriptsModel _scriptsModel; ScriptsTableWidget* _recentlyLoadedScriptsTable; QStringList _recentlyLoadedScripts;