mirror of
https://github.com/RGBCube/serenity
synced 2025-05-24 01:15:07 +00:00
LibGUI: Don't require passing model to FileSystemModel::Node APIs
The Node API was obnoxiously requiring you to pass the model into it all the time, simply because nodes could not find their way back to the containing model. This patch adds a back-reference to the model and simplifies the API.
This commit is contained in:
parent
38d8426f32
commit
f0349323c4
5 changed files with 47 additions and 40 deletions
|
@ -93,7 +93,7 @@ void DirectoryView::handle_activation(const GUI::ModelIndex& index)
|
||||||
return;
|
return;
|
||||||
dbgprintf("on activation: %d,%d, this=%p, m_model=%p\n", index.row(), index.column(), this, m_model.ptr());
|
dbgprintf("on activation: %d,%d, this=%p, m_model=%p\n", index.row(), index.column(), this, m_model.ptr());
|
||||||
auto& node = model().node(index);
|
auto& node = model().node(index);
|
||||||
auto path = node.full_path(model());
|
auto path = node.full_path();
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(path.characters(), &st) < 0) {
|
if (stat(path.characters(), &st) < 0) {
|
||||||
|
|
|
@ -705,11 +705,11 @@ int run_in_windowed_mode(RefPtr<Core::ConfigFile> config, String initial_locatio
|
||||||
auto& node = directory_view.model().node(index);
|
auto& node = directory_view.model().node(index);
|
||||||
|
|
||||||
if (node.is_directory()) {
|
if (node.is_directory()) {
|
||||||
auto should_get_enabled = access(node.full_path(directory_view.model()).characters(), W_OK) == 0 && GUI::Clipboard::the().type() == "text/uri-list";
|
auto should_get_enabled = access(node.full_path().characters(), W_OK) == 0 && GUI::Clipboard::the().type() == "text/uri-list";
|
||||||
folder_specific_paste_action->set_enabled(should_get_enabled);
|
folder_specific_paste_action->set_enabled(should_get_enabled);
|
||||||
directory_context_menu->popup(event.screen_position());
|
directory_context_menu->popup(event.screen_position());
|
||||||
} else {
|
} else {
|
||||||
auto full_path = node.full_path(directory_view.model());
|
auto full_path = node.full_path();
|
||||||
current_file_handlers = directory_view.get_launch_handlers(full_path);
|
current_file_handlers = directory_view.get_launch_handlers(full_path);
|
||||||
|
|
||||||
file_context_menu = GUI::Menu::construct("Directory View File");
|
file_context_menu = GUI::Menu::construct("Directory View File");
|
||||||
|
@ -774,10 +774,10 @@ int run_in_windowed_mode(RefPtr<Core::ConfigFile> config, String initial_locatio
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (auto& url_to_copy : urls) {
|
for (auto& url_to_copy : urls) {
|
||||||
if (!url_to_copy.is_valid() || url_to_copy.path() == target_node.full_path(directory_view.model()))
|
if (!url_to_copy.is_valid() || url_to_copy.path() == target_node.full_path())
|
||||||
continue;
|
continue;
|
||||||
auto new_path = String::format("%s/%s",
|
auto new_path = String::format("%s/%s",
|
||||||
target_node.full_path(directory_view.model()).characters(),
|
target_node.full_path().characters(),
|
||||||
LexicalPath(url_to_copy.path()).basename().characters());
|
LexicalPath(url_to_copy.path()).basename().characters());
|
||||||
|
|
||||||
if (url_to_copy.path() == new_path)
|
if (url_to_copy.path() == new_path)
|
||||||
|
|
|
@ -202,7 +202,7 @@ FilePicker::FilePicker(Window* parent_window, Mode mode, Options options, const
|
||||||
auto& filter_model = (SortingProxyModel&)*m_view->model();
|
auto& filter_model = (SortingProxyModel&)*m_view->model();
|
||||||
auto local_index = filter_model.map_to_source(index);
|
auto local_index = filter_model.map_to_source(index);
|
||||||
const FileSystemModel::Node& node = m_model->node(local_index);
|
const FileSystemModel::Node& node = m_model->node(local_index);
|
||||||
LexicalPath path { node.full_path(m_model) };
|
LexicalPath path { node.full_path() };
|
||||||
|
|
||||||
if (have_preview())
|
if (have_preview())
|
||||||
clear_preview();
|
clear_preview();
|
||||||
|
@ -240,7 +240,7 @@ FilePicker::FilePicker(Window* parent_window, Mode mode, Options options, const
|
||||||
auto& filter_model = (SortingProxyModel&)*m_view->model();
|
auto& filter_model = (SortingProxyModel&)*m_view->model();
|
||||||
auto local_index = filter_model.map_to_source(index);
|
auto local_index = filter_model.map_to_source(index);
|
||||||
const FileSystemModel::Node& node = m_model->node(local_index);
|
const FileSystemModel::Node& node = m_model->node(local_index);
|
||||||
auto path = node.full_path(m_model);
|
auto path = node.full_path();
|
||||||
|
|
||||||
if (node.is_directory()) {
|
if (node.is_directory()) {
|
||||||
set_path(path);
|
set_path(path);
|
||||||
|
|
|
@ -44,13 +44,13 @@
|
||||||
|
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
ModelIndex FileSystemModel::Node::index(const FileSystemModel& model, int column) const
|
ModelIndex FileSystemModel::Node::index(int column) const
|
||||||
{
|
{
|
||||||
if (!parent)
|
if (!parent)
|
||||||
return {};
|
return {};
|
||||||
for (size_t row = 0; row < parent->children.size(); ++row) {
|
for (size_t row = 0; row < parent->children.size(); ++row) {
|
||||||
if (&parent->children[row] == this)
|
if (&parent->children[row] == this)
|
||||||
return model.create_index(row, column, const_cast<Node*>(this));
|
return m_model.create_index(row, column, const_cast<Node*>(this));
|
||||||
}
|
}
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
@ -85,15 +85,15 @@ bool FileSystemModel::Node::fetch_data(const String& full_path, bool is_root)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::Node::traverse_if_needed(const FileSystemModel& model)
|
void FileSystemModel::Node::traverse_if_needed()
|
||||||
{
|
{
|
||||||
if (!is_directory() || has_traversed)
|
if (!is_directory() || has_traversed)
|
||||||
return;
|
return;
|
||||||
has_traversed = true;
|
has_traversed = true;
|
||||||
total_size = 0;
|
total_size = 0;
|
||||||
|
|
||||||
auto full_path = this->full_path(model);
|
auto full_path = this->full_path();
|
||||||
Core::DirIterator di(full_path, model.should_show_dotfiles() ? Core::DirIterator::SkipParentAndBaseDir : Core::DirIterator::SkipDots);
|
Core::DirIterator di(full_path, m_model.should_show_dotfiles() ? Core::DirIterator::SkipParentAndBaseDir : Core::DirIterator::SkipDots);
|
||||||
if (di.has_error()) {
|
if (di.has_error()) {
|
||||||
m_error = di.error();
|
m_error = di.error();
|
||||||
fprintf(stderr, "DirIterator: %s\n", di.error_string());
|
fprintf(stderr, "DirIterator: %s\n", di.error_string());
|
||||||
|
@ -108,11 +108,11 @@ void FileSystemModel::Node::traverse_if_needed(const FileSystemModel& model)
|
||||||
|
|
||||||
for (auto& name : child_names) {
|
for (auto& name : child_names) {
|
||||||
String child_path = String::format("%s/%s", full_path.characters(), name.characters());
|
String child_path = String::format("%s/%s", full_path.characters(), name.characters());
|
||||||
NonnullOwnPtr<Node> child = make<Node>();
|
auto child = adopt_own(*new Node(m_model));
|
||||||
bool ok = child->fetch_data(child_path, false);
|
bool ok = child->fetch_data(child_path, false);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
continue;
|
continue;
|
||||||
if (model.m_mode == DirectoriesOnly && !S_ISDIR(child->mode))
|
if (m_model.m_mode == DirectoriesOnly && !S_ISDIR(child->mode))
|
||||||
continue;
|
continue;
|
||||||
child->name = name;
|
child->name = name;
|
||||||
child->parent = this;
|
child->parent = this;
|
||||||
|
@ -131,7 +131,7 @@ void FileSystemModel::Node::traverse_if_needed(const FileSystemModel& model)
|
||||||
fcntl(m_watch_fd, F_SETFD, FD_CLOEXEC);
|
fcntl(m_watch_fd, F_SETFD, FD_CLOEXEC);
|
||||||
dbg() << "Watching " << full_path << " for changes, m_watch_fd = " << m_watch_fd;
|
dbg() << "Watching " << full_path << " for changes, m_watch_fd = " << m_watch_fd;
|
||||||
m_notifier = Core::Notifier::construct(m_watch_fd, Core::Notifier::Event::Read);
|
m_notifier = Core::Notifier::construct(m_watch_fd, Core::Notifier::Event::Read);
|
||||||
m_notifier->on_ready_to_read = [this, &model] {
|
m_notifier->on_ready_to_read = [this] {
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
int rc = read(m_notifier->fd(), buffer, sizeof(buffer));
|
int rc = read(m_notifier->fd(), buffer, sizeof(buffer));
|
||||||
ASSERT(rc >= 0);
|
ASSERT(rc >= 0);
|
||||||
|
@ -139,27 +139,27 @@ void FileSystemModel::Node::traverse_if_needed(const FileSystemModel& model)
|
||||||
has_traversed = false;
|
has_traversed = false;
|
||||||
mode = 0;
|
mode = 0;
|
||||||
children.clear();
|
children.clear();
|
||||||
reify_if_needed(model);
|
reify_if_needed();
|
||||||
const_cast<FileSystemModel&>(model).did_update();
|
m_model.did_update();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::Node::reify_if_needed(const FileSystemModel& model)
|
void FileSystemModel::Node::reify_if_needed()
|
||||||
{
|
{
|
||||||
traverse_if_needed(model);
|
traverse_if_needed();
|
||||||
if (mode != 0)
|
if (mode != 0)
|
||||||
return;
|
return;
|
||||||
fetch_data(full_path(model), parent == nullptr);
|
fetch_data(full_path(), parent == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
String FileSystemModel::Node::full_path(const FileSystemModel& model) const
|
String FileSystemModel::Node::full_path() const
|
||||||
{
|
{
|
||||||
Vector<String, 32> lineage;
|
Vector<String, 32> lineage;
|
||||||
for (auto* ancestor = parent; ancestor; ancestor = ancestor->parent) {
|
for (auto* ancestor = parent; ancestor; ancestor = ancestor->parent) {
|
||||||
lineage.append(ancestor->name);
|
lineage.append(ancestor->name);
|
||||||
}
|
}
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
builder.append(model.root_path());
|
builder.append(m_model.root_path());
|
||||||
for (int i = lineage.size() - 1; i >= 0; --i) {
|
for (int i = lineage.size() - 1; i >= 0; --i) {
|
||||||
builder.append('/');
|
builder.append('/');
|
||||||
builder.append(lineage[i]);
|
builder.append(lineage[i]);
|
||||||
|
@ -174,17 +174,17 @@ ModelIndex FileSystemModel::index(const StringView& path, int column) const
|
||||||
LexicalPath lexical_path(path);
|
LexicalPath lexical_path(path);
|
||||||
const Node* node = m_root;
|
const Node* node = m_root;
|
||||||
if (lexical_path.string() == "/")
|
if (lexical_path.string() == "/")
|
||||||
return m_root->index(*this, column);
|
return m_root->index(column);
|
||||||
for (size_t i = 0; i < lexical_path.parts().size(); ++i) {
|
for (size_t i = 0; i < lexical_path.parts().size(); ++i) {
|
||||||
auto& part = lexical_path.parts()[i];
|
auto& part = lexical_path.parts()[i];
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (auto& child : node->children) {
|
for (auto& child : node->children) {
|
||||||
if (child.name == part) {
|
if (child.name == part) {
|
||||||
const_cast<Node&>(child).reify_if_needed(*this);
|
const_cast<Node&>(child).reify_if_needed();
|
||||||
node = &child;
|
node = &child;
|
||||||
found = true;
|
found = true;
|
||||||
if (i == lexical_path.parts().size() - 1)
|
if (i == lexical_path.parts().size() - 1)
|
||||||
return child.index(*this, column);
|
return child.index(column);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,8 +197,8 @@ ModelIndex FileSystemModel::index(const StringView& path, int column) const
|
||||||
String FileSystemModel::full_path(const ModelIndex& index) const
|
String FileSystemModel::full_path(const ModelIndex& index) const
|
||||||
{
|
{
|
||||||
auto& node = this->node(index);
|
auto& node = this->node(index);
|
||||||
const_cast<Node&>(node).reify_if_needed(*this);
|
const_cast<Node&>(node).reify_if_needed();
|
||||||
return node.full_path(*this);
|
return node.full_path();
|
||||||
}
|
}
|
||||||
|
|
||||||
FileSystemModel::FileSystemModel(const StringView& root_path, Mode mode)
|
FileSystemModel::FileSystemModel(const StringView& root_path, Mode mode)
|
||||||
|
@ -303,8 +303,8 @@ void FileSystemModel::set_root_path(const StringView& root_path)
|
||||||
|
|
||||||
void FileSystemModel::update()
|
void FileSystemModel::update()
|
||||||
{
|
{
|
||||||
m_root = make<Node>();
|
m_root = adopt_own(*new Node(*this));
|
||||||
m_root->reify_if_needed(*this);
|
m_root->reify_if_needed();
|
||||||
|
|
||||||
did_update();
|
did_update();
|
||||||
}
|
}
|
||||||
|
@ -312,7 +312,7 @@ void FileSystemModel::update()
|
||||||
int FileSystemModel::row_count(const ModelIndex& index) const
|
int FileSystemModel::row_count(const ModelIndex& index) const
|
||||||
{
|
{
|
||||||
Node& node = const_cast<Node&>(this->node(index));
|
Node& node = const_cast<Node&>(this->node(index));
|
||||||
node.reify_if_needed(*this);
|
node.reify_if_needed();
|
||||||
if (node.is_directory())
|
if (node.is_directory())
|
||||||
return node.children.size();
|
return node.children.size();
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -331,7 +331,7 @@ ModelIndex FileSystemModel::index(int row, int column, const ModelIndex& parent)
|
||||||
if (row < 0 || column < 0)
|
if (row < 0 || column < 0)
|
||||||
return {};
|
return {};
|
||||||
auto& node = this->node(parent);
|
auto& node = this->node(parent);
|
||||||
const_cast<Node&>(node).reify_if_needed(*this);
|
const_cast<Node&>(node).reify_if_needed();
|
||||||
if (static_cast<size_t>(row) >= node.children.size())
|
if (static_cast<size_t>(row) >= node.children.size())
|
||||||
return {};
|
return {};
|
||||||
return create_index(row, column, &node.children[row]);
|
return create_index(row, column, &node.children[row]);
|
||||||
|
@ -346,7 +346,7 @@ ModelIndex FileSystemModel::parent_index(const ModelIndex& index) const
|
||||||
ASSERT(&node == m_root);
|
ASSERT(&node == m_root);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return node.parent->index(*this, index.column());
|
return node.parent->index(index.column());
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant FileSystemModel::data(const ModelIndex& index, ModelRole role) const
|
Variant FileSystemModel::data(const ModelIndex& index, ModelRole role) const
|
||||||
|
@ -377,14 +377,14 @@ Variant FileSystemModel::data(const ModelIndex& index, ModelRole role) const
|
||||||
if (role == ModelRole::Custom) {
|
if (role == ModelRole::Custom) {
|
||||||
// For GUI::FileSystemModel, custom role means the full path.
|
// For GUI::FileSystemModel, custom role means the full path.
|
||||||
ASSERT(index.column() == Column::Name);
|
ASSERT(index.column() == Column::Name);
|
||||||
return node.full_path(*this);
|
return node.full_path();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == ModelRole::DragData) {
|
if (role == ModelRole::DragData) {
|
||||||
if (index.column() == Column::Name) {
|
if (index.column() == Column::Name) {
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
builder.append("file://");
|
builder.append("file://");
|
||||||
builder.append(node.full_path(*this));
|
builder.append(node.full_path());
|
||||||
return builder.to_string();
|
return builder.to_string();
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
|
@ -454,7 +454,7 @@ Icon FileSystemModel::icon_for(const Node& node) const
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.is_directory()) {
|
if (node.is_directory()) {
|
||||||
if (node.full_path(*this) == Core::StandardPaths::home_directory()) {
|
if (node.full_path() == Core::StandardPaths::home_directory()) {
|
||||||
if (node.is_selected())
|
if (node.is_selected())
|
||||||
return FileIconProvider::home_directory_open_icon();
|
return FileIconProvider::home_directory_open_icon();
|
||||||
return FileIconProvider::home_directory_icon();
|
return FileIconProvider::home_directory_icon();
|
||||||
|
@ -489,7 +489,7 @@ bool FileSystemModel::fetch_thumbnail_for(const Node& node)
|
||||||
{
|
{
|
||||||
// See if we already have the thumbnail
|
// See if we already have the thumbnail
|
||||||
// we're looking for in the cache.
|
// we're looking for in the cache.
|
||||||
auto path = node.full_path(*this);
|
auto path = node.full_path();
|
||||||
auto it = s_thumbnail_cache.find(path);
|
auto it = s_thumbnail_cache.find(path);
|
||||||
if (it != s_thumbnail_cache.end()) {
|
if (it != s_thumbnail_cache.end()) {
|
||||||
if (!(*it).value)
|
if (!(*it).value)
|
||||||
|
|
|
@ -87,11 +87,18 @@ public:
|
||||||
int error() const { return m_error; }
|
int error() const { return m_error; }
|
||||||
const char* error_string() const { return strerror(m_error); }
|
const char* error_string() const { return strerror(m_error); }
|
||||||
|
|
||||||
String full_path(const FileSystemModel&) const;
|
String full_path() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class FileSystemModel;
|
friend class FileSystemModel;
|
||||||
|
|
||||||
|
explicit Node(FileSystemModel& model)
|
||||||
|
: m_model(model)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSystemModel& m_model;
|
||||||
|
|
||||||
Node* parent { nullptr };
|
Node* parent { nullptr };
|
||||||
NonnullOwnPtrVector<Node> children;
|
NonnullOwnPtrVector<Node> children;
|
||||||
bool has_traversed { false };
|
bool has_traversed { false };
|
||||||
|
@ -103,9 +110,9 @@ public:
|
||||||
|
|
||||||
int m_error { 0 };
|
int m_error { 0 };
|
||||||
|
|
||||||
ModelIndex index(const FileSystemModel&, int column) const;
|
ModelIndex index(int column) const;
|
||||||
void traverse_if_needed(const FileSystemModel&);
|
void traverse_if_needed();
|
||||||
void reify_if_needed(const FileSystemModel&);
|
void reify_if_needed();
|
||||||
bool fetch_data(const String& full_path, bool is_root);
|
bool fetch_data(const String& full_path, bool is_root);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue