mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 18:17:44 +00:00
Help+LibManual: Move non-UI-specific manual handling to LibManual
This is a first step in deduplicating code within and across Help and man. Because LibManual also doesn't contain any DeprecatedString, some adjustments to Help's string handling is included, just to interoperate with LibManual better. Further work in this area mostly requires String APIs in LibGUI.
This commit is contained in:
parent
78353ec184
commit
ad6a55e1f0
18 changed files with 339 additions and 265 deletions
|
@ -12,8 +12,6 @@ set(SOURCES
|
||||||
main.cpp
|
main.cpp
|
||||||
MainWidget.cpp
|
MainWidget.cpp
|
||||||
ManualModel.cpp
|
ManualModel.cpp
|
||||||
ManualPageNode.cpp
|
|
||||||
ManualSectionNode.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(GENERATED_SOURCES
|
set(GENERATED_SOURCES
|
||||||
|
@ -21,4 +19,5 @@ set(GENERATED_SOURCES
|
||||||
)
|
)
|
||||||
|
|
||||||
serenity_app(Help ICON app-help)
|
serenity_app(Help ICON app-help)
|
||||||
target_link_libraries(Help PRIVATE LibCore LibWebView LibWeb LibMarkdown LibGfx LibGUI LibDesktop LibMain)
|
target_link_libraries(Help PRIVATE LibCore LibWebView LibWeb LibMarkdown LibGfx LibGUI LibDesktop LibMain LibManual)
|
||||||
|
link_with_locale_data(Help)
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "MainWidget.h"
|
#include "MainWidget.h"
|
||||||
#include <AK/DeprecatedString.h>
|
#include <AK/LexicalPath.h>
|
||||||
|
#include <AK/String.h>
|
||||||
|
#include <AK/StringView.h>
|
||||||
#include <AK/URL.h>
|
#include <AK/URL.h>
|
||||||
#include <Applications/Help/HelpWindowGML.h>
|
#include <Applications/Help/HelpWindowGML.h>
|
||||||
#include <LibCore/ArgsParser.h>
|
#include <LibCore/ArgsParser.h>
|
||||||
|
@ -30,6 +32,9 @@
|
||||||
#include <LibGUI/Window.h>
|
#include <LibGUI/Window.h>
|
||||||
#include <LibGfx/Bitmap.h>
|
#include <LibGfx/Bitmap.h>
|
||||||
#include <LibMain/Main.h>
|
#include <LibMain/Main.h>
|
||||||
|
#include <LibManual/Node.h>
|
||||||
|
#include <LibManual/PageNode.h>
|
||||||
|
#include <LibManual/SectionNode.h>
|
||||||
#include <LibMarkdown/Document.h>
|
#include <LibMarkdown/Document.h>
|
||||||
|
|
||||||
namespace Help {
|
namespace Help {
|
||||||
|
@ -66,25 +71,25 @@ MainWidget::MainWidget()
|
||||||
}
|
}
|
||||||
auto& search_model = *static_cast<GUI::FilteringProxyModel*>(view_model);
|
auto& search_model = *static_cast<GUI::FilteringProxyModel*>(view_model);
|
||||||
auto const& mapped_index = search_model.map(index);
|
auto const& mapped_index = search_model.map(index);
|
||||||
DeprecatedString path = m_manual_model->page_path(mapped_index);
|
auto path = m_manual_model->page_path(mapped_index);
|
||||||
if (path.is_null()) {
|
if (!path.has_value()) {
|
||||||
m_web_view->load_empty_document();
|
m_web_view->load_empty_document();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_browse_view->selection().clear();
|
m_browse_view->selection().clear();
|
||||||
m_browse_view->selection().add(mapped_index);
|
m_browse_view->selection().add(mapped_index);
|
||||||
m_history.push(path);
|
m_history.push(path.value());
|
||||||
open_page(path);
|
open_page(path.value());
|
||||||
};
|
};
|
||||||
|
|
||||||
m_browse_view = find_descendant_of_type_named<GUI::TreeView>("browse_view");
|
m_browse_view = find_descendant_of_type_named<GUI::TreeView>("browse_view");
|
||||||
m_browse_view->on_selection_change = [this] {
|
m_browse_view->on_selection_change = [this] {
|
||||||
DeprecatedString path = m_manual_model->page_path(m_browse_view->selection().first());
|
auto path = m_manual_model->page_path(m_browse_view->selection().first());
|
||||||
if (path.is_null())
|
if (!path.has_value())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_history.push(path);
|
m_history.push(path.value());
|
||||||
open_page(path);
|
open_page(path.value());
|
||||||
};
|
};
|
||||||
m_browse_view->on_toggle = [this](GUI::ModelIndex const& index, bool open) {
|
m_browse_view->on_toggle = [this](GUI::ModelIndex const& index, bool open) {
|
||||||
m_manual_model->update_section_node_on_toggle(index, open);
|
m_manual_model->update_section_node_on_toggle(index, open);
|
||||||
|
@ -105,7 +110,7 @@ MainWidget::MainWidget()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_history.push(path);
|
m_history.push(path);
|
||||||
open_page(path);
|
open_page(MUST(String::from_utf8(path)));
|
||||||
} else if (url.scheme() == "help") {
|
} else if (url.scheme() == "help") {
|
||||||
if (url.host() == "man") {
|
if (url.host() == "man") {
|
||||||
if (url.paths().size() != 2) {
|
if (url.paths().size() != 2) {
|
||||||
|
@ -138,23 +143,17 @@ MainWidget::MainWidget()
|
||||||
|
|
||||||
m_go_back_action = GUI::CommonActions::make_go_back_action([this](auto&) {
|
m_go_back_action = GUI::CommonActions::make_go_back_action([this](auto&) {
|
||||||
m_history.go_back();
|
m_history.go_back();
|
||||||
open_page(m_history.current());
|
open_page(MUST(String::from_deprecated_string(m_history.current())));
|
||||||
});
|
});
|
||||||
|
|
||||||
m_go_forward_action = GUI::CommonActions::make_go_forward_action([this](auto&) {
|
m_go_forward_action = GUI::CommonActions::make_go_forward_action([this](auto&) {
|
||||||
m_history.go_forward();
|
m_history.go_forward();
|
||||||
open_page(m_history.current());
|
open_page(MUST(String::from_deprecated_string(m_history.current())));
|
||||||
});
|
});
|
||||||
|
|
||||||
m_go_back_action->set_enabled(false);
|
m_go_back_action->set_enabled(false);
|
||||||
m_go_forward_action->set_enabled(false);
|
m_go_forward_action->set_enabled(false);
|
||||||
|
|
||||||
m_go_home_action = GUI::CommonActions::make_go_home_action([this](auto&) {
|
|
||||||
DeprecatedString path = "/usr/share/man/man7/Help-index.md";
|
|
||||||
m_history.push(path);
|
|
||||||
open_page(path);
|
|
||||||
});
|
|
||||||
|
|
||||||
m_copy_action = GUI::CommonActions::make_copy_action([this](auto&) {
|
m_copy_action = GUI::CommonActions::make_copy_action([this](auto&) {
|
||||||
auto selected_text = m_web_view->selected_text();
|
auto selected_text = m_web_view->selected_text();
|
||||||
if (!selected_text.is_empty())
|
if (!selected_text.is_empty())
|
||||||
|
@ -174,37 +173,27 @@ MainWidget::MainWidget()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::set_start_page(StringView start_page, u32 section)
|
ErrorOr<void> MainWidget::set_start_page(StringView start_page, u32 section)
|
||||||
{
|
{
|
||||||
bool set_start_page = false;
|
bool set_start_page = false;
|
||||||
if (!start_page.is_null()) {
|
if (!start_page.is_null()) {
|
||||||
if (section != 0) {
|
if (section != 0) {
|
||||||
// > Help [section] [name]
|
// > Help [section] [name]
|
||||||
DeprecatedString path = DeprecatedString::formatted("/usr/share/man/man{}/{}.md", section, start_page);
|
String path = TRY(String::formatted("/usr/share/man/man{}/{}.md", section, start_page));
|
||||||
m_history.push(path);
|
m_history.push(path);
|
||||||
open_page(path);
|
open_page(path);
|
||||||
set_start_page = true;
|
set_start_page = true;
|
||||||
} else if (URL url = URL::create_with_url_or_path(start_page); url.is_valid() && url.path().ends_with(".md"sv)) {
|
} else if (URL url = URL::create_with_url_or_path(start_page); url.is_valid() && url.path().ends_with(".md"sv)) {
|
||||||
// > Help [/path/to/documentation/file.md]
|
// > Help [/path/to/documentation/file.md]
|
||||||
m_history.push(url.path());
|
m_history.push(url.path());
|
||||||
open_page(url.path());
|
open_page(TRY(String::from_deprecated_string(url.path())));
|
||||||
set_start_page = true;
|
set_start_page = true;
|
||||||
} else {
|
} else {
|
||||||
// > Help [query]
|
// > Help [query]
|
||||||
|
|
||||||
// First, see if we can find the page by name
|
// First, see if we can find the page by name
|
||||||
char const* sections[] = {
|
for (auto s : Manual::section_numbers) {
|
||||||
"1",
|
String path = TRY(String::formatted("/usr/share/man/man{}/{}.md", s, start_page));
|
||||||
"2",
|
|
||||||
"3",
|
|
||||||
"4",
|
|
||||||
"5",
|
|
||||||
"6",
|
|
||||||
"7",
|
|
||||||
"8"
|
|
||||||
};
|
|
||||||
for (auto s : sections) {
|
|
||||||
DeprecatedString path = DeprecatedString::formatted("/usr/share/man/man{}/{}.md", s, start_page);
|
|
||||||
if (Core::File::exists(path)) {
|
if (Core::File::exists(path)) {
|
||||||
m_history.push(path);
|
m_history.push(path);
|
||||||
open_page(path);
|
open_page(path);
|
||||||
|
@ -225,10 +214,17 @@ void MainWidget::set_start_page(StringView start_page, u32 section)
|
||||||
}
|
}
|
||||||
if (!set_start_page)
|
if (!set_start_page)
|
||||||
m_go_home_action->activate();
|
m_go_home_action->activate();
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> MainWidget::initialize_fallibles(GUI::Window& window)
|
ErrorOr<void> MainWidget::initialize_fallibles(GUI::Window& window)
|
||||||
{
|
{
|
||||||
|
static String help_index_path = TRY(String::from_utf8("/usr/share/man/man7/Help-index.md"sv));
|
||||||
|
m_go_home_action = GUI::CommonActions::make_go_home_action([this](auto&) {
|
||||||
|
m_history.push(help_index_path);
|
||||||
|
open_page(help_index_path);
|
||||||
|
});
|
||||||
|
|
||||||
(void)TRY(m_toolbar->try_add_action(*m_go_back_action));
|
(void)TRY(m_toolbar->try_add_action(*m_go_back_action));
|
||||||
(void)TRY(m_toolbar->try_add_action(*m_go_forward_action));
|
(void)TRY(m_toolbar->try_add_action(*m_go_forward_action));
|
||||||
(void)TRY(m_toolbar->try_add_action(*m_go_home_action));
|
(void)TRY(m_toolbar->try_add_action(*m_go_home_action));
|
||||||
|
@ -244,10 +240,10 @@ ErrorOr<void> MainWidget::initialize_fallibles(GUI::Window& window)
|
||||||
TRY(go_menu->try_add_action(*m_go_home_action));
|
TRY(go_menu->try_add_action(*m_go_home_action));
|
||||||
|
|
||||||
auto help_menu = TRY(window.try_add_menu("&Help"));
|
auto help_menu = TRY(window.try_add_menu("&Help"));
|
||||||
|
static String help_page_path = TRY(String::from_utf8("/usr/share/man/man1/Help.md"sv));
|
||||||
TRY(help_menu->try_add_action(GUI::CommonActions::make_command_palette_action(&window)));
|
TRY(help_menu->try_add_action(GUI::CommonActions::make_command_palette_action(&window)));
|
||||||
TRY(help_menu->try_add_action(GUI::Action::create("&Contents", { Key_F1 }, TRY(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/filetype-unknown.png"sv)), [&](auto&) {
|
TRY(help_menu->try_add_action(GUI::Action::create("&Contents", { Key_F1 }, TRY(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/filetype-unknown.png"sv)), [&](auto&) {
|
||||||
DeprecatedString path = "/usr/share/man/man1/Help.md";
|
open_page(help_page_path);
|
||||||
open_page(path);
|
|
||||||
})));
|
})));
|
||||||
TRY(help_menu->try_add_action(GUI::CommonActions::make_about_action("Help", TRY(GUI::Icon::try_create_default_icon("app-help"sv)), &window)));
|
TRY(help_menu->try_add_action(GUI::CommonActions::make_about_action("Help", TRY(GUI::Icon::try_create_default_icon("app-help"sv)), &window)));
|
||||||
|
|
||||||
|
@ -282,8 +278,12 @@ void MainWidget::open_url(URL const& url)
|
||||||
if (browse_view_index.has_value()) {
|
if (browse_view_index.has_value()) {
|
||||||
m_browse_view->expand_tree(browse_view_index.value().parent());
|
m_browse_view->expand_tree(browse_view_index.value().parent());
|
||||||
|
|
||||||
DeprecatedString page_and_section = m_manual_model->page_and_section(browse_view_index.value());
|
auto page_and_section = m_manual_model->page_and_section(browse_view_index.value());
|
||||||
window()->set_title(DeprecatedString::formatted("{} - Help", page_and_section));
|
if (!page_and_section.has_value())
|
||||||
|
return;
|
||||||
|
auto title = String::formatted("{} - Help", page_and_section.value());
|
||||||
|
if (!title.is_error())
|
||||||
|
window()->set_title(title.release_value().to_deprecated_string());
|
||||||
} else {
|
} else {
|
||||||
window()->set_title("Help");
|
window()->set_title("Help");
|
||||||
}
|
}
|
||||||
|
@ -297,17 +297,17 @@ void MainWidget::open_external(URL const& url)
|
||||||
GUI::MessageBox::show(window(), DeprecatedString::formatted("The link to '{}' could not be opened.", url), "Failed to open link"sv, GUI::MessageBox::Type::Error);
|
GUI::MessageBox::show(window(), DeprecatedString::formatted("The link to '{}' could not be opened.", url), "Failed to open link"sv, GUI::MessageBox::Type::Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::open_page(DeprecatedString const& path)
|
void MainWidget::open_page(Optional<String> const& path)
|
||||||
{
|
{
|
||||||
m_go_back_action->set_enabled(m_history.can_go_back());
|
m_go_back_action->set_enabled(m_history.can_go_back());
|
||||||
m_go_forward_action->set_enabled(m_history.can_go_forward());
|
m_go_forward_action->set_enabled(m_history.can_go_forward());
|
||||||
|
|
||||||
if (path.is_null()) {
|
if (!path.has_value()) {
|
||||||
window()->set_title("Help");
|
window()->set_title("Help");
|
||||||
m_web_view->load_empty_document();
|
m_web_view->load_empty_document();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
open_url(URL::create_with_url_or_path(path));
|
open_url(URL::create_with_url_or_path(path.value().to_deprecated_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,13 +20,13 @@ public:
|
||||||
virtual ~MainWidget() override = default;
|
virtual ~MainWidget() override = default;
|
||||||
|
|
||||||
ErrorOr<void> initialize_fallibles(GUI::Window&);
|
ErrorOr<void> initialize_fallibles(GUI::Window&);
|
||||||
void set_start_page(StringView page, u32 section);
|
ErrorOr<void> set_start_page(StringView page, u32 section);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MainWidget();
|
MainWidget();
|
||||||
|
|
||||||
void open_url(URL const&);
|
void open_url(URL const&);
|
||||||
void open_page(DeprecatedString const& path);
|
void open_page(Optional<String> const& path);
|
||||||
void open_external(URL const&);
|
void open_external(URL const&);
|
||||||
|
|
||||||
History m_history;
|
History m_history;
|
||||||
|
|
|
@ -5,21 +5,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ManualModel.h"
|
#include "ManualModel.h"
|
||||||
#include "ManualNode.h"
|
|
||||||
#include "ManualPageNode.h"
|
|
||||||
#include "ManualSectionNode.h"
|
|
||||||
#include <AK/Try.h>
|
#include <AK/Try.h>
|
||||||
|
#include <AK/Utf8View.h>
|
||||||
static ManualSectionNode s_sections[] = {
|
#include <LibManual/Node.h>
|
||||||
{ "1", "User Programs" },
|
#include <LibManual/PageNode.h>
|
||||||
{ "2", "System Calls" },
|
#include <LibManual/SectionNode.h>
|
||||||
{ "3", "Library Functions" },
|
|
||||||
{ "4", "Special Files" },
|
|
||||||
{ "5", "File Formats" },
|
|
||||||
{ "6", "Games" },
|
|
||||||
{ "7", "Miscellanea" },
|
|
||||||
{ "8", "Sysadmin Tools" }
|
|
||||||
};
|
|
||||||
|
|
||||||
ManualModel::ManualModel()
|
ManualModel::ManualModel()
|
||||||
{
|
{
|
||||||
|
@ -34,11 +24,14 @@ Optional<GUI::ModelIndex> ManualModel::index_from_path(StringView path) const
|
||||||
auto parent_index = index(section, 0);
|
auto parent_index = index(section, 0);
|
||||||
for (int row = 0; row < row_count(parent_index); ++row) {
|
for (int row = 0; row < row_count(parent_index); ++row) {
|
||||||
auto child_index = index(row, 0, parent_index);
|
auto child_index = index(row, 0, parent_index);
|
||||||
auto* node = static_cast<ManualNode const*>(child_index.internal_data());
|
auto* node = static_cast<Manual::Node const*>(child_index.internal_data());
|
||||||
if (!node->is_page())
|
if (!node->is_page())
|
||||||
continue;
|
continue;
|
||||||
auto* page = static_cast<ManualPageNode const*>(node);
|
auto* page = static_cast<Manual::PageNode const*>(node);
|
||||||
if (page->path() != path)
|
auto const maybe_path = page->path();
|
||||||
|
if (maybe_path.is_error())
|
||||||
|
return {};
|
||||||
|
if (maybe_path.value().bytes_as_string_view() != path)
|
||||||
continue;
|
continue;
|
||||||
return child_index;
|
return child_index;
|
||||||
}
|
}
|
||||||
|
@ -46,29 +39,32 @@ Optional<GUI::ModelIndex> ManualModel::index_from_path(StringView path) const
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
DeprecatedString ManualModel::page_name(const GUI::ModelIndex& index) const
|
Optional<String> ManualModel::page_name(const GUI::ModelIndex& index) const
|
||||||
{
|
{
|
||||||
if (!index.is_valid())
|
if (!index.is_valid())
|
||||||
return {};
|
return {};
|
||||||
auto* node = static_cast<ManualNode const*>(index.internal_data());
|
auto* node = static_cast<Manual::Node const*>(index.internal_data());
|
||||||
if (!node->is_page())
|
if (!node->is_page())
|
||||||
return {};
|
return {};
|
||||||
auto* page = static_cast<ManualPageNode const*>(node);
|
auto* page = static_cast<Manual::PageNode const*>(node);
|
||||||
return page->name();
|
return page->name();
|
||||||
}
|
}
|
||||||
|
|
||||||
DeprecatedString ManualModel::page_path(const GUI::ModelIndex& index) const
|
Optional<String> ManualModel::page_path(const GUI::ModelIndex& index) const
|
||||||
{
|
{
|
||||||
if (!index.is_valid())
|
if (!index.is_valid())
|
||||||
return {};
|
return {};
|
||||||
auto* node = static_cast<ManualNode const*>(index.internal_data());
|
auto* node = static_cast<Manual::Node const*>(index.internal_data());
|
||||||
if (!node->is_page())
|
if (!node->is_page())
|
||||||
return {};
|
return {};
|
||||||
auto* page = static_cast<ManualPageNode const*>(node);
|
auto* page = static_cast<Manual::PageNode const*>(node);
|
||||||
return page->path();
|
auto path = page->path();
|
||||||
|
if (path.is_error())
|
||||||
|
return {};
|
||||||
|
return path.release_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<StringView> ManualModel::page_view(DeprecatedString const& path) const
|
ErrorOr<StringView> ManualModel::page_view(String const& path) const
|
||||||
{
|
{
|
||||||
if (path.is_empty())
|
if (path.is_empty())
|
||||||
return StringView {};
|
return StringView {};
|
||||||
|
@ -87,23 +83,29 @@ ErrorOr<StringView> ManualModel::page_view(DeprecatedString const& path) const
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeprecatedString ManualModel::page_and_section(const GUI::ModelIndex& index) const
|
Optional<String> ManualModel::page_and_section(const GUI::ModelIndex& index) const
|
||||||
{
|
{
|
||||||
if (!index.is_valid())
|
if (!index.is_valid())
|
||||||
return {};
|
return {};
|
||||||
auto* node = static_cast<ManualNode const*>(index.internal_data());
|
auto* node = static_cast<Manual::Node const*>(index.internal_data());
|
||||||
if (!node->is_page())
|
if (!node->is_page())
|
||||||
return {};
|
return {};
|
||||||
auto* page = static_cast<ManualPageNode const*>(node);
|
auto* page = static_cast<Manual::PageNode const*>(node);
|
||||||
auto* section = static_cast<ManualSectionNode const*>(page->parent());
|
auto* section = static_cast<Manual::SectionNode const*>(page->parent());
|
||||||
return DeprecatedString::formatted("{}({})", page->name(), section->section_name());
|
auto page_name = page->name();
|
||||||
|
if (page_name.is_error())
|
||||||
|
return {};
|
||||||
|
auto name = String::formatted("{}({})", page_name.release_value(), section->section_name());
|
||||||
|
if (name.is_error())
|
||||||
|
return {};
|
||||||
|
return name.release_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
GUI::ModelIndex ManualModel::index(int row, int column, const GUI::ModelIndex& parent_index) const
|
GUI::ModelIndex ManualModel::index(int row, int column, const GUI::ModelIndex& parent_index) const
|
||||||
{
|
{
|
||||||
if (!parent_index.is_valid())
|
if (!parent_index.is_valid())
|
||||||
return create_index(row, column, &s_sections[row]);
|
return create_index(row, column, Manual::sections[row].ptr());
|
||||||
auto* parent = static_cast<ManualNode const*>(parent_index.internal_data());
|
auto* parent = static_cast<Manual::Node const*>(parent_index.internal_data());
|
||||||
auto* child = &parent->children()[row];
|
auto* child = &parent->children()[row];
|
||||||
return create_index(row, column, child);
|
return create_index(row, column, child);
|
||||||
}
|
}
|
||||||
|
@ -112,19 +114,19 @@ GUI::ModelIndex ManualModel::parent_index(const GUI::ModelIndex& index) const
|
||||||
{
|
{
|
||||||
if (!index.is_valid())
|
if (!index.is_valid())
|
||||||
return {};
|
return {};
|
||||||
auto* child = static_cast<ManualNode const*>(index.internal_data());
|
auto* child = static_cast<Manual::Node const*>(index.internal_data());
|
||||||
auto* parent = child->parent();
|
auto* parent = child->parent();
|
||||||
if (parent == nullptr)
|
if (parent == nullptr)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (parent->parent() == nullptr) {
|
if (parent->parent() == nullptr) {
|
||||||
for (size_t row = 0; row < sizeof(s_sections) / sizeof(s_sections[0]); row++)
|
for (size_t row = 0; row < sizeof(Manual::sections) / sizeof(Manual::sections[0]); row++)
|
||||||
if (&s_sections[row] == parent)
|
if (Manual::sections[row].ptr() == parent)
|
||||||
return create_index(row, 0, parent);
|
return create_index(row, 0, parent);
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
for (size_t row = 0; row < parent->parent()->children().size(); row++) {
|
for (size_t row = 0; row < parent->parent()->children().size(); row++) {
|
||||||
ManualNode* child_at_row = &parent->parent()->children()[row];
|
Manual::Node* child_at_row = &parent->parent()->children()[row];
|
||||||
if (child_at_row == parent)
|
if (child_at_row == parent)
|
||||||
return create_index(row, 0, parent);
|
return create_index(row, 0, parent);
|
||||||
}
|
}
|
||||||
|
@ -134,8 +136,8 @@ GUI::ModelIndex ManualModel::parent_index(const GUI::ModelIndex& index) const
|
||||||
int ManualModel::row_count(const GUI::ModelIndex& index) const
|
int ManualModel::row_count(const GUI::ModelIndex& index) const
|
||||||
{
|
{
|
||||||
if (!index.is_valid())
|
if (!index.is_valid())
|
||||||
return sizeof(s_sections) / sizeof(s_sections[0]);
|
return sizeof(Manual::sections) / sizeof(Manual::sections[0]);
|
||||||
auto* node = static_cast<ManualNode const*>(index.internal_data());
|
auto* node = static_cast<Manual::Node const*>(index.internal_data());
|
||||||
return node->children().size();
|
return node->children().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,12 +148,16 @@ int ManualModel::column_count(const GUI::ModelIndex&) const
|
||||||
|
|
||||||
GUI::Variant ManualModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
|
GUI::Variant ManualModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
|
||||||
{
|
{
|
||||||
auto* node = static_cast<ManualNode const*>(index.internal_data());
|
auto* node = static_cast<Manual::Node const*>(index.internal_data());
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case GUI::ModelRole::Search:
|
case GUI::ModelRole::Search:
|
||||||
if (!node->is_page())
|
if (!node->is_page())
|
||||||
return {};
|
return {};
|
||||||
return DeprecatedString(page_view(page_path(index)).value());
|
if (auto path = page_path(index); path.has_value())
|
||||||
|
if (auto page = page_view(path.release_value()); !page.is_error())
|
||||||
|
// FIXME: We already provide String, but GUI::Variant still needs DeprecatedString.
|
||||||
|
return DeprecatedString(page.release_value());
|
||||||
|
return {};
|
||||||
case GUI::ModelRole::Display:
|
case GUI::ModelRole::Display:
|
||||||
return node->name();
|
return node->name();
|
||||||
case GUI::ModelRole::Icon:
|
case GUI::ModelRole::Icon:
|
||||||
|
@ -167,17 +173,24 @@ GUI::Variant ManualModel::data(const GUI::ModelIndex& index, GUI::ModelRole role
|
||||||
|
|
||||||
void ManualModel::update_section_node_on_toggle(const GUI::ModelIndex& index, bool const open)
|
void ManualModel::update_section_node_on_toggle(const GUI::ModelIndex& index, bool const open)
|
||||||
{
|
{
|
||||||
auto* node = static_cast<ManualSectionNode*>(index.internal_data());
|
auto* node = static_cast<Manual::SectionNode*>(index.internal_data());
|
||||||
node->set_open(open);
|
node->set_open(open);
|
||||||
}
|
}
|
||||||
|
|
||||||
TriState ManualModel::data_matches(const GUI::ModelIndex& index, const GUI::Variant& term) const
|
TriState ManualModel::data_matches(const GUI::ModelIndex& index, const GUI::Variant& term) const
|
||||||
{
|
{
|
||||||
auto name = page_name(index);
|
auto name = page_name(index);
|
||||||
if (name.contains(term.as_string(), CaseSensitivity::CaseInsensitive))
|
if (!name.has_value())
|
||||||
|
return TriState::False;
|
||||||
|
|
||||||
|
if (name.value().bytes_as_string_view().contains(term.as_string(), CaseSensitivity::CaseInsensitive))
|
||||||
return TriState::True;
|
return TriState::True;
|
||||||
|
|
||||||
auto view_result = page_view(page_path(index));
|
auto path = page_path(index);
|
||||||
|
// 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.
|
||||||
|
if (!path.has_value())
|
||||||
|
return TriState::False;
|
||||||
|
auto view_result = page_view(path.release_value());
|
||||||
if (view_result.is_error() || view_result.value().is_empty())
|
if (view_result.is_error() || view_result.value().is_empty())
|
||||||
return TriState::False;
|
return TriState::False;
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/DeprecatedString.h>
|
|
||||||
#include <AK/NonnullRefPtr.h>
|
#include <AK/NonnullRefPtr.h>
|
||||||
#include <AK/Optional.h>
|
#include <AK/Optional.h>
|
||||||
#include <AK/Result.h>
|
#include <AK/Result.h>
|
||||||
|
#include <AK/String.h>
|
||||||
#include <LibGUI/Model.h>
|
#include <LibGUI/Model.h>
|
||||||
|
|
||||||
class ManualModel final : public GUI::Model {
|
class ManualModel final : public GUI::Model {
|
||||||
|
@ -23,10 +23,10 @@ public:
|
||||||
|
|
||||||
Optional<GUI::ModelIndex> index_from_path(StringView) const;
|
Optional<GUI::ModelIndex> index_from_path(StringView) const;
|
||||||
|
|
||||||
DeprecatedString page_name(const GUI::ModelIndex&) const;
|
Optional<String> page_name(const GUI::ModelIndex&) const;
|
||||||
DeprecatedString page_path(const GUI::ModelIndex&) const;
|
Optional<String> page_path(const GUI::ModelIndex&) const;
|
||||||
DeprecatedString page_and_section(const GUI::ModelIndex&) const;
|
Optional<String> page_and_section(const GUI::ModelIndex&) const;
|
||||||
ErrorOr<StringView> page_view(DeprecatedString const& path) const;
|
ErrorOr<StringView> page_view(String const& path) const;
|
||||||
|
|
||||||
void update_section_node_on_toggle(const GUI::ModelIndex&, bool const);
|
void update_section_node_on_toggle(const GUI::ModelIndex&, bool const);
|
||||||
virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
|
virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
|
||||||
|
@ -42,5 +42,5 @@ private:
|
||||||
GUI::Icon m_section_open_icon;
|
GUI::Icon m_section_open_icon;
|
||||||
GUI::Icon m_section_icon;
|
GUI::Icon m_section_icon;
|
||||||
GUI::Icon m_page_icon;
|
GUI::Icon m_page_icon;
|
||||||
mutable HashMap<DeprecatedString, NonnullRefPtr<Core::MappedFile>> m_mapped_files;
|
mutable HashMap<String, NonnullRefPtr<Core::MappedFile>> m_mapped_files;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
|
|
||||||
* Copyright (c) 2022, the SerenityOS developers.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <AK/DeprecatedString.h>
|
|
||||||
#include <AK/NonnullOwnPtrVector.h>
|
|
||||||
|
|
||||||
class ManualNode {
|
|
||||||
public:
|
|
||||||
virtual ~ManualNode() = default;
|
|
||||||
|
|
||||||
virtual NonnullOwnPtrVector<ManualNode>& children() const = 0;
|
|
||||||
virtual ManualNode const* parent() const = 0;
|
|
||||||
virtual DeprecatedString name() const = 0;
|
|
||||||
virtual bool is_page() const { return false; }
|
|
||||||
virtual bool is_open() const { return false; }
|
|
||||||
};
|
|
|
@ -1,25 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
|
|
||||||
* Copyright (c) 2022, the SerenityOS developers.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ManualPageNode.h"
|
|
||||||
#include "ManualSectionNode.h"
|
|
||||||
|
|
||||||
ManualNode const* ManualPageNode::parent() const
|
|
||||||
{
|
|
||||||
return &m_section;
|
|
||||||
}
|
|
||||||
|
|
||||||
NonnullOwnPtrVector<ManualNode>& ManualPageNode::children() const
|
|
||||||
{
|
|
||||||
static NonnullOwnPtrVector<ManualNode> empty_vector;
|
|
||||||
return empty_vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeprecatedString ManualPageNode::path() const
|
|
||||||
{
|
|
||||||
return DeprecatedString::formatted("{}/{}.md", m_section.path(), m_page);
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ManualNode.h"
|
|
||||||
|
|
||||||
class ManualSectionNode;
|
|
||||||
|
|
||||||
class ManualPageNode : public ManualNode {
|
|
||||||
public:
|
|
||||||
virtual ~ManualPageNode() override = default;
|
|
||||||
|
|
||||||
ManualPageNode(ManualSectionNode const& section, StringView page)
|
|
||||||
: m_section(section)
|
|
||||||
, m_page(page)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual NonnullOwnPtrVector<ManualNode>& children() const override;
|
|
||||||
virtual ManualNode const* parent() const override;
|
|
||||||
virtual DeprecatedString name() const override { return m_page; };
|
|
||||||
virtual bool is_page() const override { return true; }
|
|
||||||
|
|
||||||
DeprecatedString path() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
ManualSectionNode const& m_section;
|
|
||||||
DeprecatedString m_page;
|
|
||||||
};
|
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ManualSectionNode.h"
|
|
||||||
#include "ManualPageNode.h"
|
|
||||||
#include <AK/DeprecatedString.h>
|
|
||||||
#include <AK/LexicalPath.h>
|
|
||||||
#include <AK/QuickSort.h>
|
|
||||||
#include <LibCore/DirIterator.h>
|
|
||||||
|
|
||||||
DeprecatedString ManualSectionNode::path() const
|
|
||||||
{
|
|
||||||
return DeprecatedString::formatted("/usr/share/man/man{}", m_section);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ManualSectionNode::reify_if_needed() const
|
|
||||||
{
|
|
||||||
if (m_reified)
|
|
||||||
return;
|
|
||||||
m_reified = true;
|
|
||||||
|
|
||||||
Core::DirIterator dir_iter { path(), Core::DirIterator::Flags::SkipDots };
|
|
||||||
|
|
||||||
Vector<DeprecatedString> page_names;
|
|
||||||
while (dir_iter.has_next()) {
|
|
||||||
LexicalPath lexical_path(dir_iter.next_path());
|
|
||||||
if (lexical_path.extension() != "md")
|
|
||||||
continue;
|
|
||||||
page_names.append(lexical_path.title());
|
|
||||||
}
|
|
||||||
|
|
||||||
quick_sort(page_names);
|
|
||||||
|
|
||||||
for (auto& page_name : page_names)
|
|
||||||
m_children.append(make<ManualPageNode>(*this, move(page_name)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ManualSectionNode::set_open(bool open)
|
|
||||||
{
|
|
||||||
if (m_open == open)
|
|
||||||
return;
|
|
||||||
m_open = open;
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
|
|
||||||
* Copyright (c) 2022, the SerenityOS developers.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ManualNode.h"
|
|
||||||
|
|
||||||
class ManualSectionNode : public ManualNode {
|
|
||||||
public:
|
|
||||||
virtual ~ManualSectionNode() override = default;
|
|
||||||
|
|
||||||
ManualSectionNode(DeprecatedString section, DeprecatedString name)
|
|
||||||
: m_section(section)
|
|
||||||
, m_full_name(DeprecatedString::formatted("{}. {}", section, name))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual NonnullOwnPtrVector<ManualNode>& children() const override
|
|
||||||
{
|
|
||||||
reify_if_needed();
|
|
||||||
return m_children;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ManualNode const* parent() const override { return nullptr; }
|
|
||||||
virtual DeprecatedString name() const override { return m_full_name; }
|
|
||||||
virtual bool is_open() const override { return m_open; }
|
|
||||||
void set_open(bool open);
|
|
||||||
|
|
||||||
DeprecatedString const& section_name() const { return m_section; }
|
|
||||||
DeprecatedString path() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void reify_if_needed() const;
|
|
||||||
|
|
||||||
DeprecatedString m_section;
|
|
||||||
DeprecatedString m_full_name;
|
|
||||||
mutable NonnullOwnPtrVector<ManualNode> m_children;
|
|
||||||
mutable bool m_reified { false };
|
|
||||||
bool m_open { false };
|
|
||||||
};
|
|
|
@ -87,7 +87,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
|
|
||||||
auto main_widget = TRY(window->try_set_main_widget<MainWidget>());
|
auto main_widget = TRY(window->try_set_main_widget<MainWidget>());
|
||||||
TRY(main_widget->initialize_fallibles(window));
|
TRY(main_widget->initialize_fallibles(window));
|
||||||
main_widget->set_start_page(start_page, section);
|
TRY(main_widget->set_start_page(start_page, section));
|
||||||
|
|
||||||
window->show();
|
window->show();
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ add_subdirectory(LibKeyboard)
|
||||||
add_subdirectory(LibLine)
|
add_subdirectory(LibLine)
|
||||||
add_subdirectory(LibLocale)
|
add_subdirectory(LibLocale)
|
||||||
add_subdirectory(LibMain)
|
add_subdirectory(LibMain)
|
||||||
|
add_subdirectory(LibManual)
|
||||||
add_subdirectory(LibMarkdown)
|
add_subdirectory(LibMarkdown)
|
||||||
add_subdirectory(LibPartition)
|
add_subdirectory(LibPartition)
|
||||||
add_subdirectory(LibPCIDB)
|
add_subdirectory(LibPCIDB)
|
||||||
|
|
7
Userland/Libraries/LibManual/CMakeLists.txt
Normal file
7
Userland/Libraries/LibManual/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
set(SOURCES
|
||||||
|
PageNode.cpp
|
||||||
|
SectionNode.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
serenity_lib(LibManual manual)
|
||||||
|
target_link_libraries(LibManual PRIVATE LibCore)
|
30
Userland/Libraries/LibManual/Node.h
Normal file
30
Userland/Libraries/LibManual/Node.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
|
||||||
|
* Copyright (c) 2022, the SerenityOS developers.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/NonnullRefPtrVector.h>
|
||||||
|
#include <AK/RefCounted.h>
|
||||||
|
#include <AK/String.h>
|
||||||
|
#include <AK/StringView.h>
|
||||||
|
|
||||||
|
namespace Manual {
|
||||||
|
|
||||||
|
class PageNode;
|
||||||
|
|
||||||
|
class Node : public RefCounted<Node> {
|
||||||
|
public:
|
||||||
|
virtual ~Node() = default;
|
||||||
|
|
||||||
|
virtual NonnullRefPtrVector<Node>& children() const = 0;
|
||||||
|
virtual Node const* parent() const = 0;
|
||||||
|
virtual String name() const = 0;
|
||||||
|
virtual bool is_page() const { return false; }
|
||||||
|
virtual bool is_open() const { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
29
Userland/Libraries/LibManual/PageNode.cpp
Normal file
29
Userland/Libraries/LibManual/PageNode.cpp
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
|
||||||
|
* Copyright (c) 2022, the SerenityOS developers.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PageNode.h"
|
||||||
|
#include "SectionNode.h"
|
||||||
|
|
||||||
|
namespace Manual {
|
||||||
|
|
||||||
|
Node const* PageNode::parent() const
|
||||||
|
{
|
||||||
|
return m_section.ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
NonnullRefPtrVector<Node>& PageNode::children() const
|
||||||
|
{
|
||||||
|
static NonnullRefPtrVector<Node> empty_vector;
|
||||||
|
return empty_vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<String> PageNode::path() const
|
||||||
|
{
|
||||||
|
return TRY(String::formatted("{}/{}.md", TRY(m_section->path()), m_page));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
Userland/Libraries/LibManual/PageNode.h
Normal file
38
Userland/Libraries/LibManual/PageNode.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/NonnullRefPtr.h>
|
||||||
|
#include <LibManual/Node.h>
|
||||||
|
|
||||||
|
namespace Manual {
|
||||||
|
|
||||||
|
class SectionNode;
|
||||||
|
|
||||||
|
class PageNode : public Node {
|
||||||
|
public:
|
||||||
|
virtual ~PageNode() override = default;
|
||||||
|
|
||||||
|
PageNode(NonnullRefPtr<SectionNode> section, String page)
|
||||||
|
: m_section(move(section))
|
||||||
|
, m_page(move(page))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual NonnullRefPtrVector<Node>& children() const override;
|
||||||
|
virtual Node const* parent() const override;
|
||||||
|
virtual String name() const override { return m_page; };
|
||||||
|
virtual bool is_page() const override { return true; }
|
||||||
|
|
||||||
|
ErrorOr<String> path() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NonnullRefPtr<SectionNode> m_section;
|
||||||
|
String m_page;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
62
Userland/Libraries/LibManual/SectionNode.cpp
Normal file
62
Userland/Libraries/LibManual/SectionNode.cpp
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "SectionNode.h"
|
||||||
|
#include "PageNode.h"
|
||||||
|
#include <AK/LexicalPath.h>
|
||||||
|
#include <AK/QuickSort.h>
|
||||||
|
#include <LibCore/DirIterator.h>
|
||||||
|
|
||||||
|
namespace Manual {
|
||||||
|
|
||||||
|
ErrorOr<String> SectionNode::path() const
|
||||||
|
{
|
||||||
|
return String::formatted("/usr/share/man/man{}", m_section);
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> SectionNode::reify_if_needed() const
|
||||||
|
{
|
||||||
|
if (m_reified)
|
||||||
|
return {};
|
||||||
|
m_reified = true;
|
||||||
|
|
||||||
|
Core::DirIterator dir_iter { TRY(path()).to_deprecated_string(), Core::DirIterator::Flags::SkipDots };
|
||||||
|
|
||||||
|
Vector<String> page_names;
|
||||||
|
while (dir_iter.has_next()) {
|
||||||
|
LexicalPath lexical_path(dir_iter.next_path());
|
||||||
|
if (lexical_path.extension() != "md")
|
||||||
|
continue;
|
||||||
|
page_names.append(TRY(String::from_utf8(lexical_path.title())));
|
||||||
|
}
|
||||||
|
|
||||||
|
quick_sort(page_names);
|
||||||
|
|
||||||
|
for (auto& page_name : page_names)
|
||||||
|
m_children.append(TRY(try_make_ref_counted<PageNode>(*this, move(page_name))));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void SectionNode::set_open(bool open)
|
||||||
|
{
|
||||||
|
if (m_open == open)
|
||||||
|
return;
|
||||||
|
m_open = open;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<NonnullRefPtr<SectionNode>, number_of_sections> const sections = { {
|
||||||
|
make_ref_counted<SectionNode>("1"sv, "User Programs"sv),
|
||||||
|
make_ref_counted<SectionNode>("2"sv, "System Calls"sv),
|
||||||
|
make_ref_counted<SectionNode>("3"sv, "Library Functions"sv),
|
||||||
|
make_ref_counted<SectionNode>("4"sv, "Special Files"sv),
|
||||||
|
make_ref_counted<SectionNode>("5"sv, "File Formats"sv),
|
||||||
|
make_ref_counted<SectionNode>("6"sv, "Games"sv),
|
||||||
|
make_ref_counted<SectionNode>("7"sv, "Miscellanea"sv),
|
||||||
|
make_ref_counted<SectionNode>("8"sv, "Sysadmin Tools"sv),
|
||||||
|
} };
|
||||||
|
|
||||||
|
}
|
65
Userland/Libraries/LibManual/SectionNode.h
Normal file
65
Userland/Libraries/LibManual/SectionNode.h
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
|
||||||
|
* Copyright (c) 2022, the SerenityOS developers.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Error.h>
|
||||||
|
#include <AK/String.h>
|
||||||
|
#include <LibManual/Node.h>
|
||||||
|
|
||||||
|
namespace Manual {
|
||||||
|
|
||||||
|
class SectionNode : public Node {
|
||||||
|
public:
|
||||||
|
virtual ~SectionNode() override = default;
|
||||||
|
|
||||||
|
SectionNode(StringView section, StringView name)
|
||||||
|
: m_section(MUST(String::from_utf8(section)))
|
||||||
|
, m_full_name(MUST(String::formatted("{}. {}", section, name)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual NonnullRefPtrVector<Node>& children() const override
|
||||||
|
{
|
||||||
|
MUST(reify_if_needed());
|
||||||
|
return m_children;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Node const* parent() const override { return nullptr; }
|
||||||
|
virtual String name() const override { return m_full_name; }
|
||||||
|
virtual bool is_open() const override { return m_open; }
|
||||||
|
void set_open(bool open);
|
||||||
|
|
||||||
|
String const& section_name() const { return m_section; }
|
||||||
|
ErrorOr<String> path() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorOr<void> reify_if_needed() const;
|
||||||
|
|
||||||
|
String m_section;
|
||||||
|
String m_full_name;
|
||||||
|
mutable NonnullRefPtrVector<Node> m_children;
|
||||||
|
mutable bool m_reified { false };
|
||||||
|
bool m_open { false };
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr size_t number_of_sections = 8;
|
||||||
|
|
||||||
|
extern Array<NonnullRefPtr<SectionNode>, number_of_sections> const sections;
|
||||||
|
|
||||||
|
constexpr Array<StringView, number_of_sections> const section_numbers = {
|
||||||
|
"1"sv,
|
||||||
|
"2"sv,
|
||||||
|
"3"sv,
|
||||||
|
"4"sv,
|
||||||
|
"5"sv,
|
||||||
|
"6"sv,
|
||||||
|
"7"sv,
|
||||||
|
"8"sv,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue