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