musikcube/enc_temp_folder/12582bf990462ae4b918b1e306ff024/DirectoryAdapter.cpp

364 lines
11 KiB
C++

//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2004-2021 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#include <stdafx.h>
#include <musikcore/support/Common.h>
#include <cursespp/Text.h>
#include <cursespp/ScrollAdapterBase.h>
#include <cursespp/SingleLineEntry.h>
#include "DirectoryAdapter.h"
#include <filesystem>
namespace fs = std::filesystem;
using namespace musik::cube;
using namespace cursespp;
#ifdef WIN32
static const fs::path kDefaultRoot("");
#else
static const fs::path kDefaultRoot("/");
#endif
#ifdef WIN32
static void buildDriveList(std::vector<std::string>& target) {
target.clear();
static char buffer[4096];
const DWORD result = ::GetLogicalDriveStringsA(4096, buffer);
if (result && buffer) {
char* current = buffer;
while (*current) {
target.push_back(std::string(current));
current += strlen(current) + 1;
}
}
}
static bool shouldBuildDriveList(const fs::path& dir) {
return dir.u8string().size() == 0;
}
#endif
static bool hasSubdirectories(fs::path p, bool showDotfiles) {
try {
fs::directory_iterator end;
fs::directory_iterator file(p);
while (file != end) {
try {
if (fs::is_directory(file->status())) {
if (showDotfiles || file->path().filename().u8string().at(0) != '.') {
return true;
}
}
}
catch (...) {
/* may throw trying to stat the file */
}
++file;
}
}
catch (...) {
/* may throw trying to open the dir */
}
return false;
}
static void buildDirectoryList(
const fs::path& p,
std::vector<std::string>& target,
bool showDotfiles)
{
target.clear();
try {
fs::directory_iterator end;
fs::directory_iterator file(p);
while (file != end) {
try {
if (is_directory(file->status())) {
std::string leaf = file->path().filename().u8string();
if (showDotfiles || leaf.at(0) != '.') {
target.push_back(file->path().u8string());
}
}
}
catch (...) {
/* may throw trying to stat the file */
}
++file;
}
}
catch (...) {
/* may throw trying to open the directory */
}
try {
std::sort(
target.begin(),
target.end(),
std::locale(setlocale(LC_ALL, nullptr)));
}
catch (...) {
std::sort(target.begin(), target.end());
}
}
static std::string pathToString(fs::path path) {
return musik::core::NormalizeDir(path.u8string());
}
static fs::path stringToPath(const std::string& path) {
return fs::u8path(musik::core::NormalizeDir(path));
}
DirectoryAdapter::DirectoryAdapter() {
this->showDotfiles = false;
this->showRootDirectory = false;
this->dir = fs::path(fs::u8path(musik::core::GetHomeDirectory()));
this->rootDir = kDefaultRoot;
buildDirectoryList(dir, subdirs, showDotfiles);
}
DirectoryAdapter::~DirectoryAdapter() {
}
void DirectoryAdapter::SetAllowEscapeRoot(bool allow) {
this->allowEscapeRoot = allow;
}
void DirectoryAdapter::SetShowRootDirectory(bool show) {
if (show != this->showRootDirectory) {
this->showRootDirectory = show;
this->Refresh();
}
}
size_t DirectoryAdapter::Select(cursespp::ListWindow* window) {
const bool hasParent = this->ShowParentPath();
size_t selectedIndex = NO_INDEX;
size_t initialIndex = window->GetSelectedIndex();
if (this->IsCurrentDirectory(initialIndex)) {
return initialIndex;
}
if (hasParent && initialIndex == 0) {
if (selectedIndexStack.size()) {
selectedIndex = this->selectedIndexStack.top();
this->selectedIndexStack.pop();
}
#ifdef WIN32
/* has_relative_path() is weird, as it does what i'd expect
has_parent_path() to do... documentation doesn't really help
much, but basically we're checking if we're at the root here;
if we are, we reset the directory to empty so we can build a
drive list. */
if (!this->dir.has_relative_path()) {
this->dir = "";
}
else {
this->dir = this->dir.parent_path();
}
#else
this->dir = this->dir.parent_path();
#endif
}
else {
selectedIndexStack.push(initialIndex);
initialIndex -= this->GetHeaderCount();
this->dir = fs::u8path(this->subdirs.at(initialIndex));
}
#ifdef WIN32
if (shouldBuildDriveList(this->dir)) {
dir = fs::path();
buildDriveList(subdirs);
return selectedIndex;
}
#endif
buildDirectoryList(dir, subdirs, showDotfiles);
window->OnAdapterChanged();
return selectedIndex;
}
void DirectoryAdapter::SetRootDirectory(const std::string& directory) {
if (directory.size()) {
dir = rootDir = stringToPath(directory);
}
else {
dir = musik::core::GetHomeDirectory();
rootDir = kDefaultRoot;
}
buildDirectoryList(dir, subdirs, showDotfiles);
}
std::string DirectoryAdapter::GetFullPathAt(size_t index) {
const bool hasParent = this->ShowParentPath();
if (hasParent && index == 0) {
return "";
}
if (this->IsCurrentDirectory(index)) {
return this->dir.u8string();
}
index -= this->GetHeaderCount();
return pathToString(fs::u8path(this->subdirs.at(index)));
}
std::string DirectoryAdapter::GetLeafAt(size_t index) {
if (this->ShowParentPath() && index == 0) {
return "..";
}
if (this->IsCurrentDirectory(index)) {
return ".";
}
return fs::u8path(this->subdirs.at(index)).u8string();
}
size_t DirectoryAdapter::IndexOf(const std::string& leaf) {
for (size_t i = 0; i < this->subdirs.size(); i++) {
if (this->subdirs.at(i) == leaf) {
return i + this->GetHeaderCount();
}
}
return NO_INDEX;
}
size_t DirectoryAdapter::GetEntryCount() {
return this->GetHeaderCount() + subdirs.size();
}
void DirectoryAdapter::SetDotfilesVisible(bool visible) {
if (showDotfiles != visible) {
showDotfiles = visible;
#ifdef WIN32
if (shouldBuildDriveList(this->dir)) {
buildDriveList(subdirs);
return;
}
#endif
buildDirectoryList(dir, subdirs, showDotfiles);
}
}
std::string DirectoryAdapter::GetParentPath() {
if (dir.has_parent_path() &&
pathToString(this->dir) != pathToString(this->rootDir))
{
return pathToString(dir.parent_path().u8string());
}
return "";
}
std::string DirectoryAdapter::GetCurrentPath() {
return pathToString(dir.u8string());
}
void DirectoryAdapter::Refresh() {
buildDirectoryList(dir, subdirs, showDotfiles);
}
bool DirectoryAdapter::IsAtRoot() {
return pathToString(this->dir) == pathToString(this->rootDir);
}
bool DirectoryAdapter::ShowParentPath() {
if (this->IsAtRoot() && !this->allowEscapeRoot) {
return false;
}
return dir.has_parent_path();
}
bool DirectoryAdapter::ShowCurrentDirectory() {
return this->showRootDirectory && this->IsAtRoot();
}
size_t DirectoryAdapter::GetHeaderCount() {
return (this->ShowParentPath() ? 1 : 0) + (this->ShowCurrentDirectory() ? 1 : 0);
}
bool DirectoryAdapter::HasSubDirectories(size_t index) {
const bool hasParent = this->ShowParentPath();
if (index == 0 && hasParent) {
return true;
}
if (this->IsCurrentDirectory(index)) {
return !this->subdirs.empty();
}
index -= this->GetHeaderCount();
return hasSubdirectories(fs::u8path(this->subdirs.at(index)), this->showDotfiles);
}
bool DirectoryAdapter::HasSubDirectories() {
return hasSubdirectories(this->dir, this->showDotfiles);
}
bool DirectoryAdapter::IsCurrentDirectory(size_t index) {
if (!this->showRootDirectory) {
return false;
}
auto const showParent = this->ShowParentPath();
return !showParent && index == 0;
}
IScrollAdapter::EntryPtr DirectoryAdapter::GetEntry(cursespp::ScrollableWindow* window, size_t index) {
if (this->ShowParentPath()) {
if (index == 0) {
return IScrollAdapter::EntryPtr(new SingleLineEntry(".."));
}
}
else if (this->ShowCurrentDirectory()) {
if (IsCurrentDirectory(index)) {
const auto dir = u8fmt("[%s]", + this->rootDir.parent_path().filename().u8string().c_str());
return IScrollAdapter::EntryPtr(new SingleLineEntry(dir));
}
}
index -= this->GetHeaderCount();
auto path = fs::u8path(this->subdirs[index]);
auto pathString = path.filename().u8string();
/* windows root drives are 'x:\', so they don't have a filename leaf. if
we resolve an empty string, just use the whole path */
if (!pathString.size()) {
pathString = path.u8string();
}
auto text = text::Ellipsize(pathString, this->GetWidth());
return IScrollAdapter::EntryPtr(new SingleLineEntry(text));
}