1 // SuperTux
2 // Copyright (C) 2016 Hume2 <teratux.mail@gmail.com>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 #include "gui/menu_filesystem.hpp"
18
19 #include <physfs.h>
20
21 #include "addon/addon_manager.hpp"
22 #include "gui/menu_item.hpp"
23 #include "gui/menu_manager.hpp"
24 #include "physfs/util.hpp"
25 #include "util/file_system.hpp"
26 #include "util/log.hpp"
27 #include "util/gettext.hpp"
28 #include "util/string_util.hpp"
29
FileSystemMenu(std::string * filename,const std::vector<std::string> & extensions,const std::string & basedir,std::function<void (std::string)> callback)30 FileSystemMenu::FileSystemMenu(std::string* filename, const std::vector<std::string>& extensions,
31 const std::string& basedir, std::function<void(std::string)> callback) :
32 m_filename(filename),
33 // when a basedir is given, 'filename' is relative to basedir, so
34 // it's useless as a starting point
35 m_directory(basedir.empty() ? (filename ? FileSystem::dirname(*filename) : "/") : basedir),
36 m_extensions(extensions),
37 m_basedir(basedir),
38 m_directories(),
39 m_files(),
40 m_callback(std::move(callback))
41 {
42 AddonManager::current()->unmount_old_addons();
43
44 if (!PHYSFS_exists(m_directory.c_str())) {
45 m_directory = "/"; //The filename is probably included in an old add-on.
46 }
47
48 refresh_items();
49 }
50
~FileSystemMenu()51 FileSystemMenu::~FileSystemMenu()
52 {
53 AddonManager::current()->mount_old_addons();
54 }
55
56 void
refresh_items()57 FileSystemMenu::refresh_items()
58 {
59 m_items.clear();
60 m_directories.clear();
61 m_files.clear();
62 m_directory = FileSystem::normalize(m_directory);
63
64 add_label(m_directory);
65 add_hl();
66
67 int item_id = 0;
68
69 // Do not allow leaving the data directory
70 if (m_directory != "/") {
71 m_directories.push_back("..");
72 }
73
74 char** dir_files = PHYSFS_enumerateFiles(m_directory.c_str());
75 if (dir_files)
76 {
77 for (const char* const* file = dir_files; *file != nullptr; ++file)
78 {
79 std::string filepath = FileSystem::join(m_directory, *file);
80 if (physfsutil::is_directory(filepath))
81 {
82 m_directories.push_back(*file);
83 }
84 else
85 {
86 if (AddonManager::current()->is_from_old_addon(filepath)) {
87 continue;
88 }
89
90 if (has_right_suffix(*file))
91 {
92 m_files.push_back(*file);
93 }
94 }
95 }
96 PHYSFS_freeList(dir_files);
97 }
98
99 for (const auto& item : m_directories)
100 {
101 add_entry(item_id, "[" + std::string(item) + "]");
102 item_id++;
103 }
104
105 for (const auto& item : m_files)
106 {
107 add_entry(item_id, item);
108 item_id++;
109 }
110
111 add_hl();
112 add_back(_("Cancel"));
113
114 m_active_item = 2;
115
116 // Re-center menu
117 on_window_resize();
118 }
119
120 bool
has_right_suffix(const std::string & file) const121 FileSystemMenu::has_right_suffix(const std::string& file) const
122 {
123 if (m_extensions.empty())
124 return true;
125
126 for (const auto& extension : m_extensions) {
127 if (StringUtil::has_suffix(file, extension))
128 {
129 return true;
130 }
131 }
132 return false;
133 }
134
135 void
menu_action(MenuItem & item)136 FileSystemMenu::menu_action(MenuItem& item)
137 {
138 if (item.get_id() >= 0) {
139 size_t id = item.get_id();
140 if (id < m_directories.size()) {
141 m_directory = FileSystem::join(m_directory, m_directories[id]);
142 refresh_items();
143 } else {
144 id -= m_directories.size();
145 if (id < m_files.size()) {
146 std::string new_filename = FileSystem::join(m_directory, m_files[id]);
147
148 if (!m_basedir.empty()) {
149 std::string temp_path = FileSystem::relpath(new_filename, m_basedir);
150 new_filename = temp_path.find("..") ? temp_path : new_filename;
151 }
152
153 if (m_filename)
154 *m_filename = new_filename;
155
156 if (m_callback)
157 m_callback(new_filename);
158
159 MenuManager::instance().pop_menu();
160 } else {
161 log_warning << "Selected invalid file or directory" << std::endl;
162 }
163 }
164 }
165 }
166
167 /* EOF */
168