ProjectTemplatesModel.cpp 3.8 KB

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