1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#ifndef GAME_EDITOR_FILE_BROWSER_H
4#define GAME_EDITOR_FILE_BROWSER_H
5
6#include "component.h"
7
8#include <base/types.h>
9
10#include <game/client/ui.h>
11#include <game/client/ui_listbox.h>
12
13#include <optional>
14#include <vector>
15
16class CFileBrowser : public CEditorComponent
17{
18public:
19 enum class EFileType
20 {
21 MAP,
22 IMAGE,
23 SOUND,
24 };
25 typedef bool (*FFileDialogOpenCallback)(const char *pFilename, int StorageType, void *pUser);
26
27 void ShowFileDialog(
28 int StorageType, EFileType FileType,
29 const char *pTitle, const char *pButtonText,
30 const char *pInitialPath, const char *pInitialFilename,
31 FFileDialogOpenCallback pfnOpenCallback, void *pOpenCallbackUser);
32 void OnRender(CUIRect _) override;
33 bool IsValidSaveFilename() const;
34
35 void OnEditorClose();
36 void OnDialogClose();
37
38private:
39 /**
40 * Storage type for which the file browser is currently showing entries.
41 */
42 int m_StorageType = 0;
43 /**
44 * File type for which the file browser is currently showing entries.
45 */
46 EFileType m_FileType = EFileType::MAP;
47 /**
48 * `true` if file browser was opened with the intent to save a file.
49 * `false` if file browser was opened with the intent to open an existing file for reading.
50 */
51 bool m_SaveAction = false;
52 /**
53 * Whether multiple storage locations with the initial folder are available.
54 */
55 bool m_MultipleStorages = false;
56 /**
57 * Title text of the file dialog.
58 */
59 char m_aTitle[128] = "";
60 /**
61 * Text of the confirmation button that opens/saves the file.
62 */
63 char m_aButtonText[64] = "";
64 /**
65 * Callback function that will be called when the confirmation button is pressed.
66 */
67 FFileDialogOpenCallback m_pfnOpenCallback = nullptr;
68 /**
69 * User data for @link m_pfnOpenCallback @endlink.
70 */
71 void *m_pOpenCallbackUser = nullptr;
72 /**
73 * Whether the list of storages is currently being shown, if @link m_MultipleStorages @endlink is `true`.
74 */
75 bool m_ShowingRoot = false;
76 /**
77 * Path of the initial folder that the file browser was opened with.
78 */
79 char m_aInitialFolder[IO_MAX_PATH_LENGTH] = "";
80 /**
81 * Path of the current folder being shown in the file browser.
82 */
83 char m_aCurrentFolder[IO_MAX_PATH_LENGTH] = "";
84 /**
85 * Path of the current link being shown in the file browser.
86 */
87 char m_aCurrentLink[IO_MAX_PATH_LENGTH] = "";
88 /**
89 * Current path being shown in the file browser, which either points to @link m_aCurrentFolder @endlink or @link m_aCurrentLink @endlink.
90 */
91 char *m_pCurrentPath = m_aCurrentFolder;
92 /**
93 * Input for the filename when saving files and also buffer for the selected file's name in general.
94 */
95 CLineInputBuffered<IO_MAX_PATH_LENGTH> m_FilenameInput;
96 /**
97 * Input for the file search when opening files for reading.
98 */
99 CLineInputBuffered<IO_MAX_PATH_LENGTH> m_FilterInput;
100 /**
101 * Index of the selected file list entry in @link m_vpFilteredFileList @endlink.
102 */
103 int m_SelectedFileIndex = -1;
104 /**
105 * Display name of the selected file list entry in @link m_vpFilteredFileList @endlink and @link m_vCompleteFileList @endlink.
106 */
107 char m_aSelectedFileDisplayName[IO_MAX_PATH_LENGTH] = "";
108
109 // File list
110 class CFilelistItem
111 {
112 public:
113 char m_aFilename[IO_MAX_PATH_LENGTH];
114 char m_aDisplayName[IO_MAX_PATH_LENGTH];
115 bool m_IsDir;
116 bool m_IsLink;
117 int m_StorageType;
118 time_t m_TimeModified;
119 };
120 std::vector<CFilelistItem> m_vCompleteFileList;
121 std::vector<const CFilelistItem *> m_vpFilteredFileList;
122 enum class ESortDirection
123 {
124 NEUTRAL,
125 ASCENDING,
126 DESCENDING,
127 };
128 ESortDirection m_SortByFilename = ESortDirection::ASCENDING;
129 ESortDirection m_SortByTimeModified = ESortDirection::NEUTRAL;
130
131 // File preview
132 enum class EPreviewState
133 {
134 UNLOADED,
135 LOADED,
136 ERROR,
137 };
138 EPreviewState m_PreviewState = EPreviewState::UNLOADED;
139 IGraphics::CTextureHandle m_PreviewImage;
140 int m_PreviewImageWidth = 0;
141 int m_PreviewImageHeight = 0;
142 int m_PreviewSound = -1;
143
144 // UI elements
145 CListBox m_ListBox;
146 const char m_ButtonSortTimeModifiedId = 0;
147 const char m_ButtonSortFilenameId = 0;
148 const char m_ButtonPlayPauseId = 0;
149 const char m_ButtonStopId = 0;
150 const char m_SeekBarId = 0;
151 const char m_ButtonOkId = 0;
152 const char m_ButtonCancelId = 0;
153 const char m_ButtonRefreshId = 0;
154 const char m_ButtonShowDirectoryId = 0;
155 const char m_ButtonDeleteId = 0;
156 const char m_ButtonNewFolderId = 0;
157
158 bool CanPreviewFile() const;
159 void UpdateFilePreview();
160 void RenderFilePreview(CUIRect Preview);
161 const char *DetermineFileFontIcon(const CFilelistItem *pItem) const;
162 void UpdateFilenameInput();
163 void UpdateSelectedIndex(const char *pDisplayName);
164 void SortFilteredFileList();
165 void RefreshFilteredFileList();
166 void FilelistPopulate(int StorageType, bool KeepSelection);
167 static int DirectoryListingCallback(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser);
168 static std::optional<bool> CompareCommon(const CFilelistItem *pLhs, const CFilelistItem *pRhs);
169 static bool CompareFilenameAscending(const CFilelistItem *pLhs, const CFilelistItem *pRhs);
170 static bool CompareFilenameDescending(const CFilelistItem *pLhs, const CFilelistItem *pRhs);
171 static bool CompareTimeModifiedAscending(const CFilelistItem *pLhs, const CFilelistItem *pRhs);
172 static bool CompareTimeModifiedDescending(const CFilelistItem *pLhs, const CFilelistItem *pRhs);
173
174 class CPopupNewFolder : public SPopupMenuId
175 {
176 public:
177 CFileBrowser *m_pFileBrowser;
178 CLineInputBuffered<IO_MAX_PATH_LENGTH> m_NewFolderNameInput;
179 static CUi::EPopupMenuFunctionResult Render(void *pContext, CUIRect View, bool Active);
180
181 private:
182 const char m_ButtonCancelId = 0;
183 const char m_ButtonCreateId = 0;
184 };
185 CPopupNewFolder m_PopupNewFolder;
186
187 class CPopupConfirmDelete : public SPopupMenuId
188 {
189 public:
190 CFileBrowser *m_pFileBrowser;
191 bool m_IsDirectory;
192 char m_aDeletePath[IO_MAX_PATH_LENGTH];
193 static CUi::EPopupMenuFunctionResult Render(void *pContext, CUIRect View, bool Active);
194
195 private:
196 const char m_ButtonCancelId = 0;
197 const char m_ButtonDeleteId = 0;
198 };
199 CPopupConfirmDelete m_PopupConfirmDelete;
200
201 class CPopupConfirmOverwrite : public SPopupMenuId
202 {
203 public:
204 CFileBrowser *m_pFileBrowser;
205 char m_aOverwritePath[IO_MAX_PATH_LENGTH];
206 static CUi::EPopupMenuFunctionResult Render(void *pContext, CUIRect View, bool Active);
207
208 private:
209 const char m_ButtonCancelId = 0;
210 const char m_ButtonOverwriteId = 0;
211 };
212 CPopupConfirmOverwrite m_PopupConfirmOverwrite;
213};
214
215#endif
216