Skip to content

Commit

Permalink
Merge pull request #83 from OutpostUniverse/AddFileIoWindow
Browse files Browse the repository at this point in the history
Add file io window
  • Loading branch information
ldicker83 authored Jun 26, 2023
2 parents 2281d5c + a9307ce commit 84cd8d6
Show file tree
Hide file tree
Showing 7 changed files with 329 additions and 33 deletions.
24 changes: 19 additions & 5 deletions OP2-Landlord/EditorConfig.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
#include "EditorConfig.h"

#include <NAS2D/Dictionary.h>


#include <fstream>
#include <nlohmann/json.hpp>

Expand All @@ -19,7 +16,7 @@ namespace
return std::error_code{ errno, std::generic_category() }.message();
}

void loadConfig(const std::string filePath)
void loadConfigFromFile(const std::string filePath)
{
if (!std::filesystem::exists(filePath))
{
Expand All @@ -43,13 +40,29 @@ namespace
}
}


void saveConfigToFile(const std::string& filePath)
{
std::ofstream file;
if (!std::filesystem::exists(filePath))
{
file = std::ofstream{ filePath, std::ios::out | std::ios::binary };
if (!file)
{
throw std::runtime_error("Error opening file for writing: " + filePath + " : " + errorDescription());
}
}


}

}


EditorConfig::EditorConfig(const std::string& savePath) :
UserSavePath{ savePath }
{
loadConfig(UserSavePath + "editor.json");
loadConfigFromFile(UserSavePath + "editor.json");
}


Expand All @@ -61,4 +74,5 @@ EditorConfig::~EditorConfig()

void EditorConfig::saveConfig()
{
saveConfigToFile(UserSavePath + "editor.json");
}
5 changes: 3 additions & 2 deletions OP2-Landlord/EditorConfig.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
#pragma once


#include <map>
#include <string>


struct EditorConfig
{
EditorConfig() = default;
Expand All @@ -15,4 +14,6 @@ struct EditorConfig

char Op2FilePath[1000]{ '\0' };
std::string UserSavePath;

std::map<std::string, std::string> Options;
};
186 changes: 186 additions & 0 deletions OP2-Landlord/FileIO.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#include "FileIo.h"

#if defined(_WIN32)
#define NOMINMAX
#include <windows.h>
#include <ShlObj_core.h>
#include <Shlwapi.h>
#undef NOMINMAX
#endif

#include <filesystem>
#include <locale>
#include <codecvt>
#include <sstream>
#include <stdexcept>


namespace
{
#if defined(_WIN32)
COMDLG_FILTERSPEC FileTypeFilter[] =
{
{ L"Outpost 2 Map", L"*.map"}
};


const std::string StringFromWString(const std::wstring& str)
{
const auto length = WideCharToMultiByte(CP_UTF8, 0, &str.at(0), (int)str.size(), nullptr, 0, nullptr, nullptr);

if (length <= 0)
{
throw std::runtime_error("WideCharToMultiByte() failed.");
}

std::string out(length, 0);
WideCharToMultiByte(CP_UTF8, 0, &str.at(0), (int)str.size(), &out.at(0), length, nullptr, nullptr);
return out;
}
#endif

};


FileIo::FileIo(SDL_Window& window) :
mWindow(window)
{
SDL_GetWindowWMInfo(&mWindow, &mWmInfo);

#if defined(WIN32)
wchar_t* path{ nullptr };
std::ignore = SHGetKnownFolderPath(FOLDERID_ComputerFolder, KF_FLAG_CREATE, nullptr, &path);
CoTaskMemFree(path);
#endif

std::wstring separatorWchar(1, std::filesystem::path::preferred_separator);
mPathSeparator = std::string(separatorWchar.begin(), separatorWchar.end());
}


bool FileIo::filePicked() const
{
return !mFileName.empty();
}


/**
* \return Returns true if a file name was selected, false otherwise.
*/
bool FileIo::pickSaveFile()
{
const auto filePicked = showFileDialog(FileOperation::Save);

if (filePicked)
{
extractFileName();
}

return filePicked;
}


/**
* \return Returns true if a file name was selected, false otherwise.
*/
bool FileIo::pickOpenFile()
{
const auto filePicked = showFileDialog(FileOperation::Open);

if (filePicked)
{
extractFileName();
}

return filePicked;
}

bool FileIo::pickFolder()
{
return showFileDialog(FileOperation::Open, true);
}


void FileIo::extractFileName()
{
std::size_t location = mFileName.find_last_of("/\\");
mFileName = mFileName.substr(location + 1);
}


