Serenity Operating System
1/*
2 * Copyright (c) 2022, Jakub Berkop <jakub.berkop@gmail.com>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include "FilesystemEventModel.h"
8#include "Profile.h"
9#include <AK/StringBuilder.h>
10#include <LibGUI/FileIconProvider.h>
11#include <LibSymbolication/Symbolication.h>
12#include <stdio.h>
13
14namespace Profiler {
15
16FileEventModel::FileEventModel(Profile& profile)
17 : m_profile(profile)
18{
19}
20
21FileEventModel::~FileEventModel()
22{
23}
24
25FileEventNode& FileEventNode::find_or_create_node(DeprecatedString const& searched_path)
26{
27 // TODO: Optimize this function.
28
29 if (searched_path == ""sv) {
30 return *this;
31 }
32
33 auto lex_path = LexicalPath(searched_path);
34 auto parts = lex_path.parts();
35 auto current = parts.take_first();
36
37 StringBuilder sb;
38 sb.join('/', parts);
39 auto new_s = sb.to_deprecated_string();
40
41 for (auto& child : m_children) {
42 if (child->m_path == current) {
43 return child->find_or_create_node(new_s);
44 }
45 }
46
47 if (m_parent) {
48 for (auto& child : m_children) {
49 if (child->m_path == current) {
50 return child->find_or_create_node(new_s);
51 }
52 }
53 return create_recursively(searched_path);
54 } else {
55 if (!searched_path.starts_with("/"sv)) {
56 m_children.append(create(searched_path, this));
57 return *m_children.last();
58 }
59
60 return create_recursively(searched_path);
61 }
62}
63
64FileEventNode& FileEventNode::create_recursively(DeprecatedString new_path)
65{
66 auto const lex_path = LexicalPath(new_path);
67 auto parts = lex_path.parts();
68
69 if (parts.size() == 1) {
70 auto new_node = FileEventNode::create(new_path, this);
71 m_children.append(new_node);
72 return *new_node.ptr();
73 } else {
74 auto new_node = FileEventNode::create(parts.take_first(), this);
75 m_children.append(new_node);
76
77 StringBuilder sb;
78 sb.join('/', parts);
79
80 return new_node->create_recursively(sb.to_deprecated_string());
81 }
82}
83
84void FileEventNode::for_each_parent_node(Function<void(FileEventNode&)> callback)
85{
86 auto* current = this;
87 while (current) {
88 callback(*current);
89 current = current->m_parent;
90 }
91}
92
93GUI::ModelIndex FileEventModel::index(int row, int column, GUI::ModelIndex const& parent) const
94{
95 if (!parent.is_valid()) {
96 return create_index(row, column, m_profile.file_event_nodes()->children()[row].ptr());
97 }
98 auto& remote_parent = *static_cast<FileEventNode*>(parent.internal_data());
99 return create_index(row, column, remote_parent.children().at(row).ptr());
100}
101
102GUI::ModelIndex FileEventModel::parent_index(GUI::ModelIndex const& index) const
103{
104 if (!index.is_valid())
105 return {};
106 auto& node = *static_cast<FileEventNode*>(index.internal_data());
107 if (!node.parent())
108 return {};
109
110 if (node.parent()->parent()) {
111 auto const& children = node.parent()->parent()->children();
112
113 for (size_t row = 0; row < children.size(); ++row) {
114 if (children.at(row).ptr() == node.parent()) {
115 return create_index(row, index.column(), node.parent());
116 }
117 }
118 }
119
120 auto const& children = node.parent()->children();
121
122 for (size_t row = 0; row < children.size(); ++row) {
123 if (children.at(row).ptr() == &node) {
124 return create_index(row, index.column(), node.parent());
125 }
126 }
127
128 VERIFY_NOT_REACHED();
129 return {};
130}
131
132int FileEventModel::row_count(GUI::ModelIndex const& index) const
133{
134 if (!index.is_valid())
135 return m_profile.file_event_nodes()->children().size();
136 auto& node = *static_cast<FileEventNode*>(index.internal_data());
137 return node.children().size();
138}
139
140int FileEventModel::column_count(GUI::ModelIndex const&) const
141{
142 return Column::__Count;
143}
144
145DeprecatedString FileEventModel::column_name(int column) const
146{
147 switch (column) {
148 case Column::Path:
149 return "Path";
150 case Column::Count:
151 return "Event Count";
152 case Column::Duration:
153 return "Duration [ms]";
154 default:
155 VERIFY_NOT_REACHED();
156 return {};
157 }
158}
159
160GUI::Variant FileEventModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const
161{
162 if (role == GUI::ModelRole::TextAlignment) {
163 if (index.column() == Path)
164 return Gfx::TextAlignment::CenterLeft;
165 return Gfx::TextAlignment::CenterRight;
166 }
167
168 auto* node = static_cast<FileEventNode*>(index.internal_data());
169
170 if (role == GUI::ModelRole::Display) {
171 if (index.column() == Column::Count) {
172 return node->count();
173 }
174
175 if (index.column() == Column::Path) {
176 return node->path();
177 }
178
179 if (index.column() == Column::Duration) {
180 return node->duration();
181 }
182
183 return {};
184 }
185
186 return {};
187}
188
189}