Subversion Repositories Games.Descent

Rev

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
}