ManualModel.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /*
  2. * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "ManualModel.h"
  7. #include <AK/Try.h>
  8. #include <AK/Utf8View.h>
  9. #include <LibManual/Node.h>
  10. #include <LibManual/PageNode.h>
  11. #include <LibManual/SectionNode.h>
  12. ManualModel::ManualModel()
  13. {
  14. m_section_open_icon.set_bitmap_for_size(16, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/book-open.png"sv).release_value_but_fixme_should_propagate_errors());
  15. m_section_icon.set_bitmap_for_size(16, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/book.png"sv).release_value_but_fixme_should_propagate_errors());
  16. m_page_icon.set_bitmap_for_size(16, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/filetype-unknown.png"sv).release_value_but_fixme_should_propagate_errors());
  17. }
  18. Optional<GUI::ModelIndex> ManualModel::index_from_path(StringView path) const
  19. {
  20. for (int section = 0; section < row_count(); ++section) {
  21. auto parent_index = index(section, 0);
  22. for (int row = 0; row < row_count(parent_index); ++row) {
  23. auto child_index = index(row, 0, parent_index);
  24. auto* node = static_cast<Manual::Node const*>(child_index.internal_data());
  25. if (!node->is_page())
  26. continue;
  27. auto* page = static_cast<Manual::PageNode const*>(node);
  28. auto const maybe_path = page->path();
  29. if (maybe_path.is_error())
  30. return {};
  31. if (maybe_path.value().bytes_as_string_view() != path)
  32. continue;
  33. return child_index;
  34. }
  35. }
  36. return {};
  37. }
  38. Optional<String> ManualModel::page_name(const GUI::ModelIndex& index) const
  39. {
  40. if (!index.is_valid())
  41. return {};
  42. auto* node = static_cast<Manual::Node const*>(index.internal_data());
  43. if (!node->is_page())
  44. return {};
  45. auto* page = static_cast<Manual::PageNode const*>(node);
  46. return page->name();
  47. }
  48. Optional<String> ManualModel::page_path(const GUI::ModelIndex& index) const
  49. {
  50. if (!index.is_valid())
  51. return {};
  52. auto* node = static_cast<Manual::Node const*>(index.internal_data());
  53. if (!node->is_page())
  54. return {};
  55. auto* page = static_cast<Manual::PageNode const*>(node);
  56. auto path = page->path();
  57. if (path.is_error())
  58. return {};
  59. return path.release_value();
  60. }
  61. ErrorOr<StringView> ManualModel::page_view(String const& path) const
  62. {
  63. if (path.is_empty())
  64. return StringView {};
  65. {
  66. // Check if we've got it cached already.
  67. auto mapped_file = m_mapped_files.get(path);
  68. if (mapped_file.has_value())
  69. return StringView { mapped_file.value()->bytes() };
  70. }
  71. auto file = TRY(Core::MappedFile::map(path));
  72. StringView view { file->bytes() };
  73. m_mapped_files.set(path, move(file));
  74. return view;
  75. }
  76. Optional<String> ManualModel::page_and_section(const GUI::ModelIndex& index) const
  77. {
  78. if (!index.is_valid())
  79. return {};
  80. auto* node = static_cast<Manual::Node const*>(index.internal_data());
  81. if (!node->is_page())
  82. return {};
  83. auto* page = static_cast<Manual::PageNode const*>(node);
  84. auto* section = static_cast<Manual::SectionNode const*>(page->parent());
  85. auto page_name = page->name();
  86. if (page_name.is_error())
  87. return {};
  88. auto name = String::formatted("{}({})", page_name.release_value(), section->section_name());
  89. if (name.is_error())
  90. return {};
  91. return name.release_value();
  92. }
  93. GUI::ModelIndex ManualModel::index(int row, int column, const GUI::ModelIndex& parent_index) const
  94. {
  95. if (!parent_index.is_valid())
  96. return create_index(row, column, Manual::sections[row].ptr());
  97. auto* parent = static_cast<Manual::Node const*>(parent_index.internal_data());
  98. auto* child = &parent->children()[row];
  99. return create_index(row, column, child);
  100. }
  101. GUI::ModelIndex ManualModel::parent_index(const GUI::ModelIndex& index) const
  102. {
  103. if (!index.is_valid())
  104. return {};
  105. auto* child = static_cast<Manual::Node const*>(index.internal_data());
  106. auto* parent = child->parent();
  107. if (parent == nullptr)
  108. return {};
  109. if (parent->parent() == nullptr) {
  110. for (size_t row = 0; row < sizeof(Manual::sections) / sizeof(Manual::sections[0]); row++)
  111. if (Manual::sections[row].ptr() == parent)
  112. return create_index(row, 0, parent);
  113. VERIFY_NOT_REACHED();
  114. }
  115. for (size_t row = 0; row < parent->parent()->children().size(); row++) {
  116. Manual::Node* child_at_row = &parent->parent()->children()[row];
  117. if (child_at_row == parent)
  118. return create_index(row, 0, parent);
  119. }
  120. VERIFY_NOT_REACHED();
  121. }
  122. int ManualModel::row_count(const GUI::ModelIndex& index) const
  123. {
  124. if (!index.is_valid())
  125. return sizeof(Manual::sections) / sizeof(Manual::sections[0]);
  126. auto* node = static_cast<Manual::Node const*>(index.internal_data());
  127. return node->children().size();
  128. }
  129. int ManualModel::column_count(const GUI::ModelIndex&) const
  130. {
  131. return 1;
  132. }
  133. GUI::Variant ManualModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
  134. {
  135. auto* node = static_cast<Manual::Node const*>(index.internal_data());
  136. switch (role) {
  137. case GUI::ModelRole::Search:
  138. if (!node->is_page())
  139. return {};
  140. if (auto path = page_path(index); path.has_value())
  141. if (auto page = page_view(path.release_value()); !page.is_error())
  142. // FIXME: We already provide String, but GUI::Variant still needs DeprecatedString.
  143. return DeprecatedString(page.release_value());
  144. return {};
  145. case GUI::ModelRole::Display:
  146. return node->name();
  147. case GUI::ModelRole::Icon:
  148. if (node->is_page())
  149. return m_page_icon;
  150. if (node->is_open())
  151. return m_section_open_icon;
  152. return m_section_icon;
  153. default:
  154. return {};
  155. }
  156. }
  157. void ManualModel::update_section_node_on_toggle(const GUI::ModelIndex& index, bool const open)
  158. {
  159. auto* node = static_cast<Manual::SectionNode*>(index.internal_data());
  160. node->set_open(open);
  161. }
  162. TriState ManualModel::data_matches(const GUI::ModelIndex& index, const GUI::Variant& term) const
  163. {
  164. auto name = page_name(index);
  165. if (!name.has_value())
  166. return TriState::False;
  167. if (name.value().bytes_as_string_view().contains(term.as_string(), CaseSensitivity::CaseInsensitive))
  168. return TriState::True;
  169. auto path = page_path(index);
  170. // NOTE: This is slightly inaccurate, as page_path can also fail due to OOM. We consider it acceptable to have a data mismatch in that case.
  171. if (!path.has_value())
  172. return TriState::False;
  173. auto view_result = page_view(path.release_value());
  174. if (view_result.is_error() || view_result.value().is_empty())
  175. return TriState::False;
  176. return view_result.value().contains(term.as_string(), CaseSensitivity::CaseInsensitive) ? TriState::True : TriState::False;
  177. }