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