/**
* There are a few assumptions here regarding the Win32 API's handling of the file
* pick interface -- creating items, setting parameters and options, etc. are all
* assumed to not fail. If they fail this will undoubtedly result in other issues.
*
* The checks are not present for the sake of brevity. If it's determined that the
* hresult checks are necessary they can be added as needed.
*
* \return Returns true if a file name has been picked. False otherwise.
*/
bool FileIo::showFileDialog(FileOperation operation, bool pickFolder)
{
#if defined(WIN32)
CLSID fileOperation = (operation == FileOperation::Open ? CLSID_FileOpenDialog : CLSID_FileSaveDialog);

IFileDialog* fileDialog{ nullptr };
if (!SUCCEEDED(CoCreateInstance(fileOperation, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&fileDialog))))
{
throw std::runtime_error("Unable to create file IO dialog");
}

IShellItem* defaultFolder{ nullptr };
SHCreateItemFromParsingName(std::wstring(mSavePath.begin(), mSavePath.end()).c_str(), nullptr, IID_PPV_ARGS(&defaultFolder));
fileDialog->SetDefaultFolder(defaultFolder);
defaultFolder->Release();

DWORD dwFlags{};
fileDialog->GetOptions(&dwFlags);

fileDialog->SetOptions(dwFlags | FOS_FORCEFILESYSTEM | (pickFolder ? FOS_PICKFOLDERS : 0x00));
fileDialog->SetFileTypes(ARRAYSIZE(FileTypeFilter), FileTypeFilter);
fileDialog->SetFileTypeIndex(1);
fileDialog->SetDefaultExtension(L"map");

if (operation == FileOperation::Save && !mFileName.empty())
{
fileDialog->SetFileName(std::wstring(mFileName.begin(), mFileName.end()).c_str());
}

if (!SUCCEEDED(fileDialog->Show(mWmInfo.info.win.window)))
{
fileDialog->Release();
return false;
}

IShellItem* item{ nullptr };
if (SUCCEEDED(fileDialog->GetResult(&item)))
{
PWSTR path{ nullptr };

if (!SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH, &path)))
{
throw std::runtime_error("Unable to get file name");
}

const auto pathStr = StringFromWString(path);
CoTaskMemFree(path);

if (pickFolder)
{
mFolderPath = pathStr;
}
else
{
mFileName = pathStr;
}

item->Release();
}

fileDialog->Release();
#endif

return true;
}

45 changes: 45 additions & 0 deletions OP2-Landlord/FileIO.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once


#include <SDL2/SDL.h>
#include <SDL2/SDL_syswm.h>
#include <string>


class FileIo
{
public:
FileIo() = delete;
FileIo(const FileIo&) = delete;
const FileIo& operator=(const FileIo&) = delete;

FileIo(SDL_Window& window);
~FileIo() = default;

const std::string& savePath() const { return mSavePath; }
const std::string& fileName() const { return mFileName; }
const std::string& fullPath() const { return mSavePath + "\\" + mFileName; }
const std::string& folderPath() const { return mFolderPath; }
const std::string& pathSeparator() const { return mPathSeparator; }

bool pickSaveFile();
bool pickOpenFile();

bool pickFolder();

bool filePicked() const;

private:
enum class FileOperation { Open, Save };

bool showFileDialog(FileOperation, bool pickFolder = false);
void extractFileName();

SDL_Window& mWindow;
SDL_SysWMinfo mWmInfo{};

std::string mSavePath;
std::string mFileName;
std::string mFolderPath;
std::string mPathSeparator;
};
2 changes: 2 additions & 0 deletions OP2-Landlord/OP2-Landlord.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,15 @@
<ClCompile Include="..\nas2d-core\NAS2D\StringUtils.cpp" />
<ClCompile Include="EditorConfig.cpp" />
<ClCompile Include="Events.cpp" />
<ClCompile Include="FileIo.cpp" />
<ClCompile Include="Graphics.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="Utility.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="EditorConfig.h" />
<ClInclude Include="Events.h" />
<ClInclude Include="FileIo.h" />
<ClInclude Include="Graphics.h" />
<ClInclude Include="Utility.h" />
</ItemGroup>
Expand Down
6 changes: 6 additions & 0 deletions OP2-Landlord/OP2-Landlord.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
<ClCompile Include="..\nas2d-core\NAS2D\StringUtils.cpp">
<Filter>NAS2D</Filter>
</ClCompile>
<ClCompile Include="FileIo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Graphics.h">
Expand All @@ -53,5 +56,8 @@
<ClInclude Include="Utility.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FileIo.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
Loading

0 comments on commit 84cd8d6

Please sign in to comment.