|
|
1.1 root 1: /*
2: Hatari - dlgFileSelect.c
1.1.1.13 root 3:
1.1.1.11 root 4: This file is distributed under the GNU General Public License, version 2
5: or at your option any later version. Read the file gpl.txt for details.
1.1.1.13 root 6:
1.1 root 7: A file selection dialog for the graphical user interface for Hatari.
8: */
1.1.1.7 root 9: const char DlgFileSelect_fileid[] = "Hatari dlgFileSelect.c : " __DATE__ " " __TIME__;
1.1 root 10:
11: #include <SDL.h>
12: #include <sys/stat.h>
13: #include <unistd.h>
14:
15: #include "main.h"
1.1.1.3 root 16: #include "scandir.h"
1.1 root 17: #include "sdlgui.h"
18: #include "file.h"
1.1.1.8 root 19: #include "paths.h"
1.1 root 20: #include "zip.h"
1.1.1.15! root 21: #include "log.h"
1.1 root 22:
23:
1.1.1.2 root 24: #define SGFS_NUMENTRIES 16 /* How many entries are displayed at once */
25:
1.1.1.13 root 26: #define SGFSDLG_TITLE 1
1.1.1.11 root 27: #define SGFSDLG_FILENAME 5
28: #define SGFSDLG_UPDIR 6
29: #define SGFSDLG_CWD 7
30: #define SGFSDLG_HOMEDIR 8
31: #define SGFSDLG_ROOTDIR 9
32: #define SGFSDLG_ENTRYFIRST 12
33: #define SGFSDLG_ENTRYLAST 27
34: #define SGFSDLG_SCROLLBAR 28
35: #define SGFSDLG_UP 29
36: #define SGFSDLG_DOWN 30
37: #define SGFSDLG_SHOWHIDDEN 31
38: #define SGFSDLG_OKAY 32
39: #define SGFSDLG_CANCEL 33
1.1 root 40:
1.1.1.9 root 41: #define SCROLLOUT_ABOVE 1
42: #define SCROLLOUT_UNDER 2
1.1 root 43:
44: #define DLGPATH_SIZE 62
45: static char dlgpath[DLGPATH_SIZE+1]; /* Path name in the dialog */
46:
47: #define DLGFNAME_SIZE 56
48: static char dlgfname[DLGFNAME_SIZE+1]; /* Name of the selected file in the dialog */
49:
50: #define DLGFILENAMES_SIZE 59
1.1.1.2 root 51: static char dlgfilenames[SGFS_NUMENTRIES][DLGFILENAMES_SIZE+1]; /* Visible file names in the dialog */
1.1 root 52:
1.1.1.11 root 53: #define SCROLLBAR_MIN_HEIGHT 4 /* Min value for yScrollbar_size */
54:
1.1.1.13 root 55: #define TITLE_OFFSET 1
56: #define TITLE_MAXLEN 40
57:
1.1 root 58: /* The dialog data: */
59: static SGOBJ fsdlg[] =
60: {
61: { SGBOX, 0, 0, 0,0, 64,25, NULL },
1.1.1.13 root 62: { SGTEXT, 0, 0, 1,1, 13,1, NULL, },
1.1 root 63: { SGTEXT, 0, 0, 1,2, 7,1, "Folder:" },
64: { SGTEXT, 0, 0, 1,3, DLGPATH_SIZE,1, dlgpath },
65: { SGTEXT, 0, 0, 1,4, 6,1, "File:" },
66: { SGTEXT, 0, 0, 7,4, DLGFNAME_SIZE,1, dlgfname },
1.1.1.13 root 67: { SGBUTTON, 0, 0, 39,1, 4,1, "_Up" },
68: { SGBUTTON, 0, 0, 44,1, 5,1, "_CWD" },
69: { SGBUTTON, 0, 0, 50,1, 6,1, "_Home" },
70: { SGBUTTON, 0, 0, 57,1, 6,1, "_Root" },
1.1 root 71: { SGBOX, 0, 0, 1,6, 62,16, NULL },
72: { SGBOX, 0, 0, 62,7, 1,14, NULL },
73: { SGTEXT, SG_EXIT, 0, 2,6, DLGFILENAMES_SIZE,1, dlgfilenames[0] },
74: { SGTEXT, SG_EXIT, 0, 2,7, DLGFILENAMES_SIZE,1, dlgfilenames[1] },
75: { SGTEXT, SG_EXIT, 0, 2,8, DLGFILENAMES_SIZE,1, dlgfilenames[2] },
76: { SGTEXT, SG_EXIT, 0, 2,9, DLGFILENAMES_SIZE,1, dlgfilenames[3] },
77: { SGTEXT, SG_EXIT, 0, 2,10, DLGFILENAMES_SIZE,1, dlgfilenames[4] },
78: { SGTEXT, SG_EXIT, 0, 2,11, DLGFILENAMES_SIZE,1, dlgfilenames[5] },
79: { SGTEXT, SG_EXIT, 0, 2,12, DLGFILENAMES_SIZE,1, dlgfilenames[6] },
80: { SGTEXT, SG_EXIT, 0, 2,13, DLGFILENAMES_SIZE,1, dlgfilenames[7] },
81: { SGTEXT, SG_EXIT, 0, 2,14, DLGFILENAMES_SIZE,1, dlgfilenames[8] },
82: { SGTEXT, SG_EXIT, 0, 2,15, DLGFILENAMES_SIZE,1, dlgfilenames[9] },
83: { SGTEXT, SG_EXIT, 0, 2,16, DLGFILENAMES_SIZE,1, dlgfilenames[10] },
84: { SGTEXT, SG_EXIT, 0, 2,17, DLGFILENAMES_SIZE,1, dlgfilenames[11] },
85: { SGTEXT, SG_EXIT, 0, 2,18, DLGFILENAMES_SIZE,1, dlgfilenames[12] },
86: { SGTEXT, SG_EXIT, 0, 2,19, DLGFILENAMES_SIZE,1, dlgfilenames[13] },
87: { SGTEXT, SG_EXIT, 0, 2,20, DLGFILENAMES_SIZE,1, dlgfilenames[14] },
88: { SGTEXT, SG_EXIT, 0, 2,21, DLGFILENAMES_SIZE,1, dlgfilenames[15] },
1.1.1.9 root 89: { SGSCROLLBAR, SG_TOUCHEXIT, 0, 62, 7, 0, 0, NULL }, /* Scrollbar */
1.1.1.13 root 90: { SGBUTTON, SG_TOUCHEXIT, 0, 62, 6,1,1, "\x01", SG_SHORTCUT_UP },
91: { SGBUTTON, SG_TOUCHEXIT, 0, 62,21,1,1, "\x02", SG_SHORTCUT_DOWN },
92: { SGCHECKBOX, SG_EXIT, 0, 2,23, 19,1, "_Show hidden files" },
93: { SGBUTTON, SG_DEFAULT, 0, 32,23, 8,1, "OK" },
1.1.1.4 root 94: { SGBUTTON, SG_CANCEL, 0, 50,23, 8,1, "Cancel" },
1.1.1.14 root 95: { SGSTOP, 0, 0, 0,0, 0,0, NULL }
1.1 root 96: };
97:
98:
1.1.1.11 root 99: static int ypos = -1; /* First entry number to be displayed. If -1, file selector start on the 1st file */
100: /* else we continue from the previous position when SDLGui_FileSelect is called again */
101: static bool refreshentries; /* Do we have to update the file names in the dialog? */
102: static int entries; /* How many files are in the actual directory? */
103: static int oldMouseY = 0; /* Keep the latest Y mouse position for scrollbar move computing */
1.1.1.9 root 104: static int mouseClicked = 0; /* used to know if mouse if down for the first time or not */
1.1.1.11 root 105: static int mouseIsOut = 0; /* used to keep info that mouse if above or under the scrollbar when mousebutton is down */
106: static float scrollbar_Ypos = 0.0; /* scrollbar heigth */
1.1.1.9 root 107:
1.1.1.13 root 108: static char *dirpath; /* for get_dtype() */
109: #ifndef HAVE_DIRENT_D_TYPE
110: enum {
111: DT_UNKNOWN,
112: DT_LNK,
113: DT_DIR,
114: DT_REG
115: };
116: #endif
117:
1.1.1.9 root 118: /* Convert file position (in file list) to scrollbar y position */
119: static void DlgFileSelect_Convert_ypos_to_scrollbar_Ypos(void);
120:
1.1.1.2 root 121:
1.1 root 122:
123: /*-----------------------------------------------------------------------*/
1.1.1.8 root 124: /**
125: * Update the file name strings in the dialog.
126: * Returns false if it failed, true on success.
127: */
1.1.1.5 root 128: static int DlgFileSelect_RefreshEntries(struct dirent **files, char *path, bool browsingzip)
1.1 root 129: {
130: int i;
131: char *tempstr = malloc(FILENAME_MAX);
132:
133: if (!tempstr)
134: {
135: perror("DlgFileSelect_RefreshEntries");
1.1.1.7 root 136: return false;
1.1 root 137: }
138:
139: /* Copy entries to dialog: */
1.1.1.4 root 140: for (i=0; i<SGFS_NUMENTRIES; i++)
1.1 root 141: {
1.1.1.4 root 142: if (i+ypos < entries)
1.1 root 143: {
144: struct stat filestat;
145: /* Prepare entries: */
146: strcpy(tempstr, " ");
147: strcat(tempstr, files[i+ypos]->d_name);
148: File_ShrinkName(dlgfilenames[i], tempstr, DLGFILENAMES_SIZE);
149: /* Mark folders: */
150: strcpy(tempstr, path);
151: strcat(tempstr, files[i+ypos]->d_name);
152:
1.1.1.4 root 153: if (browsingzip)
1.1 root 154: {
1.1.1.3 root 155: if (File_DoesFileNameEndWithSlash(tempstr))
1.1 root 156: dlgfilenames[i][0] = SGFOLDER; /* Mark folders */
157: }
158: else
159: {
160: if( stat(tempstr, &filestat)==0 && S_ISDIR(filestat.st_mode) )
161: dlgfilenames[i][0] = SGFOLDER; /* Mark folders */
1.1.1.7 root 162: if (ZIP_FileNameIsZIP(tempstr) && browsingzip == false)
1.1 root 163: dlgfilenames[i][0] = SGFOLDER; /* Mark .ZIP archives as folders */
164: }
1.1.1.13 root 165:
166: fsdlg[SGFSDLG_ENTRYFIRST+i].flags |= SG_EXIT;
1.1 root 167: }
168: else
1.1.1.13 root 169: {
1.1 root 170: dlgfilenames[i][0] = 0; /* Clear entry */
1.1.1.13 root 171: fsdlg[SGFSDLG_ENTRYFIRST+i].flags &= ~SG_EXIT;
172: }
1.1 root 173: }
174:
175: free(tempstr);
1.1.1.7 root 176: return true;
1.1 root 177: }
178:
179:
180: /*-----------------------------------------------------------------------*/
1.1.1.8 root 181: /**
182: * Remove all hidden files (files with file names that begin with a dot) from
183: * the list.
184: */
1.1.1.3 root 185: static void DlgFileSelect_RemoveHiddenFiles(struct dirent **files)
186: {
187: int i;
188: int nActPos = -1;
189: int nOldEntries;
190:
191: nOldEntries = entries;
192:
193: /* Scan list for hidden files and remove them. */
194: for (i = 0; i < nOldEntries; i++)
195: {
196: /* Does file name start with a dot? -> hidden file! */
197: if (files[i]->d_name[0] == '.')
198: {
199: if (nActPos == -1)
200: nActPos = i;
201: /* Remove file from list: */
202: free(files[i]);
203: files[i] = NULL;
204: entries -= 1;
205: }
206: }
207:
208: /* Now close the gaps in the list: */
209: if (nActPos != -1)
210: {
211: for (i = nActPos; i < nOldEntries; i++)
212: {
213: if (files[i] != NULL)
214: {
215: /* Move entry to earlier position: */
216: files[nActPos] = files[i];
217: files[i] = NULL;
218: nActPos += 1;
219: }
220: }
221: }
222: }
223:
1.1.1.13 root 224: /**
225: * Reset focus to first entry if necessary.
226: */
227: static void DlgFileSelect_ResetFocus(void)
228: {
229: int i;
230:
231: for (i = SGFSDLG_ENTRYFIRST+1; i <= SGFSDLG_ENTRYLAST; i++)
232: {
233: if (fsdlg[i].state & SG_FOCUSED)
234: {
235: fsdlg[i].state &= ~SG_FOCUSED;
236: fsdlg[SGFSDLG_ENTRYFIRST].state |= SG_FOCUSED;
237: break;
238: }
239: }
240: }
1.1.1.3 root 241:
242: /*-----------------------------------------------------------------------*/
1.1.1.8 root 243: /**
244: * Prepare to scroll up one entry.
245: */
1.1.1.2 root 246: static void DlgFileSelect_ScrollUp(void)
247: {
248: if (ypos > 0)
249: {
250: --ypos;
1.1.1.9 root 251: DlgFileSelect_Convert_ypos_to_scrollbar_Ypos();
1.1.1.7 root 252: refreshentries = true;
1.1.1.2 root 253: }
254: }
255:
256:
257: /*-----------------------------------------------------------------------*/
1.1.1.8 root 258: /**
259: * Prepare to scroll down one entry.
260: */
1.1.1.2 root 261: static void DlgFileSelect_ScrollDown(void)
262: {
263: if (ypos+SGFS_NUMENTRIES < entries)
264: {
265: ++ypos;
1.1.1.9 root 266: DlgFileSelect_Convert_ypos_to_scrollbar_Ypos();
1.1.1.7 root 267: refreshentries = true;
1.1.1.2 root 268: }
269: }
270:
1.1.1.9 root 271: /*-----------------------------------------------------------------------*/
272: /**
273: * Manage the scrollbar up or down.
274: */
275: static void DlgFileSelect_ManageScrollbar(void)
276: {
277: int b, x, y;
278: int scrollY, scrollYmin, scrollYmax, scrollH_half;
279: float scrollMove;
1.1.1.12 root 280:
281: SDL_GetMouseState(&x, &y);
1.1.1.9 root 282:
283: /* If mouse is down on the scrollbar for the first time */
284: if (fsdlg[SGFSDLG_SCROLLBAR].state & SG_MOUSEDOWN) {
285: if (mouseClicked == 0) {
286: mouseClicked = 1;
287: mouseIsOut = 0;
288: oldMouseY = y;
289: }
290: }
291: /* Mouse button is up on the scrollbar */
292: else {
293: mouseClicked = 0;
294: oldMouseY = y;
295: mouseIsOut = 0;
296: }
1.1.1.13 root 297:
298: /* If mouse Y position didn't change */
1.1.1.9 root 299: if (oldMouseY == y)
300: return;
301:
1.1.1.13 root 302: /* Compute scrollbar ymin and ymax values */
1.1.1.9 root 303:
304: scrollYmin = (fsdlg[SGFSDLG_SCROLLBAR].y + fsdlg[0].y) * sdlgui_fontheight;
305: scrollYmax = (fsdlg[SGFSDLG_DOWN].y + fsdlg[0].y) * sdlgui_fontheight;
1.1.1.13 root 306:
1.1.1.9 root 307: scrollY = fsdlg[SGFSDLG_SCROLLBAR].y * sdlgui_fontheight + fsdlg[SGFSDLG_SCROLLBAR].h + fsdlg[0].y * sdlgui_fontheight;
308: scrollH_half = scrollY + fsdlg[SGFSDLG_SCROLLBAR].w / 2;
309: scrollMove = (float)(y-oldMouseY)/sdlgui_fontheight;
1.1.1.13 root 310:
1.1.1.9 root 311: /* Verify if mouse is not above the scrollbar area */
312: if (y < scrollYmin) {
313: mouseIsOut = SCROLLOUT_ABOVE;
314: oldMouseY = y;
315: return;
316: }
317: if (mouseIsOut == SCROLLOUT_ABOVE && y < scrollH_half) {
318: oldMouseY = y;
319: return;
320: }
321:
322: /* Verify if mouse is not under the scrollbar area */
323: if (y > scrollYmax) {
324: mouseIsOut = SCROLLOUT_UNDER;
325: oldMouseY = y;
326: return;
327: }
328: if (mouseIsOut == SCROLLOUT_UNDER && y > scrollH_half) {
329: oldMouseY = y;
330: return;
331: }
332:
333: mouseIsOut = 0;
334:
335: scrollbar_Ypos += scrollMove;
336: oldMouseY = y;
337:
338: /* Verifiy if scrollbar is in correct inferior boundary */
339: if (scrollbar_Ypos < 0)
340: scrollbar_Ypos = 0.0;
341:
342: /* Verifiy if scrollbar is in correct superior boundary */
343: b = (int) (scrollbar_Ypos * ((float)entries/(float)(SGFS_NUMENTRIES-2)) + 0.5);
344: if (b+SGFS_NUMENTRIES >= entries) {
345: ypos = entries - SGFS_NUMENTRIES;
346: DlgFileSelect_Convert_ypos_to_scrollbar_Ypos();
347: }
348:
1.1.1.13 root 349: refreshentries = true;
1.1.1.9 root 350: }
1.1.1.2 root 351:
352: /*-----------------------------------------------------------------------*/
1.1.1.8 root 353: /**
354: * Handle SDL events.
355: */
1.1.1.2 root 356: static void DlgFileSelect_HandleSdlEvents(SDL_Event *pEvent)
357: {
1.1.1.4 root 358: int oldypos = ypos;
1.1.1.2 root 359: switch (pEvent->type)
360: {
1.1.1.13 root 361: #if WITH_SDL2
362: case SDL_MOUSEWHEEL:
363: if (pEvent->wheel.y>0)
364: DlgFileSelect_ScrollUp();
365: else if (pEvent->wheel.y<0)
366: DlgFileSelect_ScrollDown();
367: break;
368: #else
1.1.1.2 root 369: case SDL_MOUSEBUTTONDOWN:
370: if (pEvent->button.button == SDL_BUTTON_WHEELUP)
371: DlgFileSelect_ScrollUp();
372: else if (pEvent->button.button == SDL_BUTTON_WHEELDOWN)
373: DlgFileSelect_ScrollDown();
374: break;
1.1.1.13 root 375: #endif
376: case SDL_KEYDOWN:
1.1.1.2 root 377: switch (pEvent->key.keysym.sym)
378: {
1.1.1.9 root 379: case SDLK_UP:
380: DlgFileSelect_ScrollUp();
381: break;
382: case SDLK_DOWN:
383: DlgFileSelect_ScrollDown();
384: break;
385: case SDLK_HOME:
1.1.1.13 root 386: ypos = 0;
1.1.1.9 root 387: DlgFileSelect_Convert_ypos_to_scrollbar_Ypos();
388: break;
389: case SDLK_END:
1.1.1.13 root 390: ypos = entries-SGFS_NUMENTRIES;
1.1.1.9 root 391: DlgFileSelect_Convert_ypos_to_scrollbar_Ypos();
392: break;
393: case SDLK_PAGEUP:
394: ypos -= SGFS_NUMENTRIES;
395: DlgFileSelect_Convert_ypos_to_scrollbar_Ypos();
396: break;
1.1.1.2 root 397: case SDLK_PAGEDOWN:
398: if (ypos+2*SGFS_NUMENTRIES < entries)
399: ypos += SGFS_NUMENTRIES;
400: else
401: ypos = entries-SGFS_NUMENTRIES;
1.1.1.9 root 402: DlgFileSelect_Convert_ypos_to_scrollbar_Ypos();
1.1.1.2 root 403: break;
1.1.1.4 root 404: default:
405: break;
1.1.1.2 root 406: }
407: break;
1.1.1.4 root 408: default:
409: break;
410: }
1.1.1.13 root 411:
1.1.1.9 root 412: if (ypos < 0) {
1.1.1.4 root 413: ypos = 0;
1.1.1.9 root 414: scrollbar_Ypos = 0.0;
415: }
1.1.1.13 root 416:
1.1.1.4 root 417: if (ypos != oldypos)
1.1.1.7 root 418: refreshentries = true;
1.1.1.4 root 419: }
420:
421:
422: /*-----------------------------------------------------------------------*/
1.1.1.8 root 423: /**
424: * Free file entries
425: */
1.1.1.4 root 426: static struct dirent **files_free(struct dirent **files)
427: {
428: int i;
429: if (files != NULL)
430: {
431: for(i=0; i<entries; i++)
432: {
433: free(files[i]);
434: }
435: free(files);
436: }
437: return NULL;
438: }
439:
440:
441: /*-----------------------------------------------------------------------*/
1.1.1.8 root 442: /**
443: * Copy to dst src+add if they are below maxlen and return true,
444: * otherwise return false
445: */
1.1.1.4 root 446: static int strcat_maxlen(char *dst, int maxlen, const char *src, const char *add)
447: {
448: int slen, alen;
449: slen = strlen(src);
450: alen = strlen(add);
451: if (slen + alen < maxlen)
452: {
453: strcpy(dst, src);
454: strcpy(dst+slen, add);
455: return 1;
456: }
457: return 0;
458: }
459:
460: /*-----------------------------------------------------------------------*/
1.1.1.8 root 461: /**
1.1.1.13 root 462: * Get given file's type, directory or a file.
463: * (if name itself is symlink, stat() checks file it points to)
464: */
465: static int get_dtype(const char *name)
466: {
467: struct stat buf;
468: char path[FILENAME_MAX];
469:
470: snprintf(path, sizeof(path), "%s%c%s", dirpath, PATHSEP, name);
471: if (stat(path, &buf) == 0 && S_ISDIR(buf.st_mode))
472: return DT_DIR;
473: else
474: return DT_REG;
475: }
476:
477: /*-----------------------------------------------------------------------*/
478: /**
479: * Case insensitive sorting for directory entry names, so
480: * that directory entries are listed first.
481: */
482: static int filesort(const struct dirent **d1, const struct dirent **d2)
483: {
484: const char *name1 = (*d1)->d_name;
485: const char *name2 = (*d2)->d_name;
486: #ifndef HAVE_DIRENT_D_TYPE
487: int type1 = DT_UNKNOWN;
488: int type2 = DT_UNKNOWN;
489: #else
490: int type1 = (*d1)->d_type;
491: int type2 = (*d2)->d_type;
492: #endif
493:
494: /* OS / file system that doesn't support d_type field, or symlink */
495: if (type1 == DT_UNKNOWN || type1 == DT_LNK)
496: type1 = get_dtype(name1);
497: if (type2 == DT_UNKNOWN || type2 == DT_LNK)
498: type2 = get_dtype(name2);
499:
500: if (type1 == DT_DIR)
501: {
502: if (type2 != DT_DIR)
503: return -1;
504: } else if (type2 == DT_DIR)
505: {
506: if (type1 != DT_DIR)
507: return 1;
508: }
509: return strcasecmp(name1, name2);
510: }
511:
512: /*-----------------------------------------------------------------------*/
513: /**
1.1.1.8 root 514: * Create and return suitable path into zip file
515: */
1.1.1.4 root 516: static char* zip_get_path(const char *zipdir, const char *zipfilename, int browsingzip)
517: {
518: if (browsingzip)
519: {
520: char *zippath;
521: zippath = malloc(strlen(zipdir) + strlen(zipfilename) + 1);
522: strcpy(zippath, zipdir);
523: strcat(zippath, zipfilename);
524: return zippath;
1.1.1.2 root 525: }
1.1.1.4 root 526: return strdup("");
1.1.1.2 root 527: }
528:
1.1.1.8 root 529: /**
530: * string for zip root needs to be empty, check and correct if needed
531: */
1.1.1.4 root 532: static void correct_zip_root(char *zippath)
533: {
534: if (zippath[0] == PATHSEP && !zippath[1])
535: {
536: zippath[0] = '\0';
537: }
538: }
1.1.1.2 root 539:
1.1.1.9 root 540: /**
541: * Convert Ypos to Y scrollbar position
542: */
543: static void DlgFileSelect_Convert_ypos_to_scrollbar_Ypos(void)
544: {
545: if (entries <= SGFS_NUMENTRIES)
546: scrollbar_Ypos = 0.0;
547: else
548: scrollbar_Ypos = (float)ypos / ((float)entries/(float)(SGFS_NUMENTRIES-2));
549: }
550:
1.1.1.2 root 551: /*-----------------------------------------------------------------------*/
1.1.1.8 root 552: /**
553: * Show and process a file selection dialog.
554: * Returns path/name user selected or NULL if user canceled
555: * input: zip_path = pointer's pointer to buffer to contain file path
556: * within a selected zip file, or NULL if browsing zip files is disallowed.
557: * bAllowNew: true if the user is allowed to insert new file names.
558: */
1.1.1.13 root 559: char* SDLGui_FileSelect(const char *title, const char *path_and_name, char **zip_path, bool bAllowNew)
1.1 root 560: {
561: struct dirent **files = NULL;
562: char *pStringMem;
1.1.1.10 root 563: char *retpath = NULL;
1.1.1.8 root 564: const char *home;
565: char *path, *fname; /* The actual file and path names */
1.1.1.7 root 566: bool reloaddir = true; /* Do we have to reload the directory file list? */
1.1.1.13 root 567: int retbut, len;
1.1.1.10 root 568: bool bOldMouseVisibility;
1.1.1.9 root 569: int selection; /* The selection index */
1.1 root 570: char *zipfilename; /* Filename in zip file */
571: char *zipdir;
1.1.1.7 root 572: bool browsingzip = false; /* Are we browsing an archive? */
1.1 root 573: zip_dir *zipfiles = NULL;
1.1.1.2 root 574: SDL_Event sdlEvent;
1.1.1.13 root 575: int yScrollbar_size; /* Size of the vertical scrollbar */
576: union {
577: char *mtxt;
578: const char *ctxt;
579: } dlgtitle; /* A hack to silent recent GCCs warnings */
580: bool KeepCurrentObject;
581:
582: dlgtitle.ctxt = title;
1.1.1.11 root 583:
584: /* If this is the first call to SDLGui_FileSelect, we reset scrollbar_Ypos and ypos */
585: /* Else, we keep the previous value of scrollbar_Ypos and update ypos below, to open */
586: /* the fileselector at the same position it was used */
587: if ( ypos < 0 )
588: {
589: scrollbar_Ypos = 0.0;
590: ypos = 0;
591: }
1.1.1.2 root 592:
1.1.1.7 root 593: refreshentries = true;
1.1.1.2 root 594: entries = 0;
1.1 root 595:
596: /* Allocate memory for the file and path name strings: */
597: pStringMem = malloc(4 * FILENAME_MAX);
598: path = pStringMem;
599: fname = pStringMem + FILENAME_MAX;
1.1.1.4 root 600: zipdir = pStringMem + 2 * FILENAME_MAX;
601: zipfilename = pStringMem + 3 * FILENAME_MAX;
1.1 root 602: zipfilename[0] = 0;
1.1.1.5 root 603: fname[0] = 0;
604: path[0] = 0;
1.1 root 605:
1.1.1.13 root 606: len = strlen(title);
607: fsdlg[SGFSDLG_TITLE].txt = dlgtitle.mtxt;
608: fsdlg[SGFSDLG_TITLE].x = TITLE_OFFSET + (TITLE_MAXLEN-len)/2;
609: fsdlg[SGFSDLG_TITLE].w = len;
610:
1.1.1.10 root 611: /* Save mouse state and enable cursor */
612: bOldMouseVisibility = SDL_ShowCursor(SDL_QUERY);
613: SDL_ShowCursor(SDL_ENABLE);
614:
1.1 root 615: SDLGui_CenterDlg(fsdlg);
616: if (bAllowNew)
617: {
618: fsdlg[SGFSDLG_FILENAME].type = SGEDITFIELD;
619: fsdlg[SGFSDLG_FILENAME].flags |= SG_EXIT;
620: }
621: else
622: {
623: fsdlg[SGFSDLG_FILENAME].type = SGTEXT;
624: fsdlg[SGFSDLG_FILENAME].flags &= ~SG_EXIT;
625: }
626:
627: /* Prepare the path and filename variables */
1.1.1.4 root 628: if (path_and_name && path_and_name[0])
1.1.1.2 root 629: {
1.1.1.4 root 630: strncpy(path, path_and_name, FILENAME_MAX);
631: path[FILENAME_MAX-1] = '\0';
632: }
1.1.1.5 root 633: if (!File_DirExists(path))
1.1.1.4 root 634: {
1.1.1.5 root 635: File_SplitPath(path, path, fname, NULL);
636: if (!(File_DirExists(path) || getcwd(path, FILENAME_MAX)))
1.1.1.4 root 637: {
1.1.1.5 root 638: perror("SDLGui_FileSelect: non-existing path and CWD failed");
1.1.1.10 root 639: goto clean_exit;
1.1.1.4 root 640: }
1.1.1.2 root 641: }
1.1.1.5 root 642:
1.1.1.2 root 643: File_MakeAbsoluteName(path);
644: File_MakeValidPathName(path);
1.1 root 645: File_ShrinkName(dlgpath, path, DLGPATH_SIZE);
646: File_ShrinkName(dlgfname, fname, DLGFNAME_SIZE);
647:
1.1.1.13 root 648: /* The first time we display the dialog, we reset the current position */
649: /* On next calls, current_object's value will be kept to handle scrolling */
650: KeepCurrentObject = false;
651:
1.1 root 652: do
653: {
654: if (reloaddir)
655: {
1.1.1.4 root 656: files = files_free(files);
1.1 root 657:
658: if (browsingzip)
659: {
660: files = ZIP_GetFilesDir(zipfiles, zipdir, &entries);
1.1.1.8 root 661: if(!files)
662: {
1.1.1.15! root 663: Log_Printf(LOG_WARN, "SDLGui_FileSelect: ZIP_GetFilesDir() error!\n");
1.1.1.10 root 664: goto clean_exit;
1.1.1.8 root 665: }
1.1 root 666: }
667: else
668: {
1.1.1.13 root 669: /* for get_dtype() */
670: dirpath = path;
1.1 root 671: /* Load directory entries: */
1.1.1.13 root 672: entries = scandir(path, &files, NULL, filesort);
1.1 root 673: }
1.1.1.13 root 674:
1.1.1.3 root 675: /* Remove hidden files from the list if necessary: */
676: if (!(fsdlg[SGFSDLG_SHOWHIDDEN].state & SG_SELECTED))
677: {
678: DlgFileSelect_RemoveHiddenFiles(files);
679: }
1.1 root 680:
681: if (entries < 0)
682: {
1.1.1.15! root 683: Log_Printf(LOG_WARN, "SDLGui_FileSelect: Path not found.\n");
1.1.1.10 root 684: goto clean_exit;
1.1 root 685: }
686:
1.1.1.4 root 687: /* reload always implies refresh */
1.1.1.7 root 688: reloaddir = false;
689: refreshentries = true;
1.1.1.13 root 690:
691: /* Check if focus was in list - if yes then reset to first entry */
692: DlgFileSelect_ResetFocus();
1.1 root 693: }/* reloaddir */
694:
1.1.1.9 root 695: /* Refresh scrollbar size */
1.1.1.13 root 696: if (entries <= SGFS_NUMENTRIES)
1.1.1.11 root 697: yScrollbar_size = (SGFS_NUMENTRIES-2) * sdlgui_fontheight;
1.1.1.9 root 698: else
1.1.1.11 root 699: {
700: yScrollbar_size = (int)((SGFS_NUMENTRIES-2) / ((float)entries/(float)SGFS_NUMENTRIES) * sdlgui_fontheight);
701: if ( yScrollbar_size < SCROLLBAR_MIN_HEIGHT ) /* Value could be 0 for very large directory */
702: yScrollbar_size = SCROLLBAR_MIN_HEIGHT;
703: }
704: fsdlg[SGFSDLG_SCROLLBAR].w = yScrollbar_size;
705:
1.1.1.9 root 706: /* Refresh scrolbar pos */
707: ypos = (int) (scrollbar_Ypos * ((float)entries/(float)(SGFS_NUMENTRIES-2)) + 0.5);
1.1.1.11 root 708:
709: if (ypos+SGFS_NUMENTRIES >= entries) { /* Ensure Y pos is in the correct boundaries */
710: ypos = entries - SGFS_NUMENTRIES;
711: if ( ypos < 0 )
712: ypos = 0;
713: DlgFileSelect_Convert_ypos_to_scrollbar_Ypos();
714: }
715: fsdlg[SGFSDLG_SCROLLBAR].h = (int) (scrollbar_Ypos * sdlgui_fontheight);
716:
1.1 root 717: /* Update the file name strings in the dialog? */
718: if (refreshentries)
719: {
1.1.1.2 root 720: if (!DlgFileSelect_RefreshEntries(files, path, browsingzip))
1.1 root 721: {
1.1.1.10 root 722: goto clean_exit;
1.1 root 723: }
1.1.1.7 root 724: refreshentries = false;
1.1 root 725: }
726:
727: /* Show dialog: */
1.1.1.13 root 728: retbut = SDLGui_DoDialog(fsdlg, &sdlEvent, KeepCurrentObject);
729: KeepCurrentObject = true; /* Don't reset current_object for next calls */
1.1 root 730:
731: /* Has the user clicked on a file or folder? */
1.1.1.9 root 732: if (retbut>=SGFSDLG_ENTRYFIRST && retbut<=SGFSDLG_ENTRYLAST && retbut-SGFSDLG_ENTRYFIRST+ypos<entries)
1.1 root 733: {
734: char *tempstr;
1.1.1.13 root 735:
1.1 root 736: tempstr = malloc(FILENAME_MAX);
737: if (!tempstr)
738: {
739: perror("Error while allocating temporary memory in SDLGui_FileSelect()");
1.1.1.10 root 740: goto clean_exit;
1.1 root 741: }
742:
1.1.1.7 root 743: if (browsingzip == true)
1.1 root 744: {
1.1.1.4 root 745: if (!strcat_maxlen(tempstr, FILENAME_MAX,
1.1.1.9 root 746: zipdir, files[retbut-SGFSDLG_ENTRYFIRST+ypos]->d_name))
1.1.1.4 root 747: {
1.1.1.15! root 748: Log_Printf(LOG_WARN, "SDLGui_FileSelect: Path name too long!\n");
1.1.1.12 root 749: free(tempstr);
1.1.1.10 root 750: goto clean_exit;
1.1.1.4 root 751: }
752: /* directory? */
1.1.1.3 root 753: if (File_DoesFileNameEndWithSlash(tempstr))
1.1 root 754: {
755: /* handle the ../ directory */
1.1.1.9 root 756: if (strcmp(files[retbut-SGFSDLG_ENTRYFIRST+ypos]->d_name, "../") == 0)
1.1 root 757: {
758: /* close the zip file */
1.1.1.4 root 759: if (strcmp(tempstr, "../") == 0)
1.1 root 760: {
761: /* free zip file entries */
762: ZIP_FreeZipDir(zipfiles);
763: zipfiles = NULL;
764: /* Copy the path name to the dialog */
765: File_ShrinkName(dlgpath, path, DLGPATH_SIZE);
1.1.1.7 root 766: browsingzip = false;
1.1 root 767: }
768: else
769: {
1.1.1.4 root 770: /* remove "../" and previous dir from path */
771: File_PathShorten(tempstr, 2);
772: correct_zip_root(tempstr);
1.1 root 773: strcpy(zipdir, tempstr);
774: File_ShrinkName(dlgpath, zipdir, DLGPATH_SIZE);
775: }
776: }
777: else /* not the "../" directory */
778: {
779: strcpy(zipdir, tempstr);
780: File_ShrinkName(dlgpath, zipdir, DLGPATH_SIZE);
781: }
1.1.1.7 root 782: reloaddir = true;
1.1 root 783: /* Copy the path name to the dialog */
784: zipfilename[0] = '\0';
785: dlgfname[0] = 0;
786: ypos = 0;
1.1.1.9 root 787: scrollbar_Ypos = 0.0;
1.1 root 788: }
789: else
790: {
1.1.1.4 root 791: /* not dir, select a file in the zip */
1.1.1.9 root 792: selection = retbut-SGFSDLG_ENTRYFIRST+ypos;
1.1 root 793: strcpy(zipfilename, files[selection]->d_name);
794: File_ShrinkName(dlgfname, zipfilename, DLGFNAME_SIZE);
795: }
796:
1.1.1.4 root 797: }
798: else /* not browsingzip */
1.1 root 799: {
1.1.1.4 root 800: if (!strcat_maxlen(tempstr, FILENAME_MAX,
1.1.1.9 root 801: path, files[retbut-SGFSDLG_ENTRYFIRST+ypos]->d_name))
1.1 root 802: {
1.1.1.15! root 803: Log_Printf(LOG_WARN, "SDLGui_FileSelect: Path name too long!\n");
1.1.1.12 root 804: free(tempstr);
1.1.1.10 root 805: goto clean_exit;
1.1.1.4 root 806: }
1.1.1.5 root 807: if (File_DirExists(tempstr))
1.1.1.4 root 808: {
809: File_HandleDotDirs(tempstr);
810: File_AddSlashToEndFileName(tempstr);
811: /* Copy the path name to the dialog */
812: File_ShrinkName(dlgpath, tempstr, DLGPATH_SIZE);
1.1 root 813: strcpy(path, tempstr);
1.1.1.7 root 814: reloaddir = true;
1.1 root 815: dlgfname[0] = 0;
816: ypos = 0;
1.1.1.9 root 817: scrollbar_Ypos = 0.0;
1.1 root 818: }
819: else if (ZIP_FileNameIsZIP(tempstr) && zip_path != NULL)
820: {
821: /* open a zip file */
822: zipfiles = ZIP_GetFiles(tempstr);
1.1.1.7 root 823: if (zipfiles != NULL && browsingzip == false)
1.1 root 824: {
1.1.1.9 root 825: selection = retbut-SGFSDLG_ENTRYFIRST+ypos;
1.1 root 826: strcpy(fname, files[selection]->d_name);
827: File_ShrinkName(dlgfname, fname, DLGFNAME_SIZE);
1.1.1.7 root 828: browsingzip = true;
1.1.1.4 root 829: zipdir[0] = '\0'; /* zip root */
1.1 root 830: File_ShrinkName(dlgpath, zipdir, DLGPATH_SIZE);
1.1.1.7 root 831: reloaddir = true;
1.1 root 832: ypos = 0;
1.1.1.9 root 833: scrollbar_Ypos = 0.0;
1.1 root 834: }
835:
836: }
837: else
838: {
839: /* Select a file */
1.1.1.9 root 840: selection = retbut-SGFSDLG_ENTRYFIRST+ypos;
1.1 root 841: strcpy(fname, files[selection]->d_name);
842: File_ShrinkName(dlgfname, fname, DLGFNAME_SIZE);
843: }
844:
845: } /* not browsingzip */
846:
847: free(tempstr);
848: }
849: else /* Has the user clicked on another button? */
850: {
851: switch(retbut)
852: {
853: case SGFSDLG_UPDIR: /* Change path to parent directory */
1.1.1.4 root 854: if (browsingzip)
1.1 root 855: {
1.1.1.4 root 856: /* close the zip file? */
857: if (!zipdir[0])
1.1 root 858: {
859: /* free zip file entries */
860: ZIP_FreeZipDir(zipfiles);
1.1.1.7 root 861: browsingzip = false;
1.1 root 862: zipfiles = NULL;
863: File_ShrinkName(dlgpath, path, DLGPATH_SIZE);
864: }
865: else
866: {
1.1.1.4 root 867: /* remove last dir from zipdir path */
868: File_PathShorten(zipdir, 1);
869: correct_zip_root(zipdir);
1.1 root 870: File_ShrinkName(dlgpath, zipdir, DLGPATH_SIZE);
871: zipfilename[0] = '\0';
872: }
873: } /* not a zip file: */
1.1.1.4 root 874: else
1.1 root 875: {
1.1.1.4 root 876: File_PathShorten(path, 1);
877: File_ShrinkName(dlgpath, path, DLGPATH_SIZE);
1.1 root 878: }
1.1.1.7 root 879: reloaddir = true;
1.1 root 880: break;
1.1.1.3 root 881:
882: case SGFSDLG_HOMEDIR: /* Change to home directory */
1.1.1.11 root 883: case SGFSDLG_CWD: /* Change to current work directory */
884: if (retbut == SGFSDLG_CWD)
885: home = Paths_GetWorkingDir();
886: else
887: home = Paths_GetUserHome();
1.1.1.8 root 888: if (home == NULL || !*home)
1.1.1.3 root 889: break;
890: if (browsingzip)
891: {
892: /* free zip file entries */
893: ZIP_FreeZipDir(zipfiles);
894: zipfiles = NULL;
1.1.1.7 root 895: browsingzip = false;
1.1.1.3 root 896: }
1.1.1.4 root 897: strcpy(path, home);
1.1.1.3 root 898: File_AddSlashToEndFileName(path);
1.1.1.4 root 899: File_ShrinkName(dlgpath, path, DLGPATH_SIZE);
1.1.1.7 root 900: reloaddir = true;
1.1.1.3 root 901: break;
902:
1.1 root 903: case SGFSDLG_ROOTDIR: /* Change to root directory */
1.1.1.4 root 904: if (browsingzip)
1.1 root 905: {
906: /* free zip file entries */
907: ZIP_FreeZipDir(zipfiles);
908: zipfiles = NULL;
1.1.1.7 root 909: browsingzip = false;
1.1 root 910: }
1.1.1.7 root 911: path[0] = PATHSEP; path[1] = '\0';
1.1 root 912: strcpy(dlgpath, path);
1.1.1.7 root 913: reloaddir = true;
1.1 root 914: break;
915: case SGFSDLG_UP: /* Scroll up */
1.1.1.2 root 916: DlgFileSelect_ScrollUp();
917: SDL_Delay(10);
1.1 root 918: break;
919: case SGFSDLG_DOWN: /* Scroll down */
1.1.1.2 root 920: DlgFileSelect_ScrollDown();
921: SDL_Delay(10);
1.1 root 922: break;
1.1.1.9 root 923: case SGFSDLG_SCROLLBAR: /* Scrollbar selected */
924: DlgFileSelect_ManageScrollbar();
925: SDL_Delay(10);
926: break;
1.1 root 927: case SGFSDLG_FILENAME: /* User entered new filename */
928: strcpy(fname, dlgfname);
929: break;
1.1.1.3 root 930: case SGFSDLG_SHOWHIDDEN: /* Show/hide hidden files */
1.1.1.7 root 931: reloaddir = true;
1.1.1.3 root 932: ypos = 0;
1.1.1.9 root 933: scrollbar_Ypos = 0.0;
1.1.1.3 root 934: break;
1.1.1.2 root 935: case SDLGUI_UNKNOWNEVENT:
936: DlgFileSelect_HandleSdlEvents(&sdlEvent);
937: break;
1.1 root 938: } /* switch */
1.1.1.13 root 939:
1.1.1.4 root 940: if (reloaddir)
941: {
942: /* Remove old selection */
943: fname[0] = 0;
944: dlgfname[0] = 0;
945: ypos = 0;
1.1.1.9 root 946: scrollbar_Ypos = 0.0;
1.1.1.4 root 947: }
1.1 root 948: } /* other button code */
949:
950:
951: } /* do */
1.1.1.4 root 952: while (retbut!=SGFSDLG_OKAY && retbut!=SGFSDLG_CANCEL
953: && retbut!=SDLGUI_QUIT && retbut != SDLGUI_ERROR && !bQuitProgram);
1.1 root 954:
1.1.1.4 root 955: if (retbut == SGFSDLG_OKAY)
1.1 root 956: {
1.1.1.4 root 957: if (zip_path)
958: *zip_path = zip_get_path(zipdir, zipfilename, browsingzip);
959: retpath = File_MakePath(path, fname, NULL);
960: }
961: else
962: retpath = NULL;
1.1.1.13 root 963:
1.1.1.10 root 964: clean_exit:
965: SDL_ShowCursor(bOldMouseVisibility);
1.1.1.13 root 966:
967: if (browsingzip && zipfiles != NULL)
968: {
969: /* free zip file entries */
970: ZIP_FreeZipDir(zipfiles);
971: zipfiles = NULL;
972: }
973: files_free(files);
1.1.1.4 root 974: free(pStringMem);
1.1.1.13 root 975:
1.1.1.4 root 976: return retpath;
977: }
978:
979:
980: /*-----------------------------------------------------------------------*/
1.1.1.8 root 981: /**
982: * Let user browse for a file, confname is used as default.
1.1.1.4 root 983: * If bAllowNew is true, user can select new files also.
1.1.1.13 root 984: *
1.1.1.4 root 985: * If no file is selected, or there's some problem with the file,
1.1.1.7 root 986: * return false and clear dlgname & confname.
987: * Otherwise return true, set dlgname & confname to the new file name
1.1.1.11 root 988: * (dlgname is shrunken & limited to maxlen and confname is assumed
1.1.1.4 root 989: * to have FILENAME_MAX amount of space).
990: */
1.1.1.13 root 991: bool SDLGui_FileConfSelect(const char *title, char *dlgname, char *confname, int maxlen, bool bAllowNew)
1.1.1.4 root 992: {
993: char *selname;
1.1.1.13 root 994:
995: selname = SDLGui_FileSelect(title, confname, NULL, bAllowNew);
1.1.1.4 root 996: if (selname)
997: {
998: if (!File_DoesFileNameEndWithSlash(selname) &&
999: (bAllowNew || File_Exists(selname)))
1.1 root 1000: {
1.1.1.4 root 1001: strncpy(confname, selname, FILENAME_MAX);
1002: confname[FILENAME_MAX-1] = '\0';
1003: File_ShrinkName(dlgname, selname, maxlen);
1.1 root 1004: }
1005: else
1.1.1.4 root 1006: {
1007: dlgname[0] = confname[0] = 0;
1008: }
1009: free(selname);
1.1.1.7 root 1010: return true;
1.1 root 1011: }
1.1.1.7 root 1012: return false;
1.1 root 1013: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.