mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 10:08:18 +00:00
Merge #6752
6752: Ensure `ListDirectory/Info` entries are unique, support selecting storage location in demo browser and editor file browser r=def- a=Robyt3 Description in commits below. Screenshots/video: - Storage location selection for demos: ![screenshot_2023-06-18_17-25-23](https://github.com/ddnet/ddnet/assets/23437060/bcc94977-50ba-494b-894d-7bd8fea7f91e) - Storage location selection for maps: ![screenshot_2023-06-18_17-25-33](https://github.com/ddnet/ddnet/assets/23437060/2b8b7822-b4b9-409f-86eb-e44f5c072541) - Link to "themes" folder: ![screenshot_2023-06-18_17-26-02](https://github.com/ddnet/ddnet/assets/23437060/eba661a6-0bee-4977-ab95-35e400b5b291) - Video showing navigation between storages (`temp` is the save directory and `temp2`, `temp3` and `data` are other storages) https://github.com/ddnet/ddnet/assets/23437060/5736d212-3848-44c2-aa81-7f2da9b98008 Closes #5496. ## Checklist - [X] Tested the change ingame - [X] Provided screenshots if it is a visual change - [ ] Tested in combination with possibly related configuration options - [ ] Written a unit test (especially base/) or added coverage to integration test - [ ] Considered possible null pointers and out of bounds array indexing - [ ] Changed no physics that affect existing maps - [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional) Co-authored-by: Robert Müller <robytemueller@gmail.com>
This commit is contained in:
commit
06fe73619e
|
@ -1,11 +1,14 @@
|
||||||
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
||||||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||||
#include "linereader.h"
|
|
||||||
#include <base/math.h>
|
#include <base/math.h>
|
||||||
#include <base/system.h>
|
#include <base/system.h>
|
||||||
|
|
||||||
#include <engine/client/updater.h>
|
#include <engine/client/updater.h>
|
||||||
|
#include <engine/shared/linereader.h>
|
||||||
#include <engine/storage.h>
|
#include <engine/storage.h>
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
#ifdef CONF_PLATFORM_HAIKU
|
#ifdef CONF_PLATFORM_HAIKU
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#endif
|
#endif
|
||||||
|
@ -309,14 +312,38 @@ public:
|
||||||
m_aBinarydir[0] = '\0';
|
m_aBinarydir[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int NumPaths() const override
|
||||||
|
{
|
||||||
|
return m_NumPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SListDirectoryInfoUniqueCallbackData
|
||||||
|
{
|
||||||
|
FS_LISTDIR_CALLBACK_FILEINFO m_pfnDelegate;
|
||||||
|
void *m_pDelegateUser;
|
||||||
|
std::unordered_set<std::string> m_Seen;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ListDirectoryInfoUniqueCallback(const CFsFileInfo *pInfo, int IsDir, int Type, void *pUser)
|
||||||
|
{
|
||||||
|
SListDirectoryInfoUniqueCallbackData *pData = static_cast<SListDirectoryInfoUniqueCallbackData *>(pUser);
|
||||||
|
auto [_, InsertionTookPlace] = pData->m_Seen.emplace(pInfo->m_pName);
|
||||||
|
if(InsertionTookPlace)
|
||||||
|
return pData->m_pfnDelegate(pInfo, IsDir, Type, pData->m_pDelegateUser);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void ListDirectoryInfo(int Type, const char *pPath, FS_LISTDIR_CALLBACK_FILEINFO pfnCallback, void *pUser) override
|
void ListDirectoryInfo(int Type, const char *pPath, FS_LISTDIR_CALLBACK_FILEINFO pfnCallback, void *pUser) override
|
||||||
{
|
{
|
||||||
char aBuffer[IO_MAX_PATH_LENGTH];
|
char aBuffer[IO_MAX_PATH_LENGTH];
|
||||||
if(Type == TYPE_ALL)
|
if(Type == TYPE_ALL)
|
||||||
{
|
{
|
||||||
|
SListDirectoryInfoUniqueCallbackData Data;
|
||||||
|
Data.m_pfnDelegate = pfnCallback;
|
||||||
|
Data.m_pDelegateUser = pUser;
|
||||||
// list all available directories
|
// list all available directories
|
||||||
for(int i = TYPE_SAVE; i < m_NumPaths; ++i)
|
for(int i = TYPE_SAVE; i < m_NumPaths; ++i)
|
||||||
fs_listdir_fileinfo(GetPath(i, pPath, aBuffer, sizeof(aBuffer)), pfnCallback, i, pUser);
|
fs_listdir_fileinfo(GetPath(i, pPath, aBuffer, sizeof(aBuffer)), ListDirectoryInfoUniqueCallback, i, &Data);
|
||||||
}
|
}
|
||||||
else if(Type >= TYPE_SAVE && Type < m_NumPaths)
|
else if(Type >= TYPE_SAVE && Type < m_NumPaths)
|
||||||
{
|
{
|
||||||
|
@ -329,14 +356,33 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SListDirectoryUniqueCallbackData
|
||||||
|
{
|
||||||
|
FS_LISTDIR_CALLBACK m_pfnDelegate;
|
||||||
|
void *m_pDelegateUser;
|
||||||
|
std::unordered_set<std::string> m_Seen;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ListDirectoryUniqueCallback(const char *pName, int IsDir, int Type, void *pUser)
|
||||||
|
{
|
||||||
|
SListDirectoryUniqueCallbackData *pData = static_cast<SListDirectoryUniqueCallbackData *>(pUser);
|
||||||
|
auto [_, InsertionTookPlace] = pData->m_Seen.emplace(pName);
|
||||||
|
if(InsertionTookPlace)
|
||||||
|
return pData->m_pfnDelegate(pName, IsDir, Type, pData->m_pDelegateUser);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void ListDirectory(int Type, const char *pPath, FS_LISTDIR_CALLBACK pfnCallback, void *pUser) override
|
void ListDirectory(int Type, const char *pPath, FS_LISTDIR_CALLBACK pfnCallback, void *pUser) override
|
||||||
{
|
{
|
||||||
char aBuffer[IO_MAX_PATH_LENGTH];
|
char aBuffer[IO_MAX_PATH_LENGTH];
|
||||||
if(Type == TYPE_ALL)
|
if(Type == TYPE_ALL)
|
||||||
{
|
{
|
||||||
|
SListDirectoryUniqueCallbackData Data;
|
||||||
|
Data.m_pfnDelegate = pfnCallback;
|
||||||
|
Data.m_pDelegateUser = pUser;
|
||||||
// list all available directories
|
// list all available directories
|
||||||
for(int i = TYPE_SAVE; i < m_NumPaths; ++i)
|
for(int i = TYPE_SAVE; i < m_NumPaths; ++i)
|
||||||
fs_listdir(GetPath(i, pPath, aBuffer, sizeof(aBuffer)), pfnCallback, i, pUser);
|
fs_listdir(GetPath(i, pPath, aBuffer, sizeof(aBuffer)), ListDirectoryUniqueCallback, i, &Data);
|
||||||
}
|
}
|
||||||
else if(Type >= TYPE_SAVE && Type < m_NumPaths)
|
else if(Type >= TYPE_SAVE && Type < m_NumPaths)
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,6 +42,8 @@ public:
|
||||||
STORAGETYPE_CLIENT,
|
STORAGETYPE_CLIENT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
virtual int NumPaths() const = 0;
|
||||||
|
|
||||||
virtual void ListDirectory(int Type, const char *pPath, FS_LISTDIR_CALLBACK pfnCallback, void *pUser) = 0;
|
virtual void ListDirectory(int Type, const char *pPath, FS_LISTDIR_CALLBACK pfnCallback, void *pUser) = 0;
|
||||||
virtual void ListDirectoryInfo(int Type, const char *pPath, FS_LISTDIR_CALLBACK_FILEINFO pfnCallback, void *pUser) = 0;
|
virtual void ListDirectoryInfo(int Type, const char *pPath, FS_LISTDIR_CALLBACK_FILEINFO pfnCallback, void *pUser) = 0;
|
||||||
virtual IOHANDLE OpenFile(const char *pFilename, int Flags, int Type, char *pBuffer = nullptr, int BufferSize = 0) = 0;
|
virtual IOHANDLE OpenFile(const char *pFilename, int Flags, int Type, char *pBuffer = nullptr, int BufferSize = 0) = 0;
|
||||||
|
|
|
@ -70,6 +70,7 @@ CMenus::CMenus()
|
||||||
m_ShowStart = true;
|
m_ShowStart = true;
|
||||||
|
|
||||||
str_copy(m_aCurrentDemoFolder, "demos");
|
str_copy(m_aCurrentDemoFolder, "demos");
|
||||||
|
m_DemolistStorageType = IStorage::TYPE_ALL;
|
||||||
|
|
||||||
m_DemoPlayerState = DEMOPLAYER_NONE;
|
m_DemoPlayerState = DEMOPLAYER_NONE;
|
||||||
m_Dummy = false;
|
m_Dummy = false;
|
||||||
|
@ -1530,9 +1531,9 @@ int CMenus::Render()
|
||||||
}
|
}
|
||||||
else if(Storage()->RenameFile(aBufOld, aBufNew, m_vDemos[m_DemolistSelectedIndex].m_StorageType))
|
else if(Storage()->RenameFile(aBufOld, aBufNew, m_vDemos[m_DemolistSelectedIndex].m_StorageType))
|
||||||
{
|
{
|
||||||
str_copy(g_Config.m_UiDemoSelected, m_DemoRenameInput.GetString());
|
str_copy(m_aCurrentDemoSelectionName, m_DemoRenameInput.GetString());
|
||||||
if(str_endswith(g_Config.m_UiDemoSelected, ".demo"))
|
if(str_endswith(m_aCurrentDemoSelectionName, ".demo"))
|
||||||
g_Config.m_UiDemoSelected[str_length(g_Config.m_UiDemoSelected) - str_length(".demo")] = '\0';
|
m_aCurrentDemoSelectionName[str_length(m_aCurrentDemoSelectionName) - str_length(".demo")] = '\0';
|
||||||
DemolistPopulate();
|
DemolistPopulate();
|
||||||
DemolistOnUpdate(false);
|
DemolistOnUpdate(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -259,6 +259,7 @@ protected:
|
||||||
char m_aFilename[IO_MAX_PATH_LENGTH];
|
char m_aFilename[IO_MAX_PATH_LENGTH];
|
||||||
char m_aName[IO_MAX_PATH_LENGTH];
|
char m_aName[IO_MAX_PATH_LENGTH];
|
||||||
bool m_IsDir;
|
bool m_IsDir;
|
||||||
|
bool m_IsLink;
|
||||||
int m_StorageType;
|
int m_StorageType;
|
||||||
time_t m_Date;
|
time_t m_Date;
|
||||||
|
|
||||||
|
@ -318,6 +319,7 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
char m_aCurrentDemoFolder[IO_MAX_PATH_LENGTH];
|
char m_aCurrentDemoFolder[IO_MAX_PATH_LENGTH];
|
||||||
|
char m_aCurrentDemoSelectionName[IO_MAX_PATH_LENGTH];
|
||||||
CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoRenameInput;
|
CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoRenameInput;
|
||||||
CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoSliceInput;
|
CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoSliceInput;
|
||||||
CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoRenderInput;
|
CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoRenderInput;
|
||||||
|
@ -325,6 +327,7 @@ protected:
|
||||||
bool m_DemolistSelectedIsDir;
|
bool m_DemolistSelectedIsDir;
|
||||||
bool m_DemolistSelectedReveal = false;
|
bool m_DemolistSelectedReveal = false;
|
||||||
int m_DemolistStorageType;
|
int m_DemolistStorageType;
|
||||||
|
bool m_DemolistMultipleStorages = false;
|
||||||
int m_Speed = 4;
|
int m_Speed = 4;
|
||||||
|
|
||||||
std::chrono::nanoseconds m_DemoPopulateStartTime{0};
|
std::chrono::nanoseconds m_DemoPopulateStartTime{0};
|
||||||
|
|
|
@ -724,9 +724,9 @@ void CMenus::RenderDemoPlayerSliceSavePopup(CUIRect MainView)
|
||||||
{
|
{
|
||||||
char aPath[IO_MAX_PATH_LENGTH];
|
char aPath[IO_MAX_PATH_LENGTH];
|
||||||
str_format(aPath, sizeof(aPath), "%s/%s", m_aCurrentDemoFolder, m_DemoSliceInput.GetString());
|
str_format(aPath, sizeof(aPath), "%s/%s", m_aCurrentDemoFolder, m_DemoSliceInput.GetString());
|
||||||
str_copy(g_Config.m_UiDemoSelected, m_DemoSliceInput.GetString());
|
str_copy(m_aCurrentDemoSelectionName, m_DemoSliceInput.GetString());
|
||||||
if(str_endswith(g_Config.m_UiDemoSelected, ".demo"))
|
if(str_endswith(m_aCurrentDemoSelectionName, ".demo"))
|
||||||
g_Config.m_UiDemoSelected[str_length(g_Config.m_UiDemoSelected) - str_length(".demo")] = '\0';
|
m_aCurrentDemoSelectionName[str_length(m_aCurrentDemoSelectionName) - str_length(".demo")] = '\0';
|
||||||
m_DemoPlayerState = DEMOPLAYER_NONE;
|
m_DemoPlayerState = DEMOPLAYER_NONE;
|
||||||
Client()->DemoSlice(aPath, CMenus::DemoFilterChat, &s_RemoveChat);
|
Client()->DemoSlice(aPath, CMenus::DemoFilterChat, &s_RemoveChat);
|
||||||
DemolistPopulate();
|
DemolistPopulate();
|
||||||
|
@ -741,7 +741,9 @@ void CMenus::RenderDemoPlayerSliceSavePopup(CUIRect MainView)
|
||||||
int CMenus::DemolistFetchCallback(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser)
|
int CMenus::DemolistFetchCallback(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser)
|
||||||
{
|
{
|
||||||
CMenus *pSelf = (CMenus *)pUser;
|
CMenus *pSelf = (CMenus *)pUser;
|
||||||
if(str_comp(pInfo->m_pName, ".") == 0 || (str_comp(pInfo->m_pName, "..") == 0 && str_comp(pSelf->m_aCurrentDemoFolder, "demos") == 0) || (!IsDir && !str_endswith(pInfo->m_pName, ".demo")))
|
if(str_comp(pInfo->m_pName, ".") == 0 ||
|
||||||
|
(str_comp(pInfo->m_pName, "..") == 0 && (pSelf->m_aCurrentDemoFolder[0] == '\0' || (!pSelf->m_DemolistMultipleStorages && str_comp(pSelf->m_aCurrentDemoFolder, "demos") == 0))) ||
|
||||||
|
(!IsDir && !str_endswith(pInfo->m_pName, ".demo")))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -762,6 +764,7 @@ int CMenus::DemolistFetchCallback(const CFsFileInfo *pInfo, int IsDir, int Stora
|
||||||
Item.m_Date = pInfo->m_TimeModified;
|
Item.m_Date = pInfo->m_TimeModified;
|
||||||
}
|
}
|
||||||
Item.m_IsDir = IsDir != 0;
|
Item.m_IsDir = IsDir != 0;
|
||||||
|
Item.m_IsLink = false;
|
||||||
Item.m_StorageType = StorageType;
|
Item.m_StorageType = StorageType;
|
||||||
pSelf->m_vDemos.push_back(Item);
|
pSelf->m_vDemos.push_back(Item);
|
||||||
|
|
||||||
|
@ -776,21 +779,66 @@ int CMenus::DemolistFetchCallback(const CFsFileInfo *pInfo, int IsDir, int Stora
|
||||||
void CMenus::DemolistPopulate()
|
void CMenus::DemolistPopulate()
|
||||||
{
|
{
|
||||||
m_vDemos.clear();
|
m_vDemos.clear();
|
||||||
if(!str_comp(m_aCurrentDemoFolder, "demos"))
|
|
||||||
m_DemolistStorageType = IStorage::TYPE_ALL;
|
|
||||||
m_DemoPopulateStartTime = time_get_nanoseconds();
|
|
||||||
Storage()->ListDirectoryInfo(m_DemolistStorageType, m_aCurrentDemoFolder, DemolistFetchCallback, this);
|
|
||||||
|
|
||||||
if(g_Config.m_BrDemoFetchInfo)
|
int NumStoragesWithDemos = 0;
|
||||||
FetchAllHeaders();
|
for(int StorageType = IStorage::TYPE_SAVE; StorageType < Storage()->NumPaths(); ++StorageType)
|
||||||
|
{
|
||||||
|
if(Storage()->FolderExists("demos", StorageType))
|
||||||
|
{
|
||||||
|
NumStoragesWithDemos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_DemolistMultipleStorages = NumStoragesWithDemos > 1;
|
||||||
|
|
||||||
std::stable_sort(m_vDemos.begin(), m_vDemos.end());
|
if(m_aCurrentDemoFolder[0] == '\0')
|
||||||
|
{
|
||||||
|
{
|
||||||
|
CDemoItem Item;
|
||||||
|
str_copy(Item.m_aFilename, "demos");
|
||||||
|
str_copy(Item.m_aName, Localize("All combined"));
|
||||||
|
Item.m_InfosLoaded = false;
|
||||||
|
Item.m_Valid = false;
|
||||||
|
Item.m_Date = 0;
|
||||||
|
Item.m_IsDir = true;
|
||||||
|
Item.m_IsLink = true;
|
||||||
|
Item.m_StorageType = IStorage::TYPE_ALL;
|
||||||
|
m_vDemos.push_back(Item);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int StorageType = IStorage::TYPE_SAVE; StorageType < Storage()->NumPaths(); ++StorageType)
|
||||||
|
{
|
||||||
|
if(Storage()->FolderExists("demos", StorageType))
|
||||||
|
{
|
||||||
|
CDemoItem Item;
|
||||||
|
str_copy(Item.m_aFilename, "demos");
|
||||||
|
Storage()->GetCompletePath(StorageType, "demos", Item.m_aName, sizeof(Item.m_aName));
|
||||||
|
str_append(Item.m_aName, "/", sizeof(Item.m_aName));
|
||||||
|
Item.m_InfosLoaded = false;
|
||||||
|
Item.m_Valid = false;
|
||||||
|
Item.m_Date = 0;
|
||||||
|
Item.m_IsDir = true;
|
||||||
|
Item.m_IsLink = true;
|
||||||
|
Item.m_StorageType = StorageType;
|
||||||
|
m_vDemos.push_back(Item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_DemoPopulateStartTime = time_get_nanoseconds();
|
||||||
|
Storage()->ListDirectoryInfo(m_DemolistStorageType, m_aCurrentDemoFolder, DemolistFetchCallback, this);
|
||||||
|
|
||||||
|
if(g_Config.m_BrDemoFetchInfo)
|
||||||
|
FetchAllHeaders();
|
||||||
|
|
||||||
|
std::stable_sort(m_vDemos.begin(), m_vDemos.end());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMenus::DemolistOnUpdate(bool Reset)
|
void CMenus::DemolistOnUpdate(bool Reset)
|
||||||
{
|
{
|
||||||
if(Reset)
|
if(Reset)
|
||||||
g_Config.m_UiDemoSelected[0] = '\0';
|
m_aCurrentDemoSelectionName[0] = '\0';
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool Found = false;
|
bool Found = false;
|
||||||
|
@ -800,7 +848,7 @@ void CMenus::DemolistOnUpdate(bool Reset)
|
||||||
{
|
{
|
||||||
SelectedIndex++;
|
SelectedIndex++;
|
||||||
|
|
||||||
if(str_comp(g_Config.m_UiDemoSelected, Item.m_aName) == 0)
|
if(str_comp(m_aCurrentDemoSelectionName, Item.m_aName) == 0)
|
||||||
{
|
{
|
||||||
Found = true;
|
Found = true;
|
||||||
break;
|
break;
|
||||||
|
@ -813,7 +861,6 @@ void CMenus::DemolistOnUpdate(bool Reset)
|
||||||
|
|
||||||
m_DemolistSelectedIndex = Reset ? !m_vDemos.empty() ? 0 : -1 :
|
m_DemolistSelectedIndex = Reset ? !m_vDemos.empty() ? 0 : -1 :
|
||||||
m_DemolistSelectedIndex >= (int)m_vDemos.size() ? m_vDemos.size() - 1 : m_DemolistSelectedIndex;
|
m_DemolistSelectedIndex >= (int)m_vDemos.size() ? m_vDemos.size() - 1 : m_DemolistSelectedIndex;
|
||||||
m_DemolistSelectedIsDir = m_DemolistSelectedIndex < 0 ? false : m_vDemos[m_DemolistSelectedIndex].m_IsDir;
|
|
||||||
m_DemolistSelectedReveal = true;
|
m_DemolistSelectedReveal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -854,7 +901,9 @@ void CMenus::RenderDemoList(CUIRect MainView)
|
||||||
CDemoItem &Item = m_vDemos[m_DemolistSelectedIndex];
|
CDemoItem &Item = m_vDemos[m_DemolistSelectedIndex];
|
||||||
if(str_comp(Item.m_aFilename, "..") == 0)
|
if(str_comp(Item.m_aFilename, "..") == 0)
|
||||||
str_copy(aFooterLabel, Localize("Parent Folder"));
|
str_copy(aFooterLabel, Localize("Parent Folder"));
|
||||||
else if(m_DemolistSelectedIsDir)
|
else if(m_vDemos[m_DemolistSelectedIndex].m_IsLink)
|
||||||
|
str_copy(aFooterLabel, Localize("Folder Link"));
|
||||||
|
else if(m_vDemos[m_DemolistSelectedIndex].m_IsDir)
|
||||||
str_copy(aFooterLabel, Localize("Folder"));
|
str_copy(aFooterLabel, Localize("Folder"));
|
||||||
else if(!FetchHeader(Item))
|
else if(!FetchHeader(Item))
|
||||||
str_copy(aFooterLabel, Localize("Invalid Demo"));
|
str_copy(aFooterLabel, Localize("Invalid Demo"));
|
||||||
|
@ -1090,7 +1139,7 @@ void CMenus::RenderDemoList(CUIRect MainView)
|
||||||
FileIcon.x += 2.0f;
|
FileIcon.x += 2.0f;
|
||||||
|
|
||||||
const char *pIconType;
|
const char *pIconType;
|
||||||
if(str_comp(Item.m_aFilename, "..") == 0)
|
if(Item.m_IsLink || str_comp(Item.m_aFilename, "..") == 0)
|
||||||
pIconType = FONT_ICON_FOLDER_TREE;
|
pIconType = FONT_ICON_FOLDER_TREE;
|
||||||
else if(Item.m_IsDir)
|
else if(Item.m_IsDir)
|
||||||
pIconType = FONT_ICON_FOLDER;
|
pIconType = FONT_ICON_FOLDER;
|
||||||
|
@ -1103,7 +1152,9 @@ void CMenus::RenderDemoList(CUIRect MainView)
|
||||||
|
|
||||||
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT));
|
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT));
|
||||||
TextRender()->TextColor(IconColor);
|
TextRender()->TextColor(IconColor);
|
||||||
|
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING);
|
||||||
UI()->DoLabel(&FileIcon, pIconType, 12.0f, TEXTALIGN_ML);
|
UI()->DoLabel(&FileIcon, pIconType, 12.0f, TEXTALIGN_ML);
|
||||||
|
TextRender()->SetRenderFlags(0);
|
||||||
TextRender()->TextColor(TextRender()->DefaultTextColor());
|
TextRender()->TextColor(TextRender()->DefaultTextColor());
|
||||||
TextRender()->SetCurFont(nullptr);
|
TextRender()->SetCurFont(nullptr);
|
||||||
|
|
||||||
|
@ -1155,7 +1206,7 @@ void CMenus::RenderDemoList(CUIRect MainView)
|
||||||
{
|
{
|
||||||
m_DemolistSelectedIndex = NewSelected;
|
m_DemolistSelectedIndex = NewSelected;
|
||||||
if(m_DemolistSelectedIndex >= 0)
|
if(m_DemolistSelectedIndex >= 0)
|
||||||
str_copy(g_Config.m_UiDemoSelected, m_vDemos[m_DemolistSelectedIndex].m_aName);
|
str_copy(m_aCurrentDemoSelectionName, m_vDemos[m_DemolistSelectedIndex].m_aName);
|
||||||
DemolistOnUpdate(false);
|
DemolistOnUpdate(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1174,22 +1225,41 @@ void CMenus::RenderDemoList(CUIRect MainView)
|
||||||
}
|
}
|
||||||
|
|
||||||
static CButtonContainer s_PlayButton;
|
static CButtonContainer s_PlayButton;
|
||||||
if(DoButton_Menu(&s_PlayButton, m_DemolistSelectedIsDir ? Localize("Open") : Localize("Play", "Demo browser"), 0, &PlayRect) || s_ListBox.WasItemActivated() || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER) || (Input()->KeyPress(KEY_P) && m_pClient->m_GameConsole.IsClosed()))
|
if(DoButton_Menu(&s_PlayButton, (m_DemolistSelectedIndex >= 0 && m_vDemos[m_DemolistSelectedIndex].m_IsDir) ? Localize("Open") : Localize("Play", "Demo browser"), 0, &PlayRect) || s_ListBox.WasItemActivated() || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER) || (Input()->KeyPress(KEY_P) && m_pClient->m_GameConsole.IsClosed()))
|
||||||
{
|
{
|
||||||
if(m_DemolistSelectedIndex >= 0)
|
if(m_DemolistSelectedIndex >= 0)
|
||||||
{
|
{
|
||||||
if(m_DemolistSelectedIsDir) // folder
|
if(m_vDemos[m_DemolistSelectedIndex].m_IsDir) // folder
|
||||||
{
|
{
|
||||||
if(str_comp(m_vDemos[m_DemolistSelectedIndex].m_aFilename, "..") == 0) // parent folder
|
const bool ParentFolder = str_comp(m_vDemos[m_DemolistSelectedIndex].m_aFilename, "..") == 0;
|
||||||
fs_parent_dir(m_aCurrentDemoFolder);
|
if(ParentFolder) // parent folder
|
||||||
|
{
|
||||||
|
str_copy(m_aCurrentDemoSelectionName, fs_filename(m_aCurrentDemoFolder));
|
||||||
|
str_append(m_aCurrentDemoSelectionName, "/");
|
||||||
|
if(fs_parent_dir(m_aCurrentDemoFolder))
|
||||||
|
{
|
||||||
|
m_aCurrentDemoFolder[0] = '\0';
|
||||||
|
if(m_DemolistStorageType == IStorage::TYPE_ALL)
|
||||||
|
{
|
||||||
|
m_aCurrentDemoSelectionName[0] = '\0'; // will select first list item
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Storage()->GetCompletePath(m_DemolistStorageType, "demos", m_aCurrentDemoSelectionName, sizeof(m_aCurrentDemoSelectionName));
|
||||||
|
str_append(m_aCurrentDemoSelectionName, "/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else // sub folder
|
else // sub folder
|
||||||
{
|
{
|
||||||
str_append(m_aCurrentDemoFolder, "/");
|
if(m_aCurrentDemoFolder[0] != '\0')
|
||||||
|
str_append(m_aCurrentDemoFolder, "/");
|
||||||
|
else
|
||||||
|
m_DemolistStorageType = m_vDemos[m_DemolistSelectedIndex].m_StorageType;
|
||||||
str_append(m_aCurrentDemoFolder, m_vDemos[m_DemolistSelectedIndex].m_aFilename);
|
str_append(m_aCurrentDemoFolder, m_vDemos[m_DemolistSelectedIndex].m_aFilename);
|
||||||
m_DemolistStorageType = m_vDemos[m_DemolistSelectedIndex].m_StorageType;
|
|
||||||
}
|
}
|
||||||
DemolistPopulate();
|
DemolistPopulate();
|
||||||
DemolistOnUpdate(true);
|
DemolistOnUpdate(!ParentFolder);
|
||||||
}
|
}
|
||||||
else // file
|
else // file
|
||||||
{
|
{
|
||||||
|
@ -1211,8 +1281,7 @@ void CMenus::RenderDemoList(CUIRect MainView)
|
||||||
if(DoButton_Menu(&s_DirectoryButtonID, Localize("Demos directory"), 0, &DirectoryButton))
|
if(DoButton_Menu(&s_DirectoryButtonID, Localize("Demos directory"), 0, &DirectoryButton))
|
||||||
{
|
{
|
||||||
char aBuf[IO_MAX_PATH_LENGTH];
|
char aBuf[IO_MAX_PATH_LENGTH];
|
||||||
Storage()->GetCompletePath(IStorage::TYPE_SAVE, "demos", aBuf, sizeof(aBuf));
|
Storage()->GetCompletePath(m_DemolistSelectedIndex >= 0 ? m_vDemos[m_DemolistSelectedIndex].m_StorageType : IStorage::TYPE_SAVE, m_aCurrentDemoFolder[0] == '\0' ? "demos" : m_aCurrentDemoFolder, aBuf, sizeof(aBuf));
|
||||||
Storage()->CreateFolder("demos", IStorage::TYPE_SAVE);
|
|
||||||
if(!open_file(aBuf))
|
if(!open_file(aBuf))
|
||||||
{
|
{
|
||||||
dbg_msg("menus", "couldn't open file '%s'", aBuf);
|
dbg_msg("menus", "couldn't open file '%s'", aBuf);
|
||||||
|
@ -1220,43 +1289,34 @@ void CMenus::RenderDemoList(CUIRect MainView)
|
||||||
}
|
}
|
||||||
GameClient()->m_Tooltips.DoToolTip(&s_DirectoryButtonID, &DirectoryButton, Localize("Open the directory that contains the demo files"));
|
GameClient()->m_Tooltips.DoToolTip(&s_DirectoryButtonID, &DirectoryButton, Localize("Open the directory that contains the demo files"));
|
||||||
|
|
||||||
if(!m_DemolistSelectedIsDir)
|
if(m_DemolistSelectedIndex >= 0 && !m_vDemos[m_DemolistSelectedIndex].m_IsDir)
|
||||||
{
|
{
|
||||||
static CButtonContainer s_DeleteButton;
|
static CButtonContainer s_DeleteButton;
|
||||||
if(DoButton_Menu(&s_DeleteButton, Localize("Delete"), 0, &DeleteRect) || UI()->ConsumeHotkey(CUI::HOTKEY_DELETE) || (Input()->KeyPress(KEY_D) && m_pClient->m_GameConsole.IsClosed()))
|
if(DoButton_Menu(&s_DeleteButton, Localize("Delete"), 0, &DeleteRect) || UI()->ConsumeHotkey(CUI::HOTKEY_DELETE) || (Input()->KeyPress(KEY_D) && m_pClient->m_GameConsole.IsClosed()))
|
||||||
{
|
{
|
||||||
if(m_DemolistSelectedIndex >= 0)
|
char aBuf[128 + IO_MAX_PATH_LENGTH];
|
||||||
{
|
str_format(aBuf, sizeof(aBuf), Localize("Are you sure that you want to delete the demo '%s'?"), m_vDemos[m_DemolistSelectedIndex].m_aFilename);
|
||||||
char aBuf[128 + IO_MAX_PATH_LENGTH];
|
PopupConfirm(Localize("Delete demo"), aBuf, Localize("Yes"), Localize("No"), &CMenus::PopupConfirmDeleteDemo);
|
||||||
str_format(aBuf, sizeof(aBuf), Localize("Are you sure that you want to delete the demo '%s'?"), m_vDemos[m_DemolistSelectedIndex].m_aFilename);
|
return;
|
||||||
PopupConfirm(Localize("Delete demo"), aBuf, Localize("Yes"), Localize("No"), &CMenus::PopupConfirmDeleteDemo);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static CButtonContainer s_RenameButton;
|
static CButtonContainer s_RenameButton;
|
||||||
if(DoButton_Menu(&s_RenameButton, Localize("Rename"), 0, &RenameRect))
|
if(DoButton_Menu(&s_RenameButton, Localize("Rename"), 0, &RenameRect))
|
||||||
{
|
{
|
||||||
if(m_DemolistSelectedIndex >= 0)
|
m_Popup = POPUP_RENAME_DEMO;
|
||||||
{
|
m_DemoRenameInput.Set(m_vDemos[m_DemolistSelectedIndex].m_aFilename);
|
||||||
m_Popup = POPUP_RENAME_DEMO;
|
UI()->SetActiveItem(&m_DemoRenameInput);
|
||||||
m_DemoRenameInput.Set(m_vDemos[m_DemolistSelectedIndex].m_aFilename);
|
return;
|
||||||
UI()->SetActiveItem(&m_DemoRenameInput);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONF_VIDEORECORDER)
|
#if defined(CONF_VIDEORECORDER)
|
||||||
static CButtonContainer s_RenderButton;
|
static CButtonContainer s_RenderButton;
|
||||||
if(DoButton_Menu(&s_RenderButton, Localize("Render"), 0, &RenderRect) || (Input()->KeyPress(KEY_R) && m_pClient->m_GameConsole.IsClosed()))
|
if(DoButton_Menu(&s_RenderButton, Localize("Render"), 0, &RenderRect) || (Input()->KeyPress(KEY_R) && m_pClient->m_GameConsole.IsClosed()))
|
||||||
{
|
{
|
||||||
if(m_DemolistSelectedIndex >= 0)
|
m_Popup = POPUP_RENDER_DEMO;
|
||||||
{
|
m_DemoRenderInput.Set(m_vDemos[m_DemolistSelectedIndex].m_aFilename);
|
||||||
m_Popup = POPUP_RENDER_DEMO;
|
UI()->SetActiveItem(&m_DemoRenderInput);
|
||||||
m_DemoRenderInput.Set(m_vDemos[m_DemolistSelectedIndex].m_aFilename);
|
return;
|
||||||
UI()->SetActiveItem(&m_DemoRenderInput);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -823,7 +823,7 @@ bool CEditor::CallbackOpenMap(const char *pFileName, int StorageType, void *pUse
|
||||||
CEditor *pEditor = (CEditor *)pUser;
|
CEditor *pEditor = (CEditor *)pUser;
|
||||||
if(pEditor->Load(pFileName, StorageType))
|
if(pEditor->Load(pFileName, StorageType))
|
||||||
{
|
{
|
||||||
pEditor->m_ValidSaveFilename = StorageType == IStorage::TYPE_SAVE && pEditor->m_pFileDialogPath == pEditor->m_aFileDialogCurrentFolder;
|
pEditor->m_ValidSaveFilename = StorageType == IStorage::TYPE_SAVE && (pEditor->m_pFileDialogPath == pEditor->m_aFileDialogCurrentFolder || (pEditor->m_pFileDialogPath == pEditor->m_aFileDialogCurrentLink && str_comp(pEditor->m_aFileDialogCurrentLink, "themes") == 0));
|
||||||
pEditor->m_Dialog = DIALOG_NONE;
|
pEditor->m_Dialog = DIALOG_NONE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -852,6 +852,8 @@ bool CEditor::CallbackAppendMap(const char *pFileName, int StorageType, void *pU
|
||||||
|
|
||||||
bool CEditor::CallbackSaveMap(const char *pFileName, int StorageType, void *pUser)
|
bool CEditor::CallbackSaveMap(const char *pFileName, int StorageType, void *pUser)
|
||||||
{
|
{
|
||||||
|
dbg_assert(StorageType == IStorage::TYPE_SAVE, "Saving only allowed for IStorage::TYPE_SAVE");
|
||||||
|
|
||||||
CEditor *pEditor = static_cast<CEditor *>(pUser);
|
CEditor *pEditor = static_cast<CEditor *>(pUser);
|
||||||
char aBuf[IO_MAX_PATH_LENGTH];
|
char aBuf[IO_MAX_PATH_LENGTH];
|
||||||
// add map extension
|
// add map extension
|
||||||
|
@ -865,7 +867,7 @@ bool CEditor::CallbackSaveMap(const char *pFileName, int StorageType, void *pUse
|
||||||
if(pEditor->Save(pFileName))
|
if(pEditor->Save(pFileName))
|
||||||
{
|
{
|
||||||
str_copy(pEditor->m_aFileName, pFileName);
|
str_copy(pEditor->m_aFileName, pFileName);
|
||||||
pEditor->m_ValidSaveFilename = StorageType == IStorage::TYPE_SAVE && pEditor->m_pFileDialogPath == pEditor->m_aFileDialogCurrentFolder;
|
pEditor->m_ValidSaveFilename = true;
|
||||||
pEditor->m_Map.m_Modified = false;
|
pEditor->m_Map.m_Modified = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -888,6 +890,8 @@ bool CEditor::CallbackSaveMap(const char *pFileName, int StorageType, void *pUse
|
||||||
|
|
||||||
bool CEditor::CallbackSaveCopyMap(const char *pFileName, int StorageType, void *pUser)
|
bool CEditor::CallbackSaveCopyMap(const char *pFileName, int StorageType, void *pUser)
|
||||||
{
|
{
|
||||||
|
dbg_assert(StorageType == IStorage::TYPE_SAVE, "Saving only allowed for IStorage::TYPE_SAVE");
|
||||||
|
|
||||||
CEditor *pEditor = static_cast<CEditor *>(pUser);
|
CEditor *pEditor = static_cast<CEditor *>(pUser);
|
||||||
char aBuf[IO_MAX_PATH_LENGTH];
|
char aBuf[IO_MAX_PATH_LENGTH];
|
||||||
// add map extension
|
// add map extension
|
||||||
|
@ -4542,7 +4546,7 @@ static int EditorListdirCallback(const CFsFileInfo *pInfo, int IsDir, int Storag
|
||||||
{
|
{
|
||||||
CEditor *pEditor = (CEditor *)pUser;
|
CEditor *pEditor = (CEditor *)pUser;
|
||||||
if((pInfo->m_pName[0] == '.' && (pInfo->m_pName[1] == 0 ||
|
if((pInfo->m_pName[0] == '.' && (pInfo->m_pName[1] == 0 ||
|
||||||
(pInfo->m_pName[1] == '.' && pInfo->m_pName[2] == 0 && (!str_comp(pEditor->m_pFileDialogPath, "maps") || !str_comp(pEditor->m_pFileDialogPath, "mapres"))))) ||
|
(pInfo->m_pName[1] == '.' && pInfo->m_pName[2] == 0 && (pEditor->m_FileDialogShowingRoot || (!pEditor->m_FileDialogMultipleStorages && (!str_comp(pEditor->m_pFileDialogPath, "maps") || !str_comp(pEditor->m_pFileDialogPath, "mapres"))))))) ||
|
||||||
(!IsDir && ((pEditor->m_FileDialogFileType == CEditor::FILETYPE_MAP && !str_endswith(pInfo->m_pName, ".map")) ||
|
(!IsDir && ((pEditor->m_FileDialogFileType == CEditor::FILETYPE_MAP && !str_endswith(pInfo->m_pName, ".map")) ||
|
||||||
(pEditor->m_FileDialogFileType == CEditor::FILETYPE_IMG && !str_endswith(pInfo->m_pName, ".png")) ||
|
(pEditor->m_FileDialogFileType == CEditor::FILETYPE_IMG && !str_endswith(pInfo->m_pName, ".png")) ||
|
||||||
(pEditor->m_FileDialogFileType == CEditor::FILETYPE_SOUND && !str_endswith(pInfo->m_pName, ".opus")))))
|
(pEditor->m_FileDialogFileType == CEditor::FILETYPE_SOUND && !str_endswith(pInfo->m_pName, ".opus")))))
|
||||||
|
@ -4614,54 +4618,57 @@ void CEditor::RenderFileDialog()
|
||||||
if(m_FileDialogFileType == CEditor::FILETYPE_IMG || m_FileDialogFileType == CEditor::FILETYPE_SOUND)
|
if(m_FileDialogFileType == CEditor::FILETYPE_IMG || m_FileDialogFileType == CEditor::FILETYPE_SOUND)
|
||||||
View.VSplitMid(&View, &Preview);
|
View.VSplitMid(&View, &Preview);
|
||||||
|
|
||||||
// title
|
// title bar
|
||||||
CUIRect ButtonTimeModified, ButtonFileName;
|
if(!m_FileDialogShowingRoot)
|
||||||
Title.VSplitRight(10.0f, &Title, nullptr);
|
|
||||||
Title.VSplitRight(90.0f, &Title, &ButtonTimeModified);
|
|
||||||
Title.VSplitRight(10.0f, &Title, nullptr);
|
|
||||||
Title.VSplitRight(90.0f, &Title, &ButtonFileName);
|
|
||||||
Title.VSplitRight(10.0f, &Title, nullptr);
|
|
||||||
|
|
||||||
const char *aSortIndicator[3] = {"▼", "", "▲"};
|
|
||||||
|
|
||||||
static int s_ButtonTimeModified = 0;
|
|
||||||
char aBufLabelButtonTimeModified[64];
|
|
||||||
str_format(aBufLabelButtonTimeModified, sizeof(aBufLabelButtonTimeModified), "Time modified %s", aSortIndicator[m_SortByTimeModified + 1]);
|
|
||||||
if(DoButton_Editor(&s_ButtonTimeModified, aBufLabelButtonTimeModified, 0, &ButtonTimeModified, 0, "Sort by time modified"))
|
|
||||||
{
|
{
|
||||||
if(m_SortByTimeModified == 1)
|
CUIRect ButtonTimeModified, ButtonFileName;
|
||||||
|
Title.VSplitRight(10.0f, &Title, nullptr);
|
||||||
|
Title.VSplitRight(90.0f, &Title, &ButtonTimeModified);
|
||||||
|
Title.VSplitRight(10.0f, &Title, nullptr);
|
||||||
|
Title.VSplitRight(90.0f, &Title, &ButtonFileName);
|
||||||
|
Title.VSplitRight(10.0f, &Title, nullptr);
|
||||||
|
|
||||||
|
const char *aSortIndicator[3] = {"▼", "", "▲"};
|
||||||
|
|
||||||
|
static int s_ButtonTimeModified = 0;
|
||||||
|
char aBufLabelButtonTimeModified[64];
|
||||||
|
str_format(aBufLabelButtonTimeModified, sizeof(aBufLabelButtonTimeModified), "Time modified %s", aSortIndicator[m_SortByTimeModified + 1]);
|
||||||
|
if(DoButton_Editor(&s_ButtonTimeModified, aBufLabelButtonTimeModified, 0, &ButtonTimeModified, 0, "Sort by time modified"))
|
||||||
{
|
{
|
||||||
m_SortByTimeModified = -1;
|
if(m_SortByTimeModified == 1)
|
||||||
}
|
{
|
||||||
else if(m_SortByTimeModified == -1)
|
m_SortByTimeModified = -1;
|
||||||
{
|
}
|
||||||
m_SortByTimeModified = 0;
|
else if(m_SortByTimeModified == -1)
|
||||||
}
|
{
|
||||||
else
|
m_SortByTimeModified = 0;
|
||||||
{
|
}
|
||||||
m_SortByTimeModified = 1;
|
else
|
||||||
|
{
|
||||||
|
m_SortByTimeModified = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshFilteredFileList();
|
||||||
}
|
}
|
||||||
|
|
||||||
RefreshFilteredFileList();
|
static int s_ButtonFileName = 0;
|
||||||
}
|
char aBufLabelButtonFilename[64];
|
||||||
|
str_format(aBufLabelButtonFilename, sizeof(aBufLabelButtonFilename), "Filename %s", aSortIndicator[m_SortByFilename + 1]);
|
||||||
static int s_ButtonFileName = 0;
|
if(DoButton_Editor(&s_ButtonFileName, aBufLabelButtonFilename, 0, &ButtonFileName, 0, "Sort by file name"))
|
||||||
char aBufLabelButtonFilename[64];
|
|
||||||
str_format(aBufLabelButtonFilename, sizeof(aBufLabelButtonFilename), "Filename %s", aSortIndicator[m_SortByFilename + 1]);
|
|
||||||
if(DoButton_Editor(&s_ButtonFileName, aBufLabelButtonFilename, 0, &ButtonFileName, 0, "Sort by file name"))
|
|
||||||
{
|
|
||||||
if(m_SortByFilename == 1)
|
|
||||||
{
|
{
|
||||||
m_SortByFilename = -1;
|
if(m_SortByFilename == 1)
|
||||||
m_SortByTimeModified = 0;
|
{
|
||||||
}
|
m_SortByFilename = -1;
|
||||||
else
|
m_SortByTimeModified = 0;
|
||||||
{
|
}
|
||||||
m_SortByFilename = 1;
|
else
|
||||||
m_SortByTimeModified = 0;
|
{
|
||||||
}
|
m_SortByFilename = 1;
|
||||||
|
m_SortByTimeModified = 0;
|
||||||
|
}
|
||||||
|
|
||||||
RefreshFilteredFileList();
|
RefreshFilteredFileList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Title.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 4.0f);
|
Title.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 4.0f);
|
||||||
|
@ -4669,13 +4676,13 @@ void CEditor::RenderFileDialog()
|
||||||
UI()->DoLabel(&Title, m_pFileDialogTitle, 12.0f, TEXTALIGN_ML);
|
UI()->DoLabel(&Title, m_pFileDialogTitle, 12.0f, TEXTALIGN_ML);
|
||||||
|
|
||||||
// pathbox
|
// pathbox
|
||||||
char aPath[IO_MAX_PATH_LENGTH], aBuf[128 + IO_MAX_PATH_LENGTH];
|
if(m_FilesSelectedIndex >= 0 && m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType >= IStorage::TYPE_SAVE)
|
||||||
if(m_FilesSelectedIndex != -1)
|
{
|
||||||
|
char aPath[IO_MAX_PATH_LENGTH], aBuf[128 + IO_MAX_PATH_LENGTH];
|
||||||
Storage()->GetCompletePath(m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType, m_pFileDialogPath, aPath, sizeof(aPath));
|
Storage()->GetCompletePath(m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType, m_pFileDialogPath, aPath, sizeof(aPath));
|
||||||
else
|
str_format(aBuf, sizeof(aBuf), "Current path: %s", aPath);
|
||||||
aPath[0] = 0;
|
UI()->DoLabel(&PathBox, aBuf, 10.0f, TEXTALIGN_ML);
|
||||||
str_format(aBuf, sizeof(aBuf), "Current path: %s", aPath);
|
}
|
||||||
UI()->DoLabel(&PathBox, aBuf, 10.0f, TEXTALIGN_ML);
|
|
||||||
|
|
||||||
const auto &&UpdateFileNameInput = [this]() {
|
const auto &&UpdateFileNameInput = [this]() {
|
||||||
if(m_FilesSelectedIndex >= 0 && !m_vpFilteredFileList[m_FilesSelectedIndex]->m_IsDir)
|
if(m_FilesSelectedIndex >= 0 && !m_vpFilteredFileList[m_FilesSelectedIndex]->m_IsDir)
|
||||||
|
@ -4898,14 +4905,16 @@ void CEditor::RenderFileDialog()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(str_comp(m_vpFilteredFileList[i]->m_aFilename, "..") == 0)
|
if(m_vpFilteredFileList[i]->m_IsLink || str_comp(m_vpFilteredFileList[i]->m_aFilename, "..") == 0)
|
||||||
pIconType = FONT_ICON_FOLDER_TREE;
|
pIconType = FONT_ICON_FOLDER_TREE;
|
||||||
else
|
else
|
||||||
pIconType = FONT_ICON_FOLDER;
|
pIconType = FONT_ICON_FOLDER;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT));
|
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT));
|
||||||
|
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING);
|
||||||
UI()->DoLabel(&FileIcon, pIconType, 12.0f, TEXTALIGN_ML);
|
UI()->DoLabel(&FileIcon, pIconType, 12.0f, TEXTALIGN_ML);
|
||||||
|
TextRender()->SetRenderFlags(0);
|
||||||
TextRender()->SetCurFont(nullptr);
|
TextRender()->SetCurFont(nullptr);
|
||||||
|
|
||||||
SLabelProperties Props;
|
SLabelProperties Props;
|
||||||
|
@ -4945,16 +4954,39 @@ void CEditor::RenderFileDialog()
|
||||||
|
|
||||||
CUIRect Button;
|
CUIRect Button;
|
||||||
ButtonBar.VSplitRight(50.0f, &ButtonBar, &Button);
|
ButtonBar.VSplitRight(50.0f, &ButtonBar, &Button);
|
||||||
bool IsDir = m_FilesSelectedIndex >= 0 && m_vpFilteredFileList[m_FilesSelectedIndex]->m_IsDir;
|
const bool IsDir = m_FilesSelectedIndex >= 0 && m_vpFilteredFileList[m_FilesSelectedIndex]->m_IsDir;
|
||||||
if(DoButton_Editor(&s_OkButton, IsDir ? "Open" : m_pFileDialogButtonText, 0, &Button, 0, nullptr) || s_ListBox.WasItemActivated())
|
if(DoButton_Editor(&s_OkButton, IsDir ? "Open" : m_pFileDialogButtonText, 0, &Button, 0, nullptr) || s_ListBox.WasItemActivated())
|
||||||
{
|
{
|
||||||
if(IsDir) // folder
|
if(IsDir) // folder
|
||||||
{
|
{
|
||||||
m_FileDialogFilterInput.Clear();
|
m_FileDialogFilterInput.Clear();
|
||||||
if(str_comp(m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename, "..") == 0) // parent folder
|
const bool ParentFolder = str_comp(m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename, "..") == 0;
|
||||||
|
if(ParentFolder) // parent folder
|
||||||
{
|
{
|
||||||
|
str_copy(m_aFilesSelectedName, fs_filename(m_pFileDialogPath));
|
||||||
|
str_append(m_aFilesSelectedName, "/");
|
||||||
if(fs_parent_dir(m_pFileDialogPath))
|
if(fs_parent_dir(m_pFileDialogPath))
|
||||||
m_pFileDialogPath = m_aFileDialogCurrentFolder; // leave the link
|
{
|
||||||
|
if(str_comp(m_pFileDialogPath, m_aFileDialogCurrentFolder) == 0)
|
||||||
|
{
|
||||||
|
m_FileDialogShowingRoot = true;
|
||||||
|
if(m_FileDialogStorageType == IStorage::TYPE_ALL)
|
||||||
|
{
|
||||||
|
m_aFilesSelectedName[0] = '\0'; // will select first list item
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Storage()->GetCompletePath(m_FileDialogStorageType, m_pFileDialogPath, m_aFilesSelectedName, sizeof(m_aFilesSelectedName));
|
||||||
|
str_append(m_aFilesSelectedName, "/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_pFileDialogPath = m_aFileDialogCurrentFolder; // leave the link
|
||||||
|
str_copy(m_aFilesSelectedName, m_aFileDialogCurrentLink);
|
||||||
|
str_append(m_aFilesSelectedName, "/");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else // sub folder
|
else // sub folder
|
||||||
{
|
{
|
||||||
|
@ -4969,28 +5001,26 @@ void CEditor::RenderFileDialog()
|
||||||
str_copy(aTemp, m_pFileDialogPath);
|
str_copy(aTemp, m_pFileDialogPath);
|
||||||
str_format(m_pFileDialogPath, IO_MAX_PATH_LENGTH, "%s/%s", aTemp, m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename);
|
str_format(m_pFileDialogPath, IO_MAX_PATH_LENGTH, "%s/%s", aTemp, m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename);
|
||||||
}
|
}
|
||||||
|
if(m_FileDialogShowingRoot)
|
||||||
|
m_FileDialogStorageType = m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType;
|
||||||
|
m_FileDialogShowingRoot = false;
|
||||||
}
|
}
|
||||||
FilelistPopulate(!str_comp(m_pFileDialogPath, "maps") || !str_comp(m_pFileDialogPath, "mapres") ? m_FileDialogStorageType :
|
FilelistPopulate(m_FileDialogStorageType, ParentFolder);
|
||||||
m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType);
|
|
||||||
UpdateFileNameInput();
|
UpdateFileNameInput();
|
||||||
}
|
}
|
||||||
else // file
|
else // file
|
||||||
{
|
{
|
||||||
|
const int StorageType = m_FilesSelectedIndex >= 0 ? m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType : m_FileDialogStorageType;
|
||||||
str_format(m_aFileSaveName, sizeof(m_aFileSaveName), "%s/%s", m_pFileDialogPath, m_FileDialogFileNameInput.GetString());
|
str_format(m_aFileSaveName, sizeof(m_aFileSaveName), "%s/%s", m_pFileDialogPath, m_FileDialogFileNameInput.GetString());
|
||||||
if(!str_endswith(m_aFileSaveName, FILETYPE_EXTENSIONS[m_FileDialogFileType]))
|
if(!str_endswith(m_aFileSaveName, FILETYPE_EXTENSIONS[m_FileDialogFileType]))
|
||||||
str_append(m_aFileSaveName, FILETYPE_EXTENSIONS[m_FileDialogFileType]);
|
str_append(m_aFileSaveName, FILETYPE_EXTENSIONS[m_FileDialogFileType]);
|
||||||
if(!str_comp(m_pFileDialogButtonText, "Save"))
|
if(!str_comp(m_pFileDialogButtonText, "Save") && Storage()->FileExists(m_aFileSaveName, StorageType))
|
||||||
{
|
{
|
||||||
if(Storage()->FileExists(m_aFileSaveName, IStorage::TYPE_SAVE))
|
m_PopupEventType = m_pfnFileDialogFunc == &CallbackSaveCopyMap ? POPEVENT_SAVE_COPY : POPEVENT_SAVE;
|
||||||
{
|
m_PopupEventActivated = true;
|
||||||
m_PopupEventType = m_pfnFileDialogFunc == &CallbackSaveCopyMap ? POPEVENT_SAVE_COPY : POPEVENT_SAVE;
|
|
||||||
m_PopupEventActivated = true;
|
|
||||||
}
|
|
||||||
else if(m_pfnFileDialogFunc)
|
|
||||||
m_pfnFileDialogFunc(m_aFileSaveName, m_FilesSelectedIndex >= 0 ? m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType : m_FileDialogStorageType, m_pFileDialogUser);
|
|
||||||
}
|
}
|
||||||
else if(m_pfnFileDialogFunc)
|
else if(m_pfnFileDialogFunc)
|
||||||
m_pfnFileDialogFunc(m_aFileSaveName, m_FilesSelectedIndex >= 0 ? m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType : m_FileDialogStorageType, m_pFileDialogUser);
|
m_pfnFileDialogFunc(m_aFileSaveName, StorageType, m_pFileDialogUser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5008,9 +5038,11 @@ void CEditor::RenderFileDialog()
|
||||||
ButtonBar.VSplitRight(90.0f, &ButtonBar, &Button);
|
ButtonBar.VSplitRight(90.0f, &ButtonBar, &Button);
|
||||||
if(DoButton_Editor(&s_ShowDirectoryButton, "Show directory", 0, &Button, 0, "Open the current directory in the file browser"))
|
if(DoButton_Editor(&s_ShowDirectoryButton, "Show directory", 0, &Button, 0, "Open the current directory in the file browser"))
|
||||||
{
|
{
|
||||||
if(!open_file(aPath))
|
char aOpenPath[IO_MAX_PATH_LENGTH];
|
||||||
|
Storage()->GetCompletePath(m_FilesSelectedIndex >= 0 ? m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType : IStorage::TYPE_SAVE, m_pFileDialogPath, aOpenPath, sizeof(aOpenPath));
|
||||||
|
if(!open_file(aOpenPath))
|
||||||
{
|
{
|
||||||
ShowFileDialogError("Failed to open the directory '%s'.", aPath);
|
ShowFileDialogError("Failed to open the directory '%s'.", aOpenPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5051,7 +5083,7 @@ void CEditor::RenderFileDialog()
|
||||||
else
|
else
|
||||||
s_ConfirmDeletePopupContext.Reset();
|
s_ConfirmDeletePopupContext.Reset();
|
||||||
|
|
||||||
if(m_FileDialogStorageType == IStorage::TYPE_SAVE)
|
if(!m_FileDialogShowingRoot && m_FileDialogStorageType == IStorage::TYPE_SAVE)
|
||||||
{
|
{
|
||||||
ButtonBar.VSplitLeft(70.0f, &Button, &ButtonBar);
|
ButtonBar.VSplitLeft(70.0f, &Button, &ButtonBar);
|
||||||
if(DoButton_Editor(&s_NewFolderButton, "New folder", 0, &Button, 0, nullptr))
|
if(DoButton_Editor(&s_NewFolderButton, "New folder", 0, &Button, 0, nullptr))
|
||||||
|
@ -5076,7 +5108,8 @@ void CEditor::RefreshFilteredFileList()
|
||||||
m_vpFilteredFileList.push_back(&Item);
|
m_vpFilteredFileList.push_back(&Item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SortFilteredFileList();
|
if(!m_FileDialogShowingRoot)
|
||||||
|
SortFilteredFileList();
|
||||||
if(!m_vpFilteredFileList.empty())
|
if(!m_vpFilteredFileList.empty())
|
||||||
{
|
{
|
||||||
if(m_aFilesSelectedName[0])
|
if(m_aFilesSelectedName[0])
|
||||||
|
@ -5104,18 +5137,66 @@ void CEditor::FilelistPopulate(int StorageType, bool KeepSelection)
|
||||||
{
|
{
|
||||||
m_FileDialogLastPopulatedStorageType = StorageType;
|
m_FileDialogLastPopulatedStorageType = StorageType;
|
||||||
m_vCompleteFileList.clear();
|
m_vCompleteFileList.clear();
|
||||||
if(m_FileDialogStorageType != IStorage::TYPE_SAVE && !str_comp(m_pFileDialogPath, "maps"))
|
if(m_FileDialogShowingRoot)
|
||||||
{
|
{
|
||||||
CFilelistItem Item;
|
{
|
||||||
str_copy(Item.m_aFilename, "downloadedmaps");
|
CFilelistItem Item;
|
||||||
str_copy(Item.m_aName, "downloadedmaps/");
|
str_copy(Item.m_aFilename, m_pFileDialogPath);
|
||||||
Item.m_IsDir = true;
|
str_copy(Item.m_aName, "All combined");
|
||||||
Item.m_IsLink = true;
|
Item.m_IsDir = true;
|
||||||
Item.m_StorageType = IStorage::TYPE_SAVE;
|
Item.m_IsLink = true;
|
||||||
Item.m_TimeModified = 0;
|
Item.m_StorageType = IStorage::TYPE_ALL;
|
||||||
m_vCompleteFileList.push_back(Item);
|
Item.m_TimeModified = 0;
|
||||||
|
m_vCompleteFileList.push_back(Item);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int CheckStorageType = IStorage::TYPE_SAVE; CheckStorageType < Storage()->NumPaths(); ++CheckStorageType)
|
||||||
|
{
|
||||||
|
if(Storage()->FolderExists(m_pFileDialogPath, CheckStorageType))
|
||||||
|
{
|
||||||
|
CFilelistItem Item;
|
||||||
|
str_copy(Item.m_aFilename, m_pFileDialogPath);
|
||||||
|
Storage()->GetCompletePath(CheckStorageType, m_pFileDialogPath, Item.m_aName, sizeof(Item.m_aName));
|
||||||
|
str_append(Item.m_aName, "/", sizeof(Item.m_aName));
|
||||||
|
Item.m_IsDir = true;
|
||||||
|
Item.m_IsLink = true;
|
||||||
|
Item.m_StorageType = CheckStorageType;
|
||||||
|
Item.m_TimeModified = 0;
|
||||||
|
m_vCompleteFileList.push_back(Item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Add links for downloadedmaps and themes
|
||||||
|
if(!str_comp(m_pFileDialogPath, "maps"))
|
||||||
|
{
|
||||||
|
if(str_comp(m_pFileDialogButtonText, "Save") != 0 && Storage()->FolderExists("downloadedmaps", StorageType))
|
||||||
|
{
|
||||||
|
CFilelistItem Item;
|
||||||
|
str_copy(Item.m_aFilename, "downloadedmaps");
|
||||||
|
str_copy(Item.m_aName, "downloadedmaps/");
|
||||||
|
Item.m_IsDir = true;
|
||||||
|
Item.m_IsLink = true;
|
||||||
|
Item.m_StorageType = StorageType;
|
||||||
|
Item.m_TimeModified = 0;
|
||||||
|
m_vCompleteFileList.push_back(Item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Storage()->FolderExists("themes", StorageType))
|
||||||
|
{
|
||||||
|
CFilelistItem Item;
|
||||||
|
str_copy(Item.m_aFilename, "themes");
|
||||||
|
str_copy(Item.m_aName, "themes/");
|
||||||
|
Item.m_IsDir = true;
|
||||||
|
Item.m_IsLink = true;
|
||||||
|
Item.m_StorageType = StorageType;
|
||||||
|
Item.m_TimeModified = 0;
|
||||||
|
m_vCompleteFileList.push_back(Item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Storage()->ListDirectoryInfo(StorageType, m_pFileDialogPath, EditorListdirCallback, this);
|
||||||
}
|
}
|
||||||
Storage()->ListDirectoryInfo(StorageType, m_pFileDialogPath, EditorListdirCallback, this);
|
|
||||||
RefreshFilteredFileList();
|
RefreshFilteredFileList();
|
||||||
if(!KeepSelection)
|
if(!KeepSelection)
|
||||||
{
|
{
|
||||||
|
@ -5132,8 +5213,25 @@ void CEditor::InvokeFileDialog(int StorageType, int FileType, const char *pTitle
|
||||||
const char *pBasePath, const char *pDefaultName,
|
const char *pBasePath, const char *pDefaultName,
|
||||||
bool (*pfnFunc)(const char *pFileName, int StorageType, void *pUser), void *pUser)
|
bool (*pfnFunc)(const char *pFileName, int StorageType, void *pUser), void *pUser)
|
||||||
{
|
{
|
||||||
UI()->ClosePopupMenus();
|
|
||||||
m_FileDialogStorageType = StorageType;
|
m_FileDialogStorageType = StorageType;
|
||||||
|
if(m_FileDialogStorageType == IStorage::TYPE_ALL)
|
||||||
|
{
|
||||||
|
int NumStoragedWithFolder = 0;
|
||||||
|
for(int CheckStorageType = IStorage::TYPE_SAVE; CheckStorageType < Storage()->NumPaths(); ++CheckStorageType)
|
||||||
|
{
|
||||||
|
if(Storage()->FolderExists(m_pFileDialogPath, CheckStorageType))
|
||||||
|
{
|
||||||
|
NumStoragedWithFolder++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_FileDialogMultipleStorages = NumStoragedWithFolder > 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_FileDialogMultipleStorages = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UI()->ClosePopupMenus();
|
||||||
m_pFileDialogTitle = pTitle;
|
m_pFileDialogTitle = pTitle;
|
||||||
m_pFileDialogButtonText = pButtonText;
|
m_pFileDialogButtonText = pButtonText;
|
||||||
m_pfnFileDialogFunc = pfnFunc;
|
m_pfnFileDialogFunc = pfnFunc;
|
||||||
|
@ -5146,6 +5244,7 @@ void CEditor::InvokeFileDialog(int StorageType, int FileType, const char *pTitle
|
||||||
m_FileDialogFileType = FileType;
|
m_FileDialogFileType = FileType;
|
||||||
m_FilePreviewState = PREVIEW_UNLOADED;
|
m_FilePreviewState = PREVIEW_UNLOADED;
|
||||||
m_FileDialogOpening = true;
|
m_FileDialogOpening = true;
|
||||||
|
m_FileDialogShowingRoot = false;
|
||||||
|
|
||||||
if(pDefaultName)
|
if(pDefaultName)
|
||||||
m_FileDialogFileNameInput.Set(pDefaultName);
|
m_FileDialogFileNameInput.Set(pDefaultName);
|
||||||
|
@ -7151,8 +7250,15 @@ void CEditor::OnRender()
|
||||||
|
|
||||||
void CEditor::LoadCurrentMap()
|
void CEditor::LoadCurrentMap()
|
||||||
{
|
{
|
||||||
Load(m_pClient->GetCurrentMapPath(), IStorage::TYPE_ALL);
|
if(Load(m_pClient->GetCurrentMapPath(), IStorage::TYPE_SAVE))
|
||||||
m_ValidSaveFilename = true;
|
{
|
||||||
|
m_ValidSaveFilename = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Load(m_pClient->GetCurrentMapPath(), IStorage::TYPE_ALL);
|
||||||
|
m_ValidSaveFilename = false;
|
||||||
|
}
|
||||||
|
|
||||||
CGameClient *pGameClient = (CGameClient *)Kernel()->RequestInterface<IGameClient>();
|
CGameClient *pGameClient = (CGameClient *)Kernel()->RequestInterface<IGameClient>();
|
||||||
vec2 Center = pGameClient->m_Camera.m_Center;
|
vec2 Center = pGameClient->m_Camera.m_Center;
|
||||||
|
|
|
@ -977,6 +977,8 @@ public:
|
||||||
CLineInputBuffered<IO_MAX_PATH_LENGTH> m_FileDialogFilterInput;
|
CLineInputBuffered<IO_MAX_PATH_LENGTH> m_FileDialogFilterInput;
|
||||||
char *m_pFileDialogPath;
|
char *m_pFileDialogPath;
|
||||||
int m_FileDialogFileType;
|
int m_FileDialogFileType;
|
||||||
|
bool m_FileDialogMultipleStorages = false;
|
||||||
|
bool m_FileDialogShowingRoot = false;
|
||||||
int m_FilesSelectedIndex;
|
int m_FilesSelectedIndex;
|
||||||
CLineInputBuffered<IO_MAX_PATH_LENGTH> m_FileDialogNewFolderNameInput;
|
CLineInputBuffered<IO_MAX_PATH_LENGTH> m_FileDialogNewFolderNameInput;
|
||||||
|
|
||||||
|
@ -1004,6 +1006,8 @@ public:
|
||||||
return true;
|
return true;
|
||||||
if(str_comp(pRhs->m_aFilename, "..") == 0)
|
if(str_comp(pRhs->m_aFilename, "..") == 0)
|
||||||
return false;
|
return false;
|
||||||
|
if(pLhs->m_IsLink != pRhs->m_IsLink)
|
||||||
|
return pLhs->m_IsLink;
|
||||||
if(pLhs->m_IsDir != pRhs->m_IsDir)
|
if(pLhs->m_IsDir != pRhs->m_IsDir)
|
||||||
return pLhs->m_IsDir;
|
return pLhs->m_IsDir;
|
||||||
return str_comp_filenames(pLhs->m_aName, pRhs->m_aName) < 0;
|
return str_comp_filenames(pLhs->m_aName, pRhs->m_aName) < 0;
|
||||||
|
@ -1015,6 +1019,8 @@ public:
|
||||||
return true;
|
return true;
|
||||||
if(str_comp(pRhs->m_aFilename, "..") == 0)
|
if(str_comp(pRhs->m_aFilename, "..") == 0)
|
||||||
return false;
|
return false;
|
||||||
|
if(pLhs->m_IsLink != pRhs->m_IsLink)
|
||||||
|
return pLhs->m_IsLink;
|
||||||
if(pLhs->m_IsDir != pRhs->m_IsDir)
|
if(pLhs->m_IsDir != pRhs->m_IsDir)
|
||||||
return pLhs->m_IsDir;
|
return pLhs->m_IsDir;
|
||||||
return str_comp_filenames(pLhs->m_aName, pRhs->m_aName) > 0;
|
return str_comp_filenames(pLhs->m_aName, pRhs->m_aName) > 0;
|
||||||
|
|
|
@ -140,7 +140,6 @@ MACRO_CONFIG_COL(UiColor, ui_color, 0xE4A046AF, CFGFLAG_CLIENT | CFGFLAG_SAVE |
|
||||||
MACRO_CONFIG_INT(UiColorizePing, ui_colorize_ping, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Highlight ping")
|
MACRO_CONFIG_INT(UiColorizePing, ui_colorize_ping, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Highlight ping")
|
||||||
MACRO_CONFIG_INT(UiColorizeGametype, ui_colorize_gametype, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Highlight gametype")
|
MACRO_CONFIG_INT(UiColorizeGametype, ui_colorize_gametype, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Highlight gametype")
|
||||||
|
|
||||||
MACRO_CONFIG_STR(UiDemoSelected, ui_demo_selected, 256, "", CFGFLAG_CLIENT | CFGFLAG_SAVE, "Selected demo file")
|
|
||||||
MACRO_CONFIG_INT(UiCloseWindowAfterChangingSetting, ui_close_window_after_changing_setting, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Close window after changing setting")
|
MACRO_CONFIG_INT(UiCloseWindowAfterChangingSetting, ui_close_window_after_changing_setting, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Close window after changing setting")
|
||||||
MACRO_CONFIG_INT(UiUnreadNews, ui_unread_news, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Whether there is unread news")
|
MACRO_CONFIG_INT(UiUnreadNews, ui_unread_news, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Whether there is unread news")
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue