123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- /* node_model.cpp - node tree model wrapper
- * Copyright (C) 2017-2018 caryoscelus
- *
- * 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 3 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 <http://www.gnu.org/licenses/>.
- */
- #include <fmt/format.h>
- #include <QDebug>
- #include <core/node_info/node_info.h>
- #include <core/node_info/copy.h>
- #include <core/node/make.h>
- #include <core/node/abstract_node.h>
- #include <core/node_tree/exceptions.h>
- #include <core/util/nothing.h>
- #include <core/serialize/node_writer.h>
- #include <core/action_stack.h>
- #include <core/document.h>
- #include <core/actions/change_link.h>
- #include <core/actions/custom_property.h>
- #include <core/actions/set_enabled.h>
- #include <core/actions/list.h>
- #include <util/strings.h>
- #include "node_model.h"
- using namespace fmt::literals;
- namespace rainynite::studio {
- static const size_t MAX_VALUE_LENGTH = 32;
- template <typename T, typename F>
- T report_tree_errors(F f) {
- try {
- return f();
- } catch (core::NodeTreeError const&) {
- // TODO: report
- return {};
- }
- }
- NodeModel::NodeModel(core::AbstractReference root_, shared_ptr<core::ActionStack> action_stack_, QObject* parent) :
- QAbstractItemModel(parent),
- root(root_),
- action_stack(action_stack_)
- {
- if (auto document = dynamic_cast<core::AbstractDocument*>(root.get())) {
- tree = document->get_tree();
- } else {
- qWarning() << "NodeModel: creating new tree. .this probably shouldn't happen";
- tree = root_ ? make_shared<core::NodeTree>(root_) : nullptr;
- }
- if (tree) {
- tree->connect_boost(tree->start_reload_signal, [this](auto parent) {
- auto parent_idx = from_inner_index(parent);
- beginRemoveRows(parent_idx, 0, tree->children_count(parent)-1);
- });
- tree->connect_boost(tree->start_adding_signal, [this](auto parent, size_t count) {
- endRemoveRows();
- auto parent_idx = from_inner_index(parent);
- beginInsertRows(parent_idx, 0, count-1);
- });
- tree->connect_boost(tree->end_reload_signal, [this]() {
- endInsertRows();
- });
- }
- }
- NodeModel::~NodeModel() {
- }
- void NodeModel::time_changed(core::Time time) {
- ContextListener::time_changed(time);
- Q_EMIT dataChanged(index(0, 1), index(2, 1));
- }
- QVariant NodeModel::data(QModelIndex const& index, int role) const {
- if (!index.isValid())
- return {};
- if (index.column() == 0) {
- switch (role) {
- case Qt::DisplayRole: {
- string node_role = "document";
- if (auto parent_node = get_node_as<core::AbstractNode>(parent(index)))
- node_role = parent_node->get_name_at(index.row());
- else if (parent(index).isValid())
- node_role = "{}"_format(index.row());
- string type_name = "<Bad node!>";
- string maybe_name;
- if (auto node = get_node(index)) {
- type_name = core::node_name(*node);
- if (auto node_node = abstract_node_cast(node)) {
- if (auto name = node_node->name(); !name.empty())
- maybe_name = "[{}] "_format(name);
- }
- }
- return util::str("{}{}: {}"_format(maybe_name, node_role, type_name));
- } break;
- case Qt::ToolTipRole: {
- // TODO: node role documentation
- if (auto node = get_node_as<DocString>(index))
- return util::str(node->doc_string());
- return {};
- } break;
- default:
- return {};
- }
- } else {
- switch (role) {
- case Qt::DisplayRole: {
- if (auto node = get_node(index)) {
- auto value = node->get_any(get_core_context());
- try {
- auto s = core::serialize::value_to_string(value);
- auto s_fixed = s.substr(0, MAX_VALUE_LENGTH);
- auto s_fixed2 = s_fixed.substr(0, s_fixed.find('\n'));
- if (s.size() > s_fixed2.size())
- s_fixed2 += "...";
- return util::str(s_fixed2);
- } catch (class_init::RuntimeTypeError const& ex) {
- return {};
- }
- }
- return {};
- } break;
- default:
- return {};
- }
- }
- }
- Qt::ItemFlags NodeModel::flags(QModelIndex const&) const {
- return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
- }
- QVariant NodeModel::headerData(int section, Qt::Orientation orientation, int role) const {
- if (orientation == Qt::Vertical)
- return {};
- switch (role) {
- case Qt::DisplayRole: {
- if (section == 0)
- return "node";
- else
- return "value";
- } break;
- default:
- return {};
- }
- }
- QModelIndex NodeModel::index(int row, int column, QModelIndex const& parent) const {
- return createIndex(row, column, (size_t)get_inner_index(parent, row));
- }
- core::NodeTreeIndex NodeModel::get_inner_index(QModelIndex const& parent, size_t i) const {
- if (tree == nullptr)
- return {};
- if (!parent.isValid()) {
- if (i == 0)
- return tree->get_root_index();
- else
- return tree->get_null_index();
- } else {
- return report_tree_errors<core::NodeTreeIndex>([&]() {
- return tree->index(get_inner_index(parent), i);
- });
- }
- }
- core::NodeTreeIndex NodeModel::get_inner_index(QModelIndex const& index) const {
- if (!index.isValid())
- return {};
- return index.internalId();
- }
- QModelIndex NodeModel::from_inner_index(core::NodeTreeIndex index) const {
- if (!index)
- return {};
- return report_tree_errors<QModelIndex>([&]() {
- return createIndex(tree->link_index(index), 0, (size_t)index);
- });
- }
- bool NodeModel::can_rename(QModelIndex const& index) const {
- return get_node_as<core::AbstractNode>(index) != nullptr;
- }
- QString NodeModel::get_name(QModelIndex const& index) const {
- return util::str(no_null(get_node_as<core::AbstractNode>(index))->name());
- }
- void NodeModel::set_name(QModelIndex const& index, QString const& name) {
- no_null(get_node_as<core::AbstractNode>(index))->set_name(util::str(name));
- }
- bool NodeModel::can_add_custom_property(QModelIndex const& parent) const {
- if (get_node_as<core::AbstractNode>(parent))
- return true;
- return false;
- }
- void NodeModel::add_empty_custom_property(QModelIndex const& parent, string const& name) {
- action_stack->emplace<core::actions::AddCustomProperty>(tree, get_inner_index(parent), name, core::make_value<Nothing>());
- // TODO: add rows
- }
- bool NodeModel::is_custom_property(QModelIndex const& index) const {
- if (auto parent = get_node_as<core::AbstractNode>(index.parent())) {
- auto i = index.row();
- return parent->get_name_at(i)[0] == '_';
- }
- return false;
- }
- void NodeModel::remove_custom_property(QModelIndex const& index) {
- if (auto parent = get_node_as<core::AbstractNode>(index.parent())) {
- auto i = index.row();
- beginRemoveRows(index.parent(), i, i);
- action_stack->emplace<core::actions::RemoveCustomProperty>(tree, get_inner_index(index.parent()), parent->get_name_at(i));
- endRemoveRows();
- }
- }
- bool NodeModel::can_clear_list(QModelIndex const& list) const {
- // TODO
- if (auto list_node = get_node_as<core::AbstractListLinked>(list))
- return list_node->is_editable_list();
- return false;
- }
- void NodeModel::clear_list(QModelIndex const& list) {
- if (auto idx = get_inner_index(list)) {
- using core::actions::ListClear;
- action_stack->emplace<ListClear>(tree, idx);
- }
- }
- bool NodeModel::can_add_element(QModelIndex const& parent) const {
- if (auto node = get_list_node(parent))
- return node->is_editable_list();
- return false;
- }
- void NodeModel::add_empty_element(QModelIndex const& parent) {
- if (auto node = get_inner_index(parent)) {
- auto last = tree->children_count(node);
- action_stack->emplace<core::actions::ListPushNew>(tree, node);
- }
- }
- void NodeModel::remove_list_item(QModelIndex const& parent, size_t position) {
- if (auto node = get_node_as<core::AbstractListLinked>(parent)) {
- if (node->is_editable_list()) {
- beginRemoveRows(parent, position, position);
- action_stack->emplace<core::actions::ListRemoveElement>(
- tree,
- get_inner_index(parent),
- position
- );
- endRemoveRows();
- }
- }
- }
- void NodeModel::convert_node(QModelIndex const& index, core::NodeInfo const* node_info) {
- if (auto parent_node = get_list_node(index.parent())) {
- auto node = get_node(index);
- auto new_node = core::make_node_with_name(node_info->name(), node, get_core_context());
- replace_node(index, new_node);
- }
- }
- bool NodeModel::node_is_connected(QModelIndex const& index) const {
- if (auto node = get_node(index)) {
- size_t count = 0;
- return find_nodes<bool>(
- node,
- [&count]() -> optional<bool> {
- if (count > 0)
- return make_optional(true);
- ++count;
- return {};
- }
- );
- }
- return false;
- }
- void NodeModel::disconnect_node(QModelIndex const& index) {
- replace_node(index, core::shallow_copy(*get_node(index)));
- }
- void NodeModel::connect_nodes(QList<QModelIndex> const& selection, QModelIndex const& source_index) {
- if (auto source = get_node(source_index)) {
- auto type = source->get_type();
- // TODO: check type of required by parent instead
- for (auto const& index : selection) {
- if (index != source_index && get_node(index)->get_type() == type) {
- replace_node(index, source);
- }
- }
- }
- }
- bool NodeModel::can_remove_node(QModelIndex const& index) const {
- if (auto node = get_node_as<core::AbstractListLinked>(index)) {
- if (node->link_count() == 0)
- return false;
- return index.parent().isValid() && get_link_type(index).accept(node->get_link(0)->get_type());
- }
- return false;
- }
- void NodeModel::remove_node(QModelIndex const& index) {
- if (auto node = get_node_as<core::AbstractListLinked>(index)) {
- auto child = node->get_link(0);
- replace_node(index, child);
- }
- }
- bool NodeModel::node_enabled(QModelIndex const& index) const {
- if (auto node = get_node(index)) {
- return node->enabled();
- }
- return false;
- }
- void NodeModel::node_set_enabled(QModelIndex const& index, bool value) {
- if (!action_stack)
- return;
- if (auto node = get_node(index)) {
- action_stack->emplace<core::actions::SetEnabled>(node, value);
- }
- }
- void NodeModel::replace_node(QModelIndex const& index, core::AbstractReference node) {
- if (!action_stack)
- return;
- if (auto parent = get_list_node(index.parent())) {
- // now tell Qt about our intentions
- size_t old_rows = 0;
- if (auto old_node = get_list_node(index))
- old_rows = old_node->link_count();
- size_t new_rows = 0;
- if (auto new_list = core::list_cast(node))
- new_rows = new_list->link_count();
- if (new_rows > old_rows)
- beginInsertRows(index, old_rows, new_rows-1);
- else if (new_rows < old_rows)
- beginRemoveRows(index, new_rows, old_rows-1);
- action_stack->emplace<core::actions::ChangeLink>(tree, get_inner_index(index), node);
- Q_EMIT dataChanged(index, index);
- if (new_rows > old_rows)
- endInsertRows();
- else if (new_rows < old_rows)
- endRemoveRows();
- }
- }
- void NodeModel::swap_nodes(QModelIndex const& a, QModelIndex const& b) {
- auto parent_a = get_list_node(a.parent());
- auto parent_b = get_list_node(b.parent());
- if (!parent_a || !parent_b)
- return;
- auto node_a = get_node(a);
- auto node_b = get_node(b);
- if (!node_a || !node_b)
- return;
- replace_node(a, node_b);
- replace_node(b, node_a);
- }
- bool NodeModel::can_move_up(size_t offset, QModelIndex const& /*parent*/) {
- return offset > 0;
- }
- bool NodeModel::can_move_down(size_t offset, QModelIndex const& parent) {
- return offset < get_list_node(parent)->link_count()-1;
- }
- void NodeModel::move_up(size_t offset, QModelIndex const& parent) {
- swap_nodes(index(offset, 0, parent), index(offset-1, 0, parent));
- }
- void NodeModel::move_down(size_t offset, QModelIndex const& parent) {
- swap_nodes(index(offset, 0, parent), index(offset+1, 0, parent));
- }
- core::TypeConstraint NodeModel::get_link_type(QModelIndex const& index) const {
- if (auto parent = get_node_as<core::AbstractListLinked>(index.parent()))
- return parent->get_link_type(get_node_index(index));
- return {};
- }
- core::AbstractReference NodeModel::get_node(QModelIndex const& index) const {
- if (!index.isValid())
- return nullptr;
- if (!index.parent().isValid())
- return root;
- if (auto pnode = get_node(index.parent())) {
- if (auto parent_node = dynamic_pointer_cast<core::AbstractListLinked>(pnode))
- if ((size_t) index.row() < parent_node->link_count())
- return parent_node->get_link(index.row());
- }
- return nullptr;
- }
- size_t NodeModel::get_node_index(QModelIndex const& index) const {
- return index.row();
- }
- QModelIndex NodeModel::parent(QModelIndex const& index) const {
- return report_tree_errors<QModelIndex>([&]() {
- return from_inner_index(tree->parent(get_inner_index(index)));
- });
- }
- int NodeModel::rowCount(QModelIndex const& parent) const {
- return report_tree_errors<int>([&]() {
- return tree->children_count(get_inner_index(parent));
- });
- }
- int NodeModel::columnCount(QModelIndex const& /*parent*/) const {
- return 2;
- }
- } // namespace rainynite::studio
|