ProjectTemplatesModel.cpp 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /*
  2. * Copyright (c) 2021, Nick Vella <nick@nxk.io>
  3. * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include "ProjectTemplatesModel.h"
  8. #include <AK/LexicalPath.h>
  9. #include <AK/QuickSort.h>
  10. #include <LibCore/DirIterator.h>
  11. #include <LibGUI/Variant.h>
  12. #include <LibGfx/TextAlignment.h>
  13. #include <stdio.h>
  14. namespace HackStudio {
  15. ProjectTemplatesModel::ProjectTemplatesModel()
  16. : m_templates()
  17. , m_mapping()
  18. {
  19. auto watcher_or_error = Core::FileWatcher::create();
  20. if (!watcher_or_error.is_error()) {
  21. m_file_watcher = watcher_or_error.release_value();
  22. m_file_watcher->on_change = [&](auto) {
  23. invalidate();
  24. };
  25. auto watch_result = m_file_watcher->add_watch(
  26. ProjectTemplate::templates_path(),
  27. Core::FileWatcherEvent::Type::ChildCreated
  28. | Core::FileWatcherEvent::Type::ChildDeleted);
  29. if (watch_result.is_error()) {
  30. warnln("Unable to watch templates directory, templates will not automatically refresh. Error: {}", watch_result.error());
  31. }
  32. } else {
  33. warnln("Unable to watch templates directory, templates will not automatically refresh. Error: {}", watcher_or_error.error());
  34. }
  35. rescan_templates();
  36. }
  37. ProjectTemplatesModel::~ProjectTemplatesModel()
  38. {
  39. }
  40. int ProjectTemplatesModel::row_count(const GUI::ModelIndex&) const
  41. {
  42. return m_mapping.size();
  43. }
  44. int ProjectTemplatesModel::column_count(const GUI::ModelIndex&) const
  45. {
  46. return Column::__Count;
  47. }
  48. String ProjectTemplatesModel::column_name(int column) const
  49. {
  50. switch (column) {
  51. case Column::Icon:
  52. return "Icon";
  53. case Column::Id:
  54. return "ID";
  55. case Column::Name:
  56. return "Name";
  57. }
  58. VERIFY_NOT_REACHED();
  59. }
  60. GUI::Variant ProjectTemplatesModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
  61. {
  62. if (static_cast<size_t>(index.row()) >= m_mapping.size())
  63. return {};
  64. if (role == GUI::ModelRole::TextAlignment)
  65. return Gfx::TextAlignment::CenterLeft;
  66. if (role == GUI::ModelRole::Display) {
  67. switch (index.column()) {
  68. case Column::Name:
  69. return m_mapping[index.row()]->name();
  70. case Column::Id:
  71. return m_mapping[index.row()]->id();
  72. }
  73. }
  74. if (role == GUI::ModelRole::Icon) {
  75. return m_mapping[index.row()]->icon();
  76. }
  77. return {};
  78. }
  79. RefPtr<ProjectTemplate> ProjectTemplatesModel::template_for_index(const GUI::ModelIndex& index)
  80. {
  81. if (static_cast<size_t>(index.row()) >= m_mapping.size())
  82. return {};
  83. return m_mapping[index.row()];
  84. }
  85. void ProjectTemplatesModel::update()
  86. {
  87. rescan_templates();
  88. did_update();
  89. }
  90. void ProjectTemplatesModel::rescan_templates()
  91. {
  92. m_templates.clear();
  93. // Iterate over template manifest INI files in the templates path
  94. Core::DirIterator di(ProjectTemplate::templates_path(), Core::DirIterator::SkipDots);
  95. if (di.has_error()) {
  96. warnln("DirIterator: {}", di.error_string());
  97. return;
  98. }
  99. while (di.has_next()) {
  100. auto full_path = LexicalPath(di.next_full_path());
  101. if (!full_path.has_extension(".ini"))
  102. continue;
  103. auto project_template = ProjectTemplate::load_from_manifest(full_path.string());
  104. if (!project_template) {
  105. warnln("Template manifest {} is invalid.", full_path.string());
  106. continue;
  107. }
  108. m_templates.append(project_template.release_nonnull());
  109. }
  110. // Enumerate the loaded projects into a sorted mapping, by priority value descending.
  111. m_mapping.clear();
  112. for (auto& project_template : m_templates)
  113. m_mapping.append(&project_template);
  114. quick_sort(m_mapping, [](auto a, auto b) {
  115. return a->priority() > b->priority();
  116. });
  117. }
  118. }