Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2022, the SerenityOS developers.
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#pragma once
9
10#include <AK/HashMap.h>
11#include <LibCore/DateTime.h>
12#include <LibCore/FileWatcher.h>
13#include <LibGUI/Model.h>
14#include <string.h>
15#include <sys/stat.h>
16#include <time.h>
17
18namespace GUI {
19
20class FileSystemModel
21 : public Model
22 , public Weakable<FileSystemModel> {
23 friend struct Node;
24
25public:
26 enum Mode {
27 Invalid,
28 DirectoriesOnly,
29 FilesAndDirectories
30 };
31
32 enum Column {
33 Icon = 0,
34 Name,
35 Size,
36 User,
37 Group,
38 Permissions,
39 ModificationTime,
40 Inode,
41 SymlinkTarget,
42 __Count,
43 };
44
45 struct Node {
46 ~Node() = default;
47
48 DeprecatedString name;
49 DeprecatedString symlink_target;
50 size_t size { 0 };
51 mode_t mode { 0 };
52 uid_t uid { 0 };
53 gid_t gid { 0 };
54 ino_t inode { 0 };
55 time_t mtime { 0 };
56 bool is_accessible_directory { false };
57
58 size_t total_size { 0 };
59
60 mutable RefPtr<Gfx::Bitmap> thumbnail;
61 bool is_directory() const { return S_ISDIR(mode); }
62 bool is_symlink_to_directory() const;
63 bool is_executable() const { return mode & (S_IXUSR | S_IXGRP | S_IXOTH); }
64
65 bool is_selected() const { return m_selected; }
66 void set_selected(bool selected);
67
68 bool has_error() const { return m_error != 0; }
69 int error() const { return m_error; }
70 char const* error_string() const { return strerror(m_error); }
71
72 DeprecatedString full_path() const;
73
74 private:
75 friend class FileSystemModel;
76
77 explicit Node(FileSystemModel& model)
78 : m_model(model)
79 {
80 }
81
82 FileSystemModel& m_model;
83
84 Node* m_parent { nullptr };
85 Vector<NonnullOwnPtr<Node>> m_children;
86 bool m_has_traversed { false };
87
88 bool m_selected { false };
89
90 int m_error { 0 };
91 bool m_parent_of_root { false };
92
93 ModelIndex index(int column) const;
94 void traverse_if_needed();
95 void reify_if_needed();
96 bool fetch_data(DeprecatedString const& full_path, bool is_root);
97
98 OwnPtr<Node> create_child(DeprecatedString const& child_name);
99 };
100
101 static NonnullRefPtr<FileSystemModel> create(DeprecatedString root_path = "/", Mode mode = Mode::FilesAndDirectories)
102 {
103 return adopt_ref(*new FileSystemModel(root_path, mode));
104 }
105 virtual ~FileSystemModel() override = default;
106
107 DeprecatedString root_path() const { return m_root_path; }
108 void set_root_path(DeprecatedString);
109 DeprecatedString full_path(ModelIndex const&) const;
110 ModelIndex index(DeprecatedString path, int column) const;
111
112 void update_node_on_selection(ModelIndex const&, bool const);
113 ModelIndex m_previously_selected_index {};
114
115 Node const& node(ModelIndex const& index) const;
116
117 Function<void(int done, int total)> on_thumbnail_progress;
118 Function<void()> on_complete;
119 Function<void(int error, char const* error_string)> on_directory_change_error;
120 Function<void(int error, char const* error_string)> on_rename_error;
121 Function<void(DeprecatedString const& old_name, DeprecatedString const& new_name)> on_rename_successful;
122 Function<void()> on_root_path_removed;
123
124 virtual int tree_column() const override { return Column::Name; }
125 virtual int row_count(ModelIndex const& = ModelIndex()) const override;
126 virtual int column_count(ModelIndex const& = ModelIndex()) const override;
127 virtual DeprecatedString column_name(int column) const override;
128 virtual Variant data(ModelIndex const&, ModelRole = ModelRole::Display) const override;
129 virtual ModelIndex parent_index(ModelIndex const&) const override;
130 virtual ModelIndex index(int row, int column = 0, ModelIndex const& parent = ModelIndex()) const override;
131 virtual StringView drag_data_type() const override { return "text/uri-list"sv; }
132 virtual bool accepts_drag(ModelIndex const&, Vector<DeprecatedString> const& mime_types) const override;
133 virtual bool is_column_sortable(int column_index) const override { return column_index != Column::Icon; }
134 virtual bool is_editable(ModelIndex const&) const override;
135 virtual bool is_searchable() const override { return true; }
136 virtual void set_data(ModelIndex const&, Variant const&) override;
137 virtual Vector<ModelIndex> matches(StringView, unsigned = MatchesFlag::AllMatching, ModelIndex const& = ModelIndex()) override;
138 virtual void invalidate() override;
139
140 static DeprecatedString timestamp_string(time_t timestamp)
141 {
142 return Core::DateTime::from_timestamp(timestamp).to_deprecated_string();
143 }
144
145 bool should_show_dotfiles() const { return m_should_show_dotfiles; }
146 void set_should_show_dotfiles(bool);
147
148 Optional<Vector<DeprecatedString>> allowed_file_extensions() const { return m_allowed_file_extensions; }
149 void set_allowed_file_extensions(Optional<Vector<DeprecatedString>> const& allowed_file_extensions);
150
151private:
152 FileSystemModel(DeprecatedString root_path, Mode);
153
154 DeprecatedString name_for_uid(uid_t) const;
155 DeprecatedString name_for_gid(gid_t) const;
156
157 Optional<Node const&> node_for_path(DeprecatedString const&) const;
158
159 HashMap<uid_t, DeprecatedString> m_user_names;
160 HashMap<gid_t, DeprecatedString> m_group_names;
161
162 bool fetch_thumbnail_for(Node const& node);
163 GUI::Icon icon_for(Node const& node) const;
164
165 void handle_file_event(Core::FileWatcherEvent const& event);
166
167 DeprecatedString m_root_path;
168 Mode m_mode { Invalid };
169 OwnPtr<Node> m_root { nullptr };
170
171 unsigned m_thumbnail_progress { 0 };
172 unsigned m_thumbnail_progress_total { 0 };
173
174 Optional<Vector<DeprecatedString>> m_allowed_file_extensions;
175
176 bool m_should_show_dotfiles { false };
177
178 RefPtr<Core::FileWatcher> m_file_watcher;
179};
180
181}