ManualModel.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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. auto path = page->name();
  47. if (path.is_error())
  48. return {};
  49. return path.release_value();
  50. }
  51. Optional<String> ManualModel::page_path(const GUI::ModelIndex& index) const
  52. {
  53. if (!index.is_valid())
  54. return {};
  55. auto* node = static_cast<Manual::Node const*>(index.internal_data());
  56. auto page = node->document();
  57. if (!page)
  58. return {};
  59. auto path = page->path();
  60. if (path.is_error())
  61. return {};
  62. return path.release_value();
  63. }
  64. ErrorOr<StringView> ManualModel::page_view(String const& path) const
  65. {
  66. if (path.is_empty())
  67. return StringView {};
  68. {
  69. // Check if we've got it cached already.
  70. auto mapped_file = m_mapped_files.get(path);
  71. if (mapped_file.has_value())
  72. return StringView { mapped_file.value()->bytes() };
  73. }
  74. auto file = TRY(Core::MappedFile::map(path));
  75. StringView view { file->bytes() };
  76. m_mapped_files.set(path, move(file));
  77. return view;
  78. }
  79. Optional<String> ManualModel::page_and_section(const GUI::ModelIndex& index) const
  80. {
  81. if (!index.is_valid())
  82. return {};
  83. auto* node = static_cast<Manual::Node const*>(index.internal_data());
  84. if (!node->is_page())
  85. return {};
  86. auto* page = static_cast<Manual::PageNode const*>(node);
  87. auto* section = static_cast<Manual::SectionNode const*>(page->parent());
  88. auto page_name = page->name();
  89. if (page_name.is_error())
  90. return {};
  91. auto name = String::formatted("{}({})", page_name.release_value(), section->section_name());
  92. if (name.is_error())
  93. return {};
  94. return name.release_value();
  95. }
  96. GUI::ModelIndex ManualModel::index(int row, int column, const GUI::ModelIndex& parent_index) const
  97. {
  98. if (!parent_index.is_valid())
  99. return create_index(row, column, Manual::sections[row].ptr());
  100. auto* parent = static_cast<Manual::Node const*>(parent_index.internal_data());
  101. auto const children = parent->children();
  102. if (children.is_error())
  103. return {};
  104. auto child = children.value()[row];
  105. return create_index(row, column, child.ptr());
  106. }
  107. GUI::ModelIndex ManualModel::parent_index(const GUI::ModelIndex& index) const
  108. {
  109. if (!index.is_valid())
  110. return {};
  111. auto* child = static_cast<Manual::Node const*>(index.internal_data());
  112. auto* parent = child->parent();
  113. if (parent == nullptr)
  114. return {};
  115. if (parent->parent() == nullptr) {
  116. for (size_t row = 0; row < Manual::sections.size(); row++)
  117. if (Manual::sections[row].ptr() == parent)
  118. return create_index(row, 0, parent);
  119. VERIFY_NOT_REACHED();
  120. }
  121. auto maybe_children = parent->parent()->children();
  122. if (maybe_children.is_error())
  123. return {};
  124. auto children = maybe_children.release_value();
  125. for (size_t row = 0; row < children.size(); row++) {
  126. Manual::Node* child_at_row = children[row];
  127. if (child_at_row == parent)
  128. return create_index(row, 0, parent);
  129. }
  130. VERIFY_NOT_REACHED();
  131. }
  132. int ManualModel::row_count(const GUI::ModelIndex& index) const
  133. {
  134. if (!index.is_valid())
  135. return static_cast<int>(Manual::sections.size());
  136. auto* node = static_cast<Manual::Node const*>(index.internal_data());
  137. auto maybe_children = node->children();
  138. if (maybe_children.is_error())
  139. return 0;
  140. return static_cast<int>(maybe_children.value().size());
  141. }
  142. int ManualModel::column_count(const GUI::ModelIndex&) const
  143. {
  144. return 1;
  145. }
  146. GUI::Variant ManualModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
  147. {
  148. auto* node = static_cast<Manual::Node const*>(index.internal_data());
  149. switch (role) {
  150. case GUI::ModelRole::Search:
  151. if (!node->is_page())
  152. return {};
  153. if (auto path = page_path(index); path.has_value())
  154. if (auto page = page_view(path.release_value()); !page.is_error())
  155. // FIXME: We already provide String, but GUI::Variant still needs DeprecatedString.
  156. return DeprecatedString(page.release_value());
  157. return {};
  158. case GUI::ModelRole::Display:
  159. if (auto name = node->name(); !name.is_error())
  160. return name.release_value();
  161. return {};
  162. case GUI::ModelRole::Icon:
  163. if (node->is_page())
  164. return m_page_icon;
  165. if (node->is_open())
  166. return m_section_open_icon;
  167. return m_section_icon;
  168. default:
  169. return {};
  170. }
  171. }
  172. void ManualModel::update_section_node_on_toggle(const GUI::ModelIndex& index, bool const open)
  173. {
  174. auto* node = static_cast<Manual::Node*>(index.internal_data());
  175. if (is<Manual::SectionNode>(*node))
  176. static_cast<Manual::SectionNode*>(node)->set_open(open);
  177. }
  178. TriState ManualModel::data_matches(const GUI::ModelIndex& index, const GUI::Variant& term) const
  179. {
  180. auto name = page_name(index);
  181. if (!name.has_value())
  182. return TriState::False;
  183. if (name.value().bytes_as_string_view().contains(term.as_string(), CaseSensitivity::CaseInsensitive))
  184. return TriState::True;
  185. auto path = page_path(index);
  186. // 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.
  187. if (!path.has_value())
  188. return TriState::False;
  189. auto view_result = page_view(path.release_value());
  190. if (view_result.is_error() || view_result.value().is_empty())
  191. return TriState::False;
  192. return view_result.value().contains(term.as_string(), CaseSensitivity::CaseInsensitive) ? TriState::True : TriState::False;
  193. }