Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | /* |
2 | * Portions of this file are copyright Rebirth contributors and licensed as |
||
3 | * described in COPYING.txt. |
||
4 | * Portions of this file are copyright Parallax Software and licensed |
||
5 | * according to the Parallax license below. |
||
6 | * See COPYING.txt for license details. |
||
7 | |||
8 | THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX |
||
9 | SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO |
||
10 | END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A |
||
11 | ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS |
||
12 | IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS |
||
13 | SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE |
||
14 | FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE |
||
15 | CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS |
||
16 | AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. |
||
17 | COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. |
||
18 | */ |
||
19 | |||
20 | #include <stdlib.h> |
||
21 | #include <string.h> |
||
22 | |||
23 | #include "event.h" |
||
24 | #include "maths.h" |
||
25 | #include "pstypes.h" |
||
26 | #include "gr.h" |
||
27 | #include "key.h" |
||
28 | #include "mouse.h" |
||
29 | #include "strutil.h" |
||
30 | #include "ui.h" |
||
31 | #include "window.h" |
||
32 | #include "u_mem.h" |
||
33 | #include "physfsx.h" |
||
34 | #include "physfs_list.h" |
||
35 | |||
36 | #include "compiler-range_for.h" |
||
37 | #include "d_range.h" |
||
38 | #include <memory> |
||
39 | |||
40 | namespace dcx { |
||
41 | |||
42 | bool isDirectory(const char *fname) // Pierre-Marie Baty -- work around PHYSFS_isDirectory() deprecation |
||
43 | { |
||
44 | PHYSFS_Stat statbuf; |
||
45 | if (!PHYSFS_stat(fname, &statbuf)) return false; |
||
46 | return (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY); |
||
47 | } |
||
48 | |||
49 | static PHYSFSX_counted_list file_getdirlist(const char *dir) |
||
50 | { |
||
51 | ntstring<PATH_MAX - 1> path; |
||
52 | auto dlen = path.copy_if(dir); |
||
53 | if ((!dlen && dir[0] != '\0') || !path.copy_if(dlen, "/")) |
||
54 | return nullptr; |
||
55 | ++ dlen; |
||
56 | PHYSFSX_counted_list list{PHYSFS_enumerateFiles(dir)}; |
||
57 | if (!list) |
||
58 | return nullptr; |
||
59 | const auto predicate = [&](char *i) -> bool { |
||
60 | if (path.copy_if(dlen, i) && /*PHYSFS_*/isDirectory(path)) // Pierre-Marie Baty -- work around PHYSFS_isDirectory() deprecation |
||
61 | return false; |
||
62 | free(i); |
||
63 | return true; |
||
64 | }; |
||
65 | auto j = std::remove_if(list.begin(), list.end(), predicate); |
||
66 | *j = NULL; |
||
67 | auto NumDirs = j.get() - list.get(); |
||
68 | qsort(list.get(), NumDirs, sizeof(char *), string_array_sort_func); |
||
69 | if (*dir) |
||
70 | { |
||
71 | // Put the 'go to parent directory' sequence '..' first |
||
72 | ++NumDirs; |
||
73 | auto r = reinterpret_cast<char **>(realloc(list.get(), sizeof(char *)*(NumDirs + 1))); |
||
74 | if (!r) |
||
75 | return list; |
||
76 | list.release(); |
||
77 | list.reset(r); |
||
78 | std::move_backward(r, r + NumDirs, r + NumDirs + 1); |
||
79 | list[0] = d_strdup(".."); |
||
80 | } |
||
81 | list.set_count(NumDirs); |
||
82 | return list; |
||
83 | } |
||
84 | |||
85 | static PHYSFSX_counted_list file_getfilelist(const char *filespec, const char *dir) |
||
86 | { |
||
87 | PHYSFSX_counted_list list{PHYSFS_enumerateFiles(dir)}; |
||
88 | if (!list) |
||
89 | return nullptr; |
||
90 | |||
91 | if (*filespec == '*') |
||
92 | filespec++; |
||
93 | |||
94 | const auto predicate = [&](char *i) -> bool { |
||
95 | auto ext = strrchr(i, '.'); |
||
96 | if (ext && (!d_stricmp(ext, filespec))) |
||
97 | return false; |
||
98 | free(i); |
||
99 | return true; |
||
100 | }; |
||
101 | auto j = std::remove_if(list.begin(), list.end(), predicate); |
||
102 | *j = NULL; |
||
103 | auto NumFiles = j.get() - list.get(); |
||
104 | list.set_count(NumFiles); |
||
105 | qsort(list.get(), NumFiles, sizeof(char *), string_array_sort_func); |
||
106 | return list; |
||
107 | } |
||
108 | |||
109 | namespace { |
||
110 | |||
111 | struct ui_file_browser |
||
112 | { |
||
113 | char *filename; |
||
114 | const char *filespec; |
||
115 | const char *message; |
||
116 | PHYSFSX_counted_list filename_list, directory_list; |
||
117 | std::unique_ptr<UI_GADGET_BUTTON> button1, button2, help_button; |
||
118 | std::unique_ptr<UI_GADGET_LISTBOX> listbox1, listbox2; |
||
119 | std::unique_ptr<UI_GADGET_INPUTBOX> user_file; |
||
120 | std::array<char, 35> spaces; |
||
121 | char view_dir[PATH_MAX]; |
||
122 | }; |
||
123 | |||
124 | } |
||
125 | |||
126 | static window_event_result browser_handler(UI_DIALOG *const dlg, const d_event &event, ui_file_browser *const b) |
||
127 | { |
||
128 | window_event_result rval = window_event_result::ignored; |
||
129 | |||
130 | if (event.type == EVENT_UI_DIALOG_DRAW) |
||
131 | { |
||
132 | ui_dputs_at( dlg, 10, 5, b->message ); |
||
133 | |||
134 | ui_dprintf_at( dlg, 20, 32,"N&ame" ); |
||
135 | ui_dprintf_at( dlg, 20, 86,"&Files" ); |
||
136 | ui_dprintf_at( dlg, 210, 86,"&Dirs" ); |
||
137 | |||
138 | ui_dputs_at(dlg, 20, 60, b->spaces.data()); |
||
139 | ui_dputs_at( dlg, 20, 60, b->view_dir ); |
||
140 | |||
141 | return window_event_result::handled; |
||
142 | } |
||
143 | |||
144 | if (GADGET_PRESSED(b->button2.get())) |
||
145 | { |
||
146 | b->filename_list.reset(); |
||
147 | b->directory_list.reset(); |
||
148 | return window_event_result::close; |
||
149 | } |
||
150 | |||
151 | if (GADGET_PRESSED(b->help_button.get())) |
||
152 | { |
||
153 | ui_messagebox( -1, -1, 1, "Sorry, no help is available!", "Ok" ); |
||
154 | rval = window_event_result::handled; |
||
155 | } |
||
156 | |||
157 | if (event.type == EVENT_UI_LISTBOX_MOVED) |
||
158 | { |
||
159 | if ((ui_event_get_gadget(event) == b->listbox1.get()) && (b->listbox1->current_item >= 0) && b->filename_list[b->listbox1->current_item]) |
||
160 | ui_inputbox_set_text(b->user_file.get(), b->filename_list[b->listbox1->current_item]); |
||
161 | |||
162 | if ((ui_event_get_gadget(event) == b->listbox2.get()) && (b->listbox2->current_item >= 0) && b->directory_list[b->listbox2->current_item]) |
||
163 | ui_inputbox_set_text(b->user_file.get(), b->directory_list[b->listbox2->current_item]); |
||
164 | |||
165 | rval = window_event_result::handled; |
||
166 | } |
||
167 | |||
168 | if (GADGET_PRESSED(b->button1.get()) || GADGET_PRESSED(b->user_file.get()) || event.type == EVENT_UI_LISTBOX_SELECTED) |
||
169 | { |
||
170 | char *p; |
||
171 | |||
172 | if (ui_event_get_gadget(event) == b->listbox2.get()) |
||
173 | strcpy(b->user_file->text.get(), b->directory_list[b->listbox2->current_item]); |
||
174 | |||
175 | strncpy(b->filename, b->view_dir, PATH_MAX); |
||
176 | |||
177 | p = b->user_file->text.get(); |
||
178 | while (!strncmp(p, "..", 2)) // shorten the path manually |
||
179 | { |
||
180 | char *sep = strrchr(b->filename, '/'); |
||
181 | if (sep) |
||
182 | *sep = 0; |
||
183 | else |
||
184 | *b->filename = 0; // look directly in search paths |
||
185 | |||
186 | p += 2; |
||
187 | if (*p == '/') |
||
188 | p++; |
||
189 | } |
||
190 | |||
191 | if (*b->filename && *p) |
||
192 | strncat(b->filename, "/", PATH_MAX - strlen(b->filename)); |
||
193 | strncat(b->filename, p, PATH_MAX - strlen(b->filename)); |
||
194 | |||
195 | if (!/*PHYSFS_*/isDirectory(b->filename)) // Pierre-Marie Baty -- work around PHYSFS_isDirectory() deprecation |
||
196 | { |
||
197 | if (RAIIPHYSFS_File{PHYSFS_openRead(b->filename)}) |
||
198 | { |
||
199 | // Looks like a valid filename that already exists! |
||
200 | return window_event_result::close; |
||
201 | } |
||
202 | |||
203 | // File doesn't exist, but can we create it? |
||
204 | if (RAIIPHYSFS_File TempFile{PHYSFS_openWrite(b->filename)}) |
||
205 | { |
||
206 | TempFile.reset(); |
||
207 | // Looks like a valid filename! |
||
208 | PHYSFS_delete(b->filename); |
||
209 | return window_event_result::close; |
||
210 | } |
||
211 | } |
||
212 | else |
||
213 | { |
||
214 | if (b->filename[strlen(b->filename) - 1] == '/') // user typed a separator on the end |
||
215 | b->filename[strlen(b->filename) - 1] = 0; |
||
216 | |||
217 | strcpy(b->view_dir, b->filename); |
||
218 | b->filename_list = file_getfilelist(b->filespec, b->view_dir); |
||
219 | if (!b->filename_list) |
||
220 | { |
||
221 | b->directory_list.reset(); |
||
222 | return window_event_result::close; |
||
223 | } |
||
224 | |||
225 | ui_inputbox_set_text(b->user_file.get(), b->filespec); |
||
226 | b->directory_list = file_getdirlist(b->view_dir); |
||
227 | if (!b->directory_list) |
||
228 | { |
||
229 | b->filename_list.reset(); |
||
230 | return window_event_result::close; |
||
231 | } |
||
232 | |||
233 | ui_listbox_change(dlg, b->listbox1.get(), b->filename_list.get_count(), b->filename_list.get()); |
||
234 | ui_listbox_change(dlg, b->listbox2.get(), b->directory_list.get_count(), b->directory_list.get()); |
||
235 | |||
236 | //i = TICKER; |
||
237 | //while ( TICKER < i+2 ); |
||
238 | |||
239 | } |
||
240 | |||
241 | rval = window_event_result::handled; |
||
242 | } |
||
243 | |||
244 | return rval; |
||
245 | } |
||
246 | |||
247 | int ui_get_filename(char (&filename)[PATH_MAX], const char *const filespec, const char *const message) |
||
248 | { |
||
249 | char InputText[PATH_MAX]; |
||
250 | char *p; |
||
251 | UI_DIALOG *dlg; |
||
252 | int rval = 0; |
||
253 | auto b = std::make_unique<ui_file_browser>(); |
||
254 | if ((p = strrchr(filename, '/'))) |
||
255 | { |
||
256 | *p++ = 0; |
||
257 | strcpy(b->view_dir, filename); |
||
258 | strcpy(InputText, p); |
||
259 | } |
||
260 | else |
||
261 | { |
||
262 | strcpy(b->view_dir, ""); |
||
263 | strcpy(InputText, filename); |
||
264 | } |
||
265 | |||
266 | b->filename_list = file_getfilelist(filespec, b->view_dir); |
||
267 | if (!b->filename_list) |
||
268 | { |
||
269 | return 0; |
||
270 | } |
||
271 | |||
272 | b->directory_list = file_getdirlist(b->view_dir); |
||
273 | if (!b->directory_list) |
||
274 | { |
||
275 | b->filename_list.reset(); |
||
276 | return 0; |
||
277 | } |
||
278 | |||
279 | //ui_messagebox( -2,-2, 1,"DEBUG:0", "Ok" ); |
||
280 | range_for (const int i, xrange(35u)) |
||
281 | b->spaces[i] = ' '; |
||
282 | b->spaces[34] = 0; |
||
283 | |||
284 | dlg = ui_create_dialog( 200, 100, 400, 370, static_cast<dialog_flags>(DF_DIALOG | DF_MODAL), browser_handler, b.get()); |
||
285 | |||
286 | b->user_file = ui_add_gadget_inputbox<40>(dlg, 60, 30, InputText); |
||
287 | |||
288 | b->listbox1 = ui_add_gadget_listbox(dlg, 20, 110, 125, 200, b->filename_list.get_count(), b->filename_list.get()); |
||
289 | b->listbox2 = ui_add_gadget_listbox(dlg, 210, 110, 100, 200, b->directory_list.get_count(), b->directory_list.get()); |
||
290 | |||
291 | b->button1 = ui_add_gadget_button( dlg, 20, 330, 60, 25, "Ok", NULL ); |
||
292 | b->button2 = ui_add_gadget_button( dlg, 100, 330, 60, 25, "Cancel", NULL ); |
||
293 | b->help_button = ui_add_gadget_button( dlg, 180, 330, 60, 25, "Help", NULL ); |
||
294 | |||
295 | dlg->keyboard_focus_gadget = b->user_file.get(); |
||
296 | |||
297 | b->button1->hotkey = KEY_CTRLED + KEY_ENTER; |
||
298 | b->button2->hotkey = KEY_ESC; |
||
299 | b->help_button->hotkey = KEY_F1; |
||
300 | b->listbox1->hotkey = KEY_ALTED + KEY_F; |
||
301 | b->listbox2->hotkey = KEY_ALTED + KEY_D; |
||
302 | b->user_file->hotkey = KEY_ALTED + KEY_A; |
||
303 | |||
304 | ui_gadget_calc_keys(dlg); |
||
305 | |||
306 | b->filename = filename; |
||
307 | b->filespec = filespec; |
||
308 | b->message = message; |
||
309 | |||
310 | event_process_all(); |
||
311 | |||
312 | //key_flush(); |
||
313 | |||
314 | rval = static_cast<bool>(b->filename_list); |
||
315 | b->filename_list.reset(); |
||
316 | b->directory_list.reset(); |
||
317 | return rval; |
||
318 | } |
||
319 | |||
320 | int ui_get_file( char * filename, const char * Filespec ) |
||
321 | { |
||
322 | int x; |
||
323 | auto list = file_getfilelist(Filespec, ""); |
||
324 | if (!list) return 0; |
||
325 | x = MenuX(-1, -1, list.get_count(), list.get()); |
||
326 | if (x > 0) |
||
327 | strcpy(filename, list[x - 1]); |
||
328 | return (x > 0); |
||
329 | } |
||
330 | |||
331 | } |