diff --git a/Userland/Libraries/LibGUI/FileSystemModel.cpp b/Userland/Libraries/LibGUI/FileSystemModel.cpp index eb2b931641..144fd6ff63 100644 --- a/Userland/Libraries/LibGUI/FileSystemModel.cpp +++ b/Userland/Libraries/LibGUI/FileSystemModel.cpp @@ -107,15 +107,11 @@ void FileSystemModel::Node::traverse_if_needed() NonnullOwnPtrVector file_children; for (auto& child_name : child_names) { - String child_path = String::formatted("{}/{}", full_path, child_name); - auto child = adopt_own(*new Node(m_model)); - bool ok = child->fetch_data(child_path, false); - if (!ok) + auto maybe_child = create_child(child_name); + if (!maybe_child) continue; - if (m_model.m_mode == DirectoriesOnly && !S_ISDIR(child->mode)) - continue; - child->name = child_name; - child->m_parent = this; + + auto child = maybe_child.release_nonnull(); total_size += child->size; if (S_ISDIR(child->mode)) directory_children.append(move(child)); @@ -142,6 +138,23 @@ void FileSystemModel::Node::traverse_if_needed() } } +OwnPtr FileSystemModel::Node::create_child(String const& child_name) +{ + String child_path = LexicalPath::join(full_path(), child_name).string(); + auto child = adopt_own(*new Node(m_model)); + + bool ok = child->fetch_data(child_path, false); + if (!ok) + return {}; + + if (m_model.m_mode == DirectoriesOnly && !S_ISDIR(child->mode)) + return {}; + + child->name = child_name; + child->m_parent = this; + return child; +} + void FileSystemModel::Node::reify_if_needed() { traverse_if_needed(); @@ -251,39 +264,7 @@ FileSystemModel::FileSystemModel(String root_path, Mode mode) m_file_watcher = result.release_value(); m_file_watcher->on_change = [this](Core::FileWatcherEvent const& event) { - Node const* maybe_node = node_for_path(event.event_path); - if (maybe_node == nullptr) { - dbgln("Received event at \"{}\" but we don't have that node", event.event_path); - return; - } - auto& node = *const_cast(maybe_node); - - dbgln("Event at \"{}\" on Node {}: {}", node.full_path(), &node, event); - - // FIXME: Your time is coming, un-granular updates. - auto refresh_node = [](Node& node) { - node.m_has_traversed = false; - node.mode = 0; - node.m_children.clear(); - node.reify_if_needed(); - }; - - if (event.type == Core::FileWatcherEvent::Type::Deleted) { - auto canonical_event_path = LexicalPath::canonicalized_path(event.event_path); - if (m_root_path.starts_with(canonical_event_path)) { - // Deleted directory contains our root, so navigate to our nearest parent. - auto new_path = LexicalPath(m_root_path).parent(); - while (!Core::File::is_directory(new_path.string())) - new_path = new_path.parent(); - - set_root_path(new_path.string()); - } else if (node.m_parent) { - refresh_node(*node.m_parent); - } - } else { - refresh_node(node); - } - did_update(); + handle_file_event(event); }; invalidate(); @@ -386,6 +367,74 @@ void FileSystemModel::invalidate() Model::invalidate(); } +void FileSystemModel::handle_file_event(Core::FileWatcherEvent const& event) +{ + if (event.type == Core::FileWatcherEvent::Type::ChildCreated) { + if (node_for_path(event.event_path) != nullptr) + return; + } else { + if (node_for_path(event.event_path) == nullptr) + return; + } + + switch (event.type) { + case Core::FileWatcherEvent::Type::ChildCreated: { + LexicalPath path { event.event_path }; + auto& parts = path.parts_view(); + StringView child_name = parts.last(); + + auto parent_name = path.parent().string(); + Node* parent = const_cast(node_for_path(parent_name)); + if (parent == nullptr) { + dbgln("Got a ChildCreated on '{}' but that path does not exist?!", parent_name); + break; + } + + int child_count = parent->m_children.size(); + + auto maybe_child = parent->create_child(child_name); + if (!maybe_child) + break; + + begin_insert_rows(parent->index(0), child_count, child_count); + + auto child = maybe_child.release_nonnull(); + parent->total_size += child->size; + parent->m_children.append(move(child)); + + end_insert_rows(); + break; + } + case Core::FileWatcherEvent::Type::Deleted: + case Core::FileWatcherEvent::Type::ChildDeleted: { + Node* child = const_cast(node_for_path(event.event_path)); + if (child == nullptr) { + dbgln("Got a ChildDeleted/Deleted on '{}' but the child does not exist?! (already gone?)", event.event_path); + break; + } + + auto index = child->index(0); + begin_delete_rows(index.parent(), index.row(), index.row()); + + Node* parent = child->m_parent; + parent->m_children.remove(index.row()); + + end_delete_rows(); + break; + } + case Core::FileWatcherEvent::Type::MetadataModified: { + // FIXME: Do we do anything in case the metadata is modified? + // Perhaps re-stat'ing the modified node would make sense + // here, but let's leave that to when we actually need it. + break; + } + default: + VERIFY_NOT_REACHED(); + } + + did_update(UpdateFlag::DontInvalidateIndices); +} + int FileSystemModel::row_count(const ModelIndex& index) const { Node& node = const_cast(this->node(index)); diff --git a/Userland/Libraries/LibGUI/FileSystemModel.h b/Userland/Libraries/LibGUI/FileSystemModel.h index 6a8f768ec3..3bbfeecfaf 100644 --- a/Userland/Libraries/LibGUI/FileSystemModel.h +++ b/Userland/Libraries/LibGUI/FileSystemModel.h @@ -93,7 +93,9 @@ public: ModelIndex index(int column) const; void traverse_if_needed(); void reify_if_needed(); - bool fetch_data(const String& full_path, bool is_root); + bool fetch_data(String const& full_path, bool is_root); + + OwnPtr create_child(String const& child_name); }; static NonnullRefPtr create(String root_path = "/", Mode mode = Mode::FilesAndDirectories) @@ -155,6 +157,8 @@ private: bool fetch_thumbnail_for(const Node& node); GUI::Icon icon_for(const Node& node) const; + void handle_file_event(Core::FileWatcherEvent const& event); + String m_root_path; Mode m_mode { Invalid }; OwnPtr m_root { nullptr };