node_model.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. /* node_model.cpp - node tree model wrapper
  2. * Copyright (C) 2017-2018 caryoscelus
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include <fmt/format.h>
  18. #include <QDebug>
  19. #include <core/node_info/node_info.h>
  20. #include <core/node_info/copy.h>
  21. #include <core/node/make.h>
  22. #include <core/node/abstract_node.h>
  23. #include <core/node_tree/exceptions.h>
  24. #include <core/util/nothing.h>
  25. #include <core/serialize/node_writer.h>
  26. #include <core/action_stack.h>
  27. #include <core/document.h>
  28. #include <core/actions/change_link.h>
  29. #include <core/actions/custom_property.h>
  30. #include <core/actions/set_enabled.h>
  31. #include <core/actions/list.h>
  32. #include <util/strings.h>
  33. #include "node_model.h"
  34. using namespace fmt::literals;
  35. namespace rainynite::studio {
  36. static const size_t MAX_VALUE_LENGTH = 32;
  37. template <typename T, typename F>
  38. T report_tree_errors(F f) {
  39. try {
  40. return f();
  41. } catch (core::NodeTreeError const&) {
  42. // TODO: report
  43. return {};
  44. }
  45. }
  46. NodeModel::NodeModel(core::AbstractReference root_, shared_ptr<core::ActionStack> action_stack_, QObject* parent) :
  47. QAbstractItemModel(parent),
  48. root(root_),
  49. action_stack(action_stack_)
  50. {
  51. if (auto document = dynamic_cast<core::AbstractDocument*>(root.get())) {
  52. tree = document->get_tree();
  53. } else {
  54. qWarning() << "NodeModel: creating new tree. .this probably shouldn't happen";
  55. tree = root_ ? make_shared<core::NodeTree>(root_) : nullptr;
  56. }
  57. if (tree) {
  58. tree->connect_boost(tree->start_reload_signal, [this](auto parent) {
  59. auto parent_idx = from_inner_index(parent);
  60. beginRemoveRows(parent_idx, 0, tree->children_count(parent)-1);
  61. });
  62. tree->connect_boost(tree->start_adding_signal, [this](auto parent, size_t count) {
  63. endRemoveRows();
  64. auto parent_idx = from_inner_index(parent);
  65. beginInsertRows(parent_idx, 0, count-1);
  66. });
  67. tree->connect_boost(tree->end_reload_signal, [this]() {
  68. endInsertRows();
  69. });
  70. }
  71. }
  72. NodeModel::~NodeModel() {
  73. }
  74. void NodeModel::time_changed(core::Time time) {
  75. ContextListener::time_changed(time);
  76. Q_EMIT dataChanged(index(0, 1), index(2, 1));
  77. }
  78. QVariant NodeModel::data(QModelIndex const& index, int role) const {
  79. if (!index.isValid())
  80. return {};
  81. if (index.column() == 0) {
  82. switch (role) {
  83. case Qt::DisplayRole: {
  84. string node_role = "document";
  85. if (auto parent_node = get_node_as<core::AbstractNode>(parent(index)))
  86. node_role = parent_node->get_name_at(index.row());
  87. else if (parent(index).isValid())
  88. node_role = "{}"_format(index.row());
  89. string type_name = "<Bad node!>";
  90. string maybe_name;
  91. if (auto node = get_node(index)) {
  92. type_name = core::node_name(*node);
  93. if (auto node_node = abstract_node_cast(node)) {
  94. if (auto name = node_node->name(); !name.empty())
  95. maybe_name = "[{}] "_format(name);
  96. }
  97. }
  98. return util::str("{}{}: {}"_format(maybe_name, node_role, type_name));
  99. } break;
  100. case Qt::ToolTipRole: {
  101. // TODO: node role documentation
  102. if (auto node = get_node_as<DocString>(index))
  103. return util::str(node->doc_string());
  104. return {};
  105. } break;
  106. default:
  107. return {};
  108. }
  109. } else {
  110. switch (role) {
  111. case Qt::DisplayRole: {
  112. if (auto node = get_node(index)) {
  113. auto value = node->get_any(get_core_context());
  114. try {
  115. auto s = core::serialize::value_to_string(value);
  116. auto s_fixed = s.substr(0, MAX_VALUE_LENGTH);
  117. auto s_fixed2 = s_fixed.substr(0, s_fixed.find('\n'));
  118. if (s.size() > s_fixed2.size())
  119. s_fixed2 += "...";
  120. return util::str(s_fixed2);
  121. } catch (class_init::RuntimeTypeError const& ex) {
  122. return {};
  123. }
  124. }
  125. return {};
  126. } break;
  127. default:
  128. return {};
  129. }
  130. }
  131. }
  132. Qt::ItemFlags NodeModel::flags(QModelIndex const&) const {
  133. return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
  134. }
  135. QVariant NodeModel::headerData(int section, Qt::Orientation orientation, int role) const {
  136. if (orientation == Qt::Vertical)
  137. return {};
  138. switch (role) {
  139. case Qt::DisplayRole: {
  140. if (section == 0)
  141. return "node";
  142. else
  143. return "value";
  144. } break;
  145. default:
  146. return {};
  147. }
  148. }
  149. QModelIndex NodeModel::index(int row, int column, QModelIndex const& parent) const {
  150. return createIndex(row, column, (size_t)get_inner_index(parent, row));
  151. }
  152. core::NodeTreeIndex NodeModel::get_inner_index(QModelIndex const& parent, size_t i) const {
  153. if (tree == nullptr)
  154. return {};
  155. if (!parent.isValid()) {
  156. if (i == 0)
  157. return tree->get_root_index();
  158. else
  159. return tree->get_null_index();
  160. } else {
  161. return report_tree_errors<core::NodeTreeIndex>([&]() {
  162. return tree->index(get_inner_index(parent), i);
  163. });
  164. }
  165. }
  166. core::NodeTreeIndex NodeModel::get_inner_index(QModelIndex const& index) const {
  167. if (!index.isValid())
  168. return {};
  169. return index.internalId();
  170. }
  171. QModelIndex NodeModel::from_inner_index(core::NodeTreeIndex index) const {
  172. if (!index)
  173. return {};
  174. return report_tree_errors<QModelIndex>([&]() {
  175. return createIndex(tree->link_index(index), 0, (size_t)index);
  176. });
  177. }
  178. bool NodeModel::can_rename(QModelIndex const& index) const {
  179. return get_node_as<core::AbstractNode>(index) != nullptr;
  180. }
  181. QString NodeModel::get_name(QModelIndex const& index) const {
  182. return util::str(no_null(get_node_as<core::AbstractNode>(index))->name());
  183. }
  184. void NodeModel::set_name(QModelIndex const& index, QString const& name) {
  185. no_null(get_node_as<core::AbstractNode>(index))->set_name(util::str(name));
  186. }
  187. bool NodeModel::can_add_custom_property(QModelIndex const& parent) const {
  188. if (get_node_as<core::AbstractNode>(parent))
  189. return true;
  190. return false;
  191. }
  192. void NodeModel::add_empty_custom_property(QModelIndex const& parent, string const& name) {
  193. action_stack->emplace<core::actions::AddCustomProperty>(tree, get_inner_index(parent), name, core::make_value<Nothing>());
  194. // TODO: add rows
  195. }
  196. bool NodeModel::is_custom_property(QModelIndex const& index) const {
  197. if (auto parent = get_node_as<core::AbstractNode>(index.parent())) {
  198. auto i = index.row();
  199. return parent->get_name_at(i)[0] == '_';
  200. }
  201. return false;
  202. }
  203. void NodeModel::remove_custom_property(QModelIndex const& index) {
  204. if (auto parent = get_node_as<core::AbstractNode>(index.parent())) {
  205. auto i = index.row();
  206. beginRemoveRows(index.parent(), i, i);
  207. action_stack->emplace<core::actions::RemoveCustomProperty>(tree, get_inner_index(index.parent()), parent->get_name_at(i));
  208. endRemoveRows();
  209. }
  210. }
  211. bool NodeModel::can_clear_list(QModelIndex const& list) const {
  212. // TODO
  213. if (auto list_node = get_node_as<core::AbstractListLinked>(list))
  214. return list_node->is_editable_list();
  215. return false;
  216. }
  217. void NodeModel::clear_list(QModelIndex const& list) {
  218. if (auto idx = get_inner_index(list)) {
  219. using core::actions::ListClear;
  220. action_stack->emplace<ListClear>(tree, idx);
  221. }
  222. }
  223. bool NodeModel::can_add_element(QModelIndex const& parent) const {
  224. if (auto node = get_list_node(parent))
  225. return node->is_editable_list();
  226. return false;
  227. }
  228. void NodeModel::add_empty_element(QModelIndex const& parent) {
  229. if (auto node = get_inner_index(parent)) {
  230. auto last = tree->children_count(node);
  231. action_stack->emplace<core::actions::ListPushNew>(tree, node);
  232. }
  233. }
  234. void NodeModel::remove_list_item(QModelIndex const& parent, size_t position) {
  235. if (auto node = get_node_as<core::AbstractListLinked>(parent)) {
  236. if (node->is_editable_list()) {
  237. beginRemoveRows(parent, position, position);
  238. action_stack->emplace<core::actions::ListRemoveElement>(
  239. tree,
  240. get_inner_index(parent),
  241. position
  242. );
  243. endRemoveRows();
  244. }
  245. }
  246. }
  247. void NodeModel::convert_node(QModelIndex const& index, core::NodeInfo const* node_info) {
  248. if (auto parent_node = get_list_node(index.parent())) {
  249. auto node = get_node(index);
  250. auto new_node = core::make_node_with_name(node_info->name(), node, get_core_context());
  251. replace_node(index, new_node);
  252. }
  253. }
  254. bool NodeModel::node_is_connected(QModelIndex const& index) const {
  255. if (auto node = get_node(index)) {
  256. size_t count = 0;
  257. return find_nodes<bool>(
  258. node,
  259. [&count]() -> optional<bool> {
  260. if (count > 0)
  261. return make_optional(true);
  262. ++count;
  263. return {};
  264. }
  265. );
  266. }
  267. return false;
  268. }
  269. void NodeModel::disconnect_node(QModelIndex const& index) {
  270. replace_node(index, core::shallow_copy(*get_node(index)));
  271. }
  272. void NodeModel::connect_nodes(QList<QModelIndex> const& selection, QModelIndex const& source_index) {
  273. if (auto source = get_node(source_index)) {
  274. auto type = source->get_type();
  275. // TODO: check type of required by parent instead
  276. for (auto const& index : selection) {
  277. if (index != source_index && get_node(index)->get_type() == type) {
  278. replace_node(index, source);
  279. }
  280. }
  281. }
  282. }
  283. bool NodeModel::can_remove_node(QModelIndex const& index) const {
  284. if (auto node = get_node_as<core::AbstractListLinked>(index)) {
  285. if (node->link_count() == 0)
  286. return false;
  287. return index.parent().isValid() && get_link_type(index).accept(node->get_link(0)->get_type());
  288. }
  289. return false;
  290. }
  291. void NodeModel::remove_node(QModelIndex const& index) {
  292. if (auto node = get_node_as<core::AbstractListLinked>(index)) {
  293. auto child = node->get_link(0);
  294. replace_node(index, child);
  295. }
  296. }
  297. bool NodeModel::node_enabled(QModelIndex const& index) const {
  298. if (auto node = get_node(index)) {
  299. return node->enabled();
  300. }
  301. return false;
  302. }
  303. void NodeModel::node_set_enabled(QModelIndex const& index, bool value) {
  304. if (!action_stack)
  305. return;
  306. if (auto node = get_node(index)) {
  307. action_stack->emplace<core::actions::SetEnabled>(node, value);
  308. }
  309. }
  310. void NodeModel::replace_node(QModelIndex const& index, core::AbstractReference node) {
  311. if (!action_stack)
  312. return;
  313. if (auto parent = get_list_node(index.parent())) {
  314. // now tell Qt about our intentions
  315. size_t old_rows = 0;
  316. if (auto old_node = get_list_node(index))
  317. old_rows = old_node->link_count();
  318. size_t new_rows = 0;
  319. if (auto new_list = core::list_cast(node))
  320. new_rows = new_list->link_count();
  321. if (new_rows > old_rows)
  322. beginInsertRows(index, old_rows, new_rows-1);
  323. else if (new_rows < old_rows)
  324. beginRemoveRows(index, new_rows, old_rows-1);
  325. action_stack->emplace<core::actions::ChangeLink>(tree, get_inner_index(index), node);
  326. Q_EMIT dataChanged(index, index);
  327. if (new_rows > old_rows)
  328. endInsertRows();
  329. else if (new_rows < old_rows)
  330. endRemoveRows();
  331. }
  332. }
  333. void NodeModel::swap_nodes(QModelIndex const& a, QModelIndex const& b) {
  334. auto parent_a = get_list_node(a.parent());
  335. auto parent_b = get_list_node(b.parent());
  336. if (!parent_a || !parent_b)
  337. return;
  338. auto node_a = get_node(a);
  339. auto node_b = get_node(b);
  340. if (!node_a || !node_b)
  341. return;
  342. replace_node(a, node_b);
  343. replace_node(b, node_a);
  344. }
  345. bool NodeModel::can_move_up(size_t offset, QModelIndex const& /*parent*/) {
  346. return offset > 0;
  347. }
  348. bool NodeModel::can_move_down(size_t offset, QModelIndex const& parent) {
  349. return offset < get_list_node(parent)->link_count()-1;
  350. }
  351. void NodeModel::move_up(size_t offset, QModelIndex const& parent) {
  352. swap_nodes(index(offset, 0, parent), index(offset-1, 0, parent));
  353. }
  354. void NodeModel::move_down(size_t offset, QModelIndex const& parent) {
  355. swap_nodes(index(offset, 0, parent), index(offset+1, 0, parent));
  356. }
  357. core::TypeConstraint NodeModel::get_link_type(QModelIndex const& index) const {
  358. if (auto parent = get_node_as<core::AbstractListLinked>(index.parent()))
  359. return parent->get_link_type(get_node_index(index));
  360. return {};
  361. }
  362. core::AbstractReference NodeModel::get_node(QModelIndex const& index) const {
  363. if (!index.isValid())
  364. return nullptr;
  365. if (!index.parent().isValid())
  366. return root;
  367. if (auto pnode = get_node(index.parent())) {
  368. if (auto parent_node = dynamic_pointer_cast<core::AbstractListLinked>(pnode))
  369. if ((size_t) index.row() < parent_node->link_count())
  370. return parent_node->get_link(index.row());
  371. }
  372. return nullptr;
  373. }
  374. size_t NodeModel::get_node_index(QModelIndex const& index) const {
  375. return index.row();
  376. }
  377. QModelIndex NodeModel::parent(QModelIndex const& index) const {
  378. return report_tree_errors<QModelIndex>([&]() {
  379. return from_inner_index(tree->parent(get_inner_index(index)));
  380. });
  381. }
  382. int NodeModel::rowCount(QModelIndex const& parent) const {
  383. return report_tree_errors<int>([&]() {
  384. return tree->children_count(get_inner_index(parent));
  385. });
  386. }
  387. int NodeModel::columnCount(QModelIndex const& /*parent*/) const {
  388. return 2;
  389. }
  390. } // namespace rainynite::studio