2019-02-25 16:52:53 +00:00
|
|
|
/*
|
|
|
|
Copyright 2019 flyinghead
|
|
|
|
|
|
|
|
This file is part of reicast.
|
|
|
|
|
|
|
|
reicast is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
reicast is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include "gui_util.h"
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
#include <algorithm>
|
2019-09-07 12:37:39 +00:00
|
|
|
#include <cstdlib>
|
2019-03-30 18:26:05 +00:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#include <io.h>
|
|
|
|
#define access _access
|
|
|
|
#define R_OK 4
|
|
|
|
#else
|
2019-02-25 16:52:53 +00:00
|
|
|
#include <unistd.h>
|
2019-03-30 18:26:05 +00:00
|
|
|
#endif
|
2019-09-02 20:57:53 +00:00
|
|
|
#include <dirent.h>
|
2019-02-25 16:52:53 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
|
|
#include "types.h"
|
|
|
|
#include "stdclass.h"
|
|
|
|
#include "imgui/imgui.h"
|
|
|
|
|
|
|
|
extern int screen_width, screen_height;
|
|
|
|
|
|
|
|
static std::string select_current_directory;
|
|
|
|
static std::vector<std::string> select_subfolders;
|
2019-03-27 20:09:53 +00:00
|
|
|
bool subfolders_read;
|
2019-02-25 16:52:53 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
static const std::string separators = "/\\";
|
|
|
|
static const std::string native_separator = "\\";
|
|
|
|
#else
|
|
|
|
static const std::string separators = "/";
|
|
|
|
static const std::string native_separator = "/";
|
|
|
|
#endif
|
2019-03-04 20:49:36 +00:00
|
|
|
#define PSEUDO_ROOT ":"
|
2019-02-25 16:52:53 +00:00
|
|
|
|
|
|
|
void select_directory_popup(const char *prompt, float scaling, StringCallback callback)
|
|
|
|
{
|
|
|
|
if (select_current_directory.empty())
|
|
|
|
{
|
2019-08-25 16:38:36 +00:00
|
|
|
#if defined(__ANDROID__)
|
2019-02-25 16:52:53 +00:00
|
|
|
const char *home = getenv("REICAST_HOME");
|
|
|
|
if (home != NULL)
|
2019-03-04 20:49:36 +00:00
|
|
|
{
|
|
|
|
const char *pcolon = strchr(home, ':');
|
|
|
|
if (pcolon != NULL)
|
|
|
|
select_current_directory = std::string(home, pcolon - home);
|
|
|
|
else
|
|
|
|
select_current_directory = home;
|
|
|
|
}
|
2019-02-25 16:52:53 +00:00
|
|
|
#elif HOST_OS == OS_LINUX || HOST_OS == OS_DARWIN
|
|
|
|
const char *home = getenv("HOME");
|
|
|
|
if (home != NULL)
|
|
|
|
select_current_directory = home;
|
2019-08-25 17:29:56 +00:00
|
|
|
#elif defined(_WIN32)
|
2019-02-25 16:52:53 +00:00
|
|
|
const char *home = getenv("HOMEPATH");
|
2019-02-25 18:15:59 +00:00
|
|
|
const char *home_drive = getenv("HOMEDRIVE");
|
2019-02-25 16:52:53 +00:00
|
|
|
if (home != NULL)
|
2019-02-25 18:15:59 +00:00
|
|
|
{
|
|
|
|
if (home_drive != NULL)
|
|
|
|
select_current_directory = home_drive;
|
|
|
|
else
|
|
|
|
select_current_directory.clear();
|
|
|
|
select_current_directory += home;
|
|
|
|
}
|
2019-02-25 16:52:53 +00:00
|
|
|
#endif
|
|
|
|
if (select_current_directory.empty())
|
|
|
|
{
|
|
|
|
select_current_directory = get_writable_config_path("");
|
|
|
|
if (select_current_directory.empty())
|
|
|
|
select_current_directory = ".";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::SetNextWindowPos(ImVec2(0, 0));
|
|
|
|
ImGui::SetNextWindowSize(ImVec2(screen_width, screen_height));
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0);
|
|
|
|
|
|
|
|
if (ImGui::BeginPopupModal(prompt, NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ))
|
|
|
|
{
|
|
|
|
std::string path = select_current_directory;
|
|
|
|
int last_sep = path.find_last_of(separators);
|
|
|
|
if (last_sep == path.size() - 1)
|
|
|
|
path.pop_back();
|
|
|
|
|
|
|
|
static std::string error_message;
|
|
|
|
|
|
|
|
if (!subfolders_read)
|
|
|
|
{
|
|
|
|
select_subfolders.clear();
|
|
|
|
error_message.clear();
|
|
|
|
#ifdef _WIN32
|
2019-03-04 20:49:36 +00:00
|
|
|
if (select_current_directory == PSEUDO_ROOT)
|
2019-02-25 16:52:53 +00:00
|
|
|
{
|
2019-02-25 18:15:59 +00:00
|
|
|
error_message = "Drives";
|
2019-02-25 16:52:53 +00:00
|
|
|
// List all the drives
|
|
|
|
u32 drives = GetLogicalDrives();
|
|
|
|
for (int i = 0; i < 32; i++)
|
|
|
|
if ((drives & (1 << i)) != 0)
|
2019-02-25 18:15:59 +00:00
|
|
|
select_subfolders.push_back(std::string(1, (char)('A' + i)) + ":\\");
|
2019-02-25 16:52:53 +00:00
|
|
|
}
|
|
|
|
else
|
2019-08-25 16:38:36 +00:00
|
|
|
#elif __ANDROID__
|
2019-03-04 20:49:36 +00:00
|
|
|
if (select_current_directory == PSEUDO_ROOT)
|
|
|
|
{
|
|
|
|
error_message = "Storage Locations";
|
|
|
|
const char *home = getenv("REICAST_HOME");
|
|
|
|
while (home != NULL)
|
|
|
|
{
|
|
|
|
const char *pcolon = strchr(home, ':');
|
|
|
|
if (pcolon != NULL)
|
|
|
|
{
|
|
|
|
select_subfolders.push_back(std::string(home, pcolon - home));
|
|
|
|
home = pcolon + 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
select_subfolders.push_back(home);
|
|
|
|
home = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2019-02-25 16:52:53 +00:00
|
|
|
#endif
|
|
|
|
{
|
|
|
|
DIR *dir = opendir(select_current_directory.c_str());
|
|
|
|
if (dir == NULL)
|
|
|
|
{
|
|
|
|
error_message = "Cannot read " + select_current_directory;
|
|
|
|
select_subfolders.push_back("..");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bool dotdot_seen = false;
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
struct dirent *entry = readdir(dir);
|
|
|
|
if (entry == NULL)
|
|
|
|
break;
|
2019-10-06 08:58:44 +00:00
|
|
|
std::string name(entry->d_name);
|
2019-02-25 16:52:53 +00:00
|
|
|
if (name == ".")
|
|
|
|
continue;
|
|
|
|
std::string child_path = path + "/" + name;
|
2019-02-25 18:15:59 +00:00
|
|
|
bool is_dir = false;
|
|
|
|
#ifndef _WIN32
|
|
|
|
if (entry->d_type == DT_DIR)
|
|
|
|
is_dir = true;
|
2019-02-25 16:52:53 +00:00
|
|
|
if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK)
|
2019-02-25 18:15:59 +00:00
|
|
|
#endif
|
2019-02-25 16:52:53 +00:00
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
if (stat(child_path.c_str(), &st) != 0)
|
|
|
|
continue;
|
|
|
|
if (S_ISDIR(st.st_mode))
|
2019-02-25 18:15:59 +00:00
|
|
|
is_dir = true;
|
2019-02-25 16:52:53 +00:00
|
|
|
}
|
2019-02-25 18:15:59 +00:00
|
|
|
if (is_dir && access(child_path.c_str(), R_OK) == 0)
|
2019-03-04 20:49:36 +00:00
|
|
|
{
|
|
|
|
if (name == "..")
|
|
|
|
dotdot_seen = true;
|
2019-02-25 16:52:53 +00:00
|
|
|
select_subfolders.push_back(name);
|
2019-03-04 20:49:36 +00:00
|
|
|
}
|
2019-02-25 16:52:53 +00:00
|
|
|
}
|
|
|
|
closedir(dir);
|
2019-08-25 16:38:36 +00:00
|
|
|
#if defined(_WIN32) || defined(__ANDROID__)
|
2019-02-25 16:52:53 +00:00
|
|
|
if (!dotdot_seen)
|
|
|
|
select_subfolders.push_back("..");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::stable_sort(select_subfolders.begin(), select_subfolders.end());
|
|
|
|
subfolders_read = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::Text("%s", error_message.empty() ? select_current_directory.c_str() : error_message.c_str());
|
2019-03-29 16:35:00 +00:00
|
|
|
ImGui::BeginChild(ImGui::GetID("dir_list"), ImVec2(0, - 30 * scaling - ImGui::GetStyle().ItemSpacing.y), true);
|
2019-02-25 16:52:53 +00:00
|
|
|
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8 * scaling, 20 * scaling)); // from 8, 4
|
|
|
|
|
|
|
|
|
2019-06-08 11:04:35 +00:00
|
|
|
for (auto& name : select_subfolders)
|
2019-02-25 16:52:53 +00:00
|
|
|
{
|
|
|
|
std::string child_path;
|
|
|
|
if (name == "..")
|
|
|
|
{
|
|
|
|
std::string::size_type last_sep = path.find_last_of(separators);
|
|
|
|
if (last_sep == std::string::npos)
|
|
|
|
{
|
|
|
|
if (path.empty())
|
|
|
|
// Root folder
|
|
|
|
continue;
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (path.size() == 2 && path[1] == ':')
|
2019-03-04 20:49:36 +00:00
|
|
|
child_path = PSEUDO_ROOT;
|
2019-02-25 16:52:53 +00:00
|
|
|
else
|
|
|
|
#endif
|
|
|
|
if (path == ".")
|
|
|
|
child_path = "..";
|
|
|
|
else if (path == "..")
|
|
|
|
child_path = ".." + native_separator + "..";
|
|
|
|
else
|
|
|
|
child_path = ".";
|
|
|
|
}
|
|
|
|
else if (last_sep == 0)
|
|
|
|
child_path = native_separator;
|
|
|
|
else if (path.size() >= 2 && path.substr(path.size() - 2) == "..")
|
|
|
|
child_path = path + native_separator + "..";
|
|
|
|
else
|
2019-02-25 18:15:59 +00:00
|
|
|
{
|
2019-02-25 16:52:53 +00:00
|
|
|
child_path = path.substr(0, last_sep);
|
2019-02-25 18:15:59 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
if (child_path.size() == 2 && child_path[1] == ':') // C: -> C:/
|
|
|
|
child_path += native_separator;
|
|
|
|
#endif
|
|
|
|
}
|
2019-08-25 16:38:36 +00:00
|
|
|
#ifdef __ANDROID__
|
2019-03-04 20:49:36 +00:00
|
|
|
if (access(child_path.c_str(), R_OK) != 0)
|
|
|
|
child_path = PSEUDO_ROOT;
|
|
|
|
#endif
|
2019-02-25 16:52:53 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-08-25 16:38:36 +00:00
|
|
|
#if defined(_WIN32) || defined(__ANDROID__)
|
2019-03-04 20:49:36 +00:00
|
|
|
if (path == PSEUDO_ROOT)
|
2019-02-25 18:15:59 +00:00
|
|
|
child_path = name;
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
child_path = path + native_separator + name;
|
2019-02-25 16:52:53 +00:00
|
|
|
}
|
|
|
|
if (ImGui::Selectable(name.c_str()))
|
|
|
|
{
|
|
|
|
subfolders_read = false;
|
|
|
|
select_current_directory = child_path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::PopStyleVar();
|
|
|
|
ImGui::EndChild();
|
2019-03-29 16:35:00 +00:00
|
|
|
if (ImGui::Button("Select Current Directory", ImVec2(0, 30 * scaling)))
|
2019-02-25 16:52:53 +00:00
|
|
|
{
|
|
|
|
subfolders_read = false;
|
|
|
|
callback(false, select_current_directory);
|
|
|
|
ImGui::CloseCurrentPopup();
|
|
|
|
}
|
|
|
|
ImGui::SameLine();
|
2019-03-29 16:35:00 +00:00
|
|
|
if (ImGui::Button("Cancel", ImVec2(0, 30 * scaling)))
|
2019-02-25 16:52:53 +00:00
|
|
|
{
|
|
|
|
subfolders_read = false;
|
|
|
|
callback(true, "");
|
|
|
|
ImGui::CloseCurrentPopup();
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::EndPopup();
|
|
|
|
}
|
|
|
|
ImGui::PopStyleVar();
|
|
|
|
}
|