|
|
1.1 root 1: /*
2: Hatari - dlgFileSelect.c
3:
4: This file is distributed under the GNU Public License, version 2 or at
5: your option any later version. Read the file gpl.txt for details.
6:
7: A file selection dialog for the graphical user interface for Hatari.
8: */
1.1.1.2 ! root 9: char DlgFileSelect_rcsid[] = "Hatari $Id: dlgFileSelect.c,v 1.9 2005/03/07 23:15:50 thothy Exp $";
1.1 root 10:
11: #include <SDL.h>
12: #include <sys/stat.h>
13: #include <unistd.h>
14: #include <dirent.h>
15:
16: #include "main.h"
17: #include "sdlgui.h"
18: #include "file.h"
19: #include "zip.h"
20:
21:
1.1.1.2 ! root 22: #define SGFS_NUMENTRIES 16 /* How many entries are displayed at once */
! 23:
! 24:
1.1 root 25: #define SGFSDLG_FILENAME 5
26: #define SGFSDLG_UPDIR 6
27: #define SGFSDLG_ROOTDIR 7
28: #define SGFSDLG_ENTRY1 10
29: #define SGFSDLG_ENTRY16 25
30: #define SGFSDLG_UP 26
31: #define SGFSDLG_DOWN 27
32: #define SGFSDLG_OKAY 28
33: #define SGFSDLG_CANCEL 29
34:
35:
36: #define DLGPATH_SIZE 62
37: static char dlgpath[DLGPATH_SIZE+1]; /* Path name in the dialog */
38:
39: #define DLGFNAME_SIZE 56
40: static char dlgfname[DLGFNAME_SIZE+1]; /* Name of the selected file in the dialog */
41:
42: #define DLGFILENAMES_SIZE 59
1.1.1.2 ! root 43: static char dlgfilenames[SGFS_NUMENTRIES][DLGFILENAMES_SIZE+1]; /* Visible file names in the dialog */
1.1 root 44:
45: /* The dialog data: */
46: static SGOBJ fsdlg[] =
47: {
48: { SGBOX, 0, 0, 0,0, 64,25, NULL },
49: { SGTEXT, 0, 0, 25,1, 13,1, "Choose a file" },
50: { SGTEXT, 0, 0, 1,2, 7,1, "Folder:" },
51: { SGTEXT, 0, 0, 1,3, DLGPATH_SIZE,1, dlgpath },
52: { SGTEXT, 0, 0, 1,4, 6,1, "File:" },
53: { SGTEXT, 0, 0, 7,4, DLGFNAME_SIZE,1, dlgfname },
54: { SGBUTTON, 0, 0, 55,1, 4,1, ".." },
55: { SGBUTTON, 0, 0, 60,1, 3,1, "/" },
56: { SGBOX, 0, 0, 1,6, 62,16, NULL },
57: { SGBOX, 0, 0, 62,7, 1,14, NULL },
58: { SGTEXT, SG_EXIT, 0, 2,6, DLGFILENAMES_SIZE,1, dlgfilenames[0] },
59: { SGTEXT, SG_EXIT, 0, 2,7, DLGFILENAMES_SIZE,1, dlgfilenames[1] },
60: { SGTEXT, SG_EXIT, 0, 2,8, DLGFILENAMES_SIZE,1, dlgfilenames[2] },
61: { SGTEXT, SG_EXIT, 0, 2,9, DLGFILENAMES_SIZE,1, dlgfilenames[3] },
62: { SGTEXT, SG_EXIT, 0, 2,10, DLGFILENAMES_SIZE,1, dlgfilenames[4] },
63: { SGTEXT, SG_EXIT, 0, 2,11, DLGFILENAMES_SIZE,1, dlgfilenames[5] },
64: { SGTEXT, SG_EXIT, 0, 2,12, DLGFILENAMES_SIZE,1, dlgfilenames[6] },
65: { SGTEXT, SG_EXIT, 0, 2,13, DLGFILENAMES_SIZE,1, dlgfilenames[7] },
66: { SGTEXT, SG_EXIT, 0, 2,14, DLGFILENAMES_SIZE,1, dlgfilenames[8] },
67: { SGTEXT, SG_EXIT, 0, 2,15, DLGFILENAMES_SIZE,1, dlgfilenames[9] },
68: { SGTEXT, SG_EXIT, 0, 2,16, DLGFILENAMES_SIZE,1, dlgfilenames[10] },
69: { SGTEXT, SG_EXIT, 0, 2,17, DLGFILENAMES_SIZE,1, dlgfilenames[11] },
70: { SGTEXT, SG_EXIT, 0, 2,18, DLGFILENAMES_SIZE,1, dlgfilenames[12] },
71: { SGTEXT, SG_EXIT, 0, 2,19, DLGFILENAMES_SIZE,1, dlgfilenames[13] },
72: { SGTEXT, SG_EXIT, 0, 2,20, DLGFILENAMES_SIZE,1, dlgfilenames[14] },
73: { SGTEXT, SG_EXIT, 0, 2,21, DLGFILENAMES_SIZE,1, dlgfilenames[15] },
74: { SGBUTTON, SG_TOUCHEXIT, 0, 62,6, 1,1, "\x01" }, /* Arrow up */
75: { SGBUTTON, SG_TOUCHEXIT, 0, 62,21, 1,1, "\x02" }, /* Arrow down */
76: { SGBUTTON, 0, 0, 14,23, 8,1, "Okay" },
77: { SGBUTTON, 0, 0, 34,23, 8,1, "Cancel" },
78: { -1, 0, 0, 0,0, 0,0, NULL }
79: };
80:
81:
1.1.1.2 ! root 82: static int ypos; /* First entry number to be displayed */
! 83: static BOOL refreshentries; /* Do we have to update the file names in the dialog? */
! 84: static int entries; /* How many files are in the actual directory? */
! 85:
1.1 root 86:
87: /*-----------------------------------------------------------------------*/
88: /*
89: Update the file name strings in the dialog.
90: Returns FALSE if it failed, TRUE on success.
91: */
1.1.1.2 ! root 92: static int DlgFileSelect_RefreshEntries(struct dirent **files, char *path, BOOL browsingzip)
1.1 root 93: {
94: int i;
95: char *tempstr = malloc(FILENAME_MAX);
96:
97: if (!tempstr)
98: {
99: perror("DlgFileSelect_RefreshEntries");
100: return FALSE;
101: }
102:
103: /* Copy entries to dialog: */
1.1.1.2 ! root 104: for(i=0; i<SGFS_NUMENTRIES; i++)
1.1 root 105: {
106: if( i+ypos < entries )
107: {
108: struct stat filestat;
109: /* Prepare entries: */
110: strcpy(tempstr, " ");
111: strcat(tempstr, files[i+ypos]->d_name);
112: File_ShrinkName(dlgfilenames[i], tempstr, DLGFILENAMES_SIZE);
113: /* Mark folders: */
114: strcpy(tempstr, path);
115: strcat(tempstr, files[i+ypos]->d_name);
116:
117: if( browsingzip )
118: {
119: if( tempstr[strlen(tempstr)-1] == '/' )
120: dlgfilenames[i][0] = SGFOLDER; /* Mark folders */
121: }
122: else
123: {
124: if( stat(tempstr, &filestat)==0 && S_ISDIR(filestat.st_mode) )
125: dlgfilenames[i][0] = SGFOLDER; /* Mark folders */
126: if (ZIP_FileNameIsZIP(tempstr) && browsingzip == FALSE)
127: dlgfilenames[i][0] = SGFOLDER; /* Mark .ZIP archives as folders */
128: }
129: }
130: else
131: dlgfilenames[i][0] = 0; /* Clear entry */
132: }
133:
134: free(tempstr);
135: return TRUE;
136: }
137:
138:
139: /*-----------------------------------------------------------------------*/
140: /*
1.1.1.2 ! root 141: Prepare to scroll up one entry.
! 142: */
! 143: static void DlgFileSelect_ScrollUp(void)
! 144: {
! 145: if (ypos > 0)
! 146: {
! 147: --ypos;
! 148: refreshentries = TRUE;
! 149: }
! 150: }
! 151:
! 152:
! 153: /*-----------------------------------------------------------------------*/
! 154: /*
! 155: Prepare to scroll down one entry.
! 156: */
! 157: static void DlgFileSelect_ScrollDown(void)
! 158: {
! 159: if (ypos+SGFS_NUMENTRIES < entries)
! 160: {
! 161: ++ypos;
! 162: refreshentries = TRUE;
! 163: }
! 164: }
! 165:
! 166:
! 167: /*-----------------------------------------------------------------------*/
! 168: /*
! 169: Handle SDL events.
! 170: */
! 171: static void DlgFileSelect_HandleSdlEvents(SDL_Event *pEvent)
! 172: {
! 173: switch (pEvent->type)
! 174: {
! 175: case SDL_MOUSEBUTTONDOWN:
! 176: if (pEvent->button.button == SDL_BUTTON_WHEELUP)
! 177: DlgFileSelect_ScrollUp();
! 178: else if (pEvent->button.button == SDL_BUTTON_WHEELDOWN)
! 179: DlgFileSelect_ScrollDown();
! 180: break;
! 181: case SDL_KEYDOWN:
! 182: switch (pEvent->key.keysym.sym)
! 183: {
! 184: case SDLK_UP: DlgFileSelect_ScrollUp(); break;
! 185: case SDLK_DOWN: DlgFileSelect_ScrollDown(); break;
! 186: case SDLK_HOME: ypos = 0; refreshentries = TRUE; break;
! 187: case SDLK_END: ypos = entries-SGFS_NUMENTRIES; refreshentries = TRUE; break;
! 188: case SDLK_PAGEUP:
! 189: if (ypos > SGFS_NUMENTRIES)
! 190: ypos -= SGFS_NUMENTRIES;
! 191: else
! 192: ypos = 0;
! 193: refreshentries = TRUE;
! 194: break;
! 195: case SDLK_PAGEDOWN:
! 196: if (ypos+2*SGFS_NUMENTRIES < entries)
! 197: ypos += SGFS_NUMENTRIES;
! 198: else
! 199: ypos = entries-SGFS_NUMENTRIES;
! 200: refreshentries = TRUE;
! 201: break;
! 202: default: break;
! 203: }
! 204: break;
! 205: }
! 206: }
! 207:
! 208:
! 209: /*-----------------------------------------------------------------------*/
! 210: /*
1.1 root 211: Show and process a file selection dialog.
212: Returns TRUE if the use selected "okay", FALSE if "cancel".
213: input: zip_path = pointer to buffer to contain file path within a selected
214: zip file, or NULL if browsing zip files is disallowed.
215: bAllowNew: TRUE if the user is allowed to insert new file names.
216: */
217: int SDLGui_FileSelect(char *path_and_name, char *zip_path, BOOL bAllowNew)
218: {
219: int i,n;
220: struct dirent **files = NULL;
221: char *pStringMem;
222: char *path, *fname; /* The actual file and path names */
223: BOOL reloaddir = TRUE; /* Do we have to reload the directory file list? */
224: int retbut;
225: int oldcursorstate;
226: int selection = -1; /* The actual selection, -1 if none selected */
227: char *zipfilename; /* Filename in zip file */
228: char *zipdir;
229: BOOL browsingzip = FALSE; /* Are we browsing an archive? */
230: zip_dir *zipfiles = NULL;
1.1.1.2 ! root 231: SDL_Event sdlEvent;
! 232: struct stat filestat;
! 233:
! 234: ypos = 0;
! 235: refreshentries = TRUE;
! 236: entries = 0;
1.1 root 237:
238: /* Allocate memory for the file and path name strings: */
239: pStringMem = malloc(4 * FILENAME_MAX);
240: path = pStringMem;
241: fname = pStringMem + FILENAME_MAX;
242: zipfilename = pStringMem + 2 * FILENAME_MAX;
243: zipdir = pStringMem + 3 * FILENAME_MAX;
244:
245: zipfilename[0] = 0;
246:
247: SDLGui_CenterDlg(fsdlg);
248: if (bAllowNew)
249: {
250: fsdlg[SGFSDLG_FILENAME].type = SGEDITFIELD;
251: fsdlg[SGFSDLG_FILENAME].flags |= SG_EXIT;
252: }
253: else
254: {
255: fsdlg[SGFSDLG_FILENAME].type = SGTEXT;
256: fsdlg[SGFSDLG_FILENAME].flags &= ~SG_EXIT;
257: }
258:
259: /* Prepare the path and filename variables */
1.1.1.2 ! root 260: if (stat(path_and_name, &filestat) == 0 && S_ISDIR(filestat.st_mode))
! 261: {
! 262: /* assure that a directory name ends with a '/' */
! 263: File_AddSlashToEndFileName(path_and_name);
! 264: }
1.1 root 265: File_splitpath(path_and_name, path, fname, NULL);
1.1.1.2 ! root 266: File_MakeAbsoluteName(path);
! 267: File_MakeValidPathName(path);
1.1 root 268: File_ShrinkName(dlgpath, path, DLGPATH_SIZE);
269: File_ShrinkName(dlgfname, fname, DLGFNAME_SIZE);
270:
271: /* Save old mouse cursor state and enable cursor anyway */
272: oldcursorstate = SDL_ShowCursor(SDL_QUERY);
273: if (oldcursorstate == SDL_DISABLE)
274: SDL_ShowCursor(SDL_ENABLE);
275:
276: do
277: {
278: if (reloaddir)
279: {
280: if (strlen(path) >= FILENAME_MAX)
281: {
282: fprintf(stderr, "SDLGui_FileSelect: Path name too long!\n");
283: free(pStringMem);
284: return FALSE;
285: }
286:
287: /* Free old allocated memory: */
288: if (files != NULL)
289: {
290: for(i=0; i<entries; i++)
291: {
292: free(files[i]);
293: }
294: free(files);
295: files = NULL;
296: }
297:
298: if (browsingzip)
299: {
300: files = ZIP_GetFilesDir(zipfiles, zipdir, &entries);
301: }
302: else
303: {
304: /* Load directory entries: */
305: entries = scandir(path, &files, 0, alphasort);
306: }
307:
308: if (entries < 0)
309: {
310: fprintf(stderr, "SDLGui_FileSelect: Path not found.\n");
311: free(pStringMem);
312: return FALSE;
313: }
314:
315: reloaddir = FALSE;
316: refreshentries = TRUE;
317: }/* reloaddir */
318:
319: /* Update the file name strings in the dialog? */
320: if (refreshentries)
321: {
1.1.1.2 ! root 322: if (!DlgFileSelect_RefreshEntries(files, path, browsingzip))
1.1 root 323: {
324: free(pStringMem);
325: return FALSE;
326: }
327: refreshentries = FALSE;
328: }
329:
330: /* Show dialog: */
1.1.1.2 ! root 331: retbut = SDLGui_DoDialog(fsdlg, &sdlEvent);
1.1 root 332:
333: /* Has the user clicked on a file or folder? */
334: if( retbut>=SGFSDLG_ENTRY1 && retbut<=SGFSDLG_ENTRY16 && retbut-SGFSDLG_ENTRY1+ypos<entries)
335: {
336: char *tempstr;
337:
338: tempstr = malloc(FILENAME_MAX);
339: if (!tempstr)
340: {
341: perror("Error while allocating temporary memory in SDLGui_FileSelect()");
342: free(pStringMem);
343: return FALSE;
344: }
345:
346: if( browsingzip == TRUE )
347: {
348: strcpy(tempstr, zipdir);
349: strcat(tempstr, files[retbut-SGFSDLG_ENTRY1+ypos]->d_name);
350: if(tempstr[strlen(tempstr)-1] == '/')
351: {
352: /* handle the ../ directory */
353: if(strcmp(files[retbut-SGFSDLG_ENTRY1+ypos]->d_name, "../") == 0)
354: {
355: /* close the zip file */
356: if( strcmp(tempstr, "../") == 0 )
357: {
358: reloaddir = refreshentries = TRUE;
359: /* free zip file entries */
360: ZIP_FreeZipDir(zipfiles);
361: zipfiles = NULL;
362: /* Copy the path name to the dialog */
363: File_ShrinkName(dlgpath, path, DLGPATH_SIZE);
364: browsingzip = FALSE;
365: }
366: else
367: {
368: i=strlen(tempstr)-1;
369: n=0;
370: while(i > 0 && n < 3)
371: if( tempstr[i--] == '/' )
372: n++;
373: if(tempstr[i+1] == '/')
374: tempstr[i+2] = '\0';
375: else
376: tempstr[0] = '\0';
377:
378: strcpy(zipdir, tempstr);
379: File_ShrinkName(dlgpath, zipdir, DLGPATH_SIZE);
380: }
381: }
382: else /* not the "../" directory */
383: {
384: strcpy(zipdir, tempstr);
385: File_ShrinkName(dlgpath, zipdir, DLGPATH_SIZE);
386: }
387: reloaddir = TRUE;
388: /* Copy the path name to the dialog */
389: selection = -1; /* Remove old selection */
390: zipfilename[0] = '\0';
391: dlgfname[0] = 0;
392: ypos = 0;
393:
394: }
395: else
396: {
397: /* Select a file in the zip */
398: selection = retbut-SGFSDLG_ENTRY1+ypos;
399: strcpy(zipfilename, files[selection]->d_name);
400: File_ShrinkName(dlgfname, zipfilename, DLGFNAME_SIZE);
401: }
402:
403: } /* if browsingzip */
404: else
405: {
406: strcpy(tempstr, path);
407: strcat(tempstr, files[retbut-SGFSDLG_ENTRY1+ypos]->d_name);
408: if( stat(tempstr, &filestat)==0 && S_ISDIR(filestat.st_mode) )
409: {
410: /* Set the new directory */
411: strcpy(path, tempstr);
412: if( strlen(path)>=3 )
413: {
414: if(path[strlen(path)-2]=='/' && path[strlen(path)-1]=='.')
415: path[strlen(path)-2] = 0; /* Strip a single dot at the end of the path name */
416: if(path[strlen(path)-3]=='/' && path[strlen(path)-2]=='.' && path[strlen(path)-1]=='.')
417: {
418: /* Handle the ".." folder */
419: char *ptr;
420: if( strlen(path)==3 )
421: path[1] = 0;
422: else
423: {
424: path[strlen(path)-3] = 0;
425: ptr = strrchr(path, '/');
426: if(ptr)
427: *(ptr+1) = 0;
428: }
429: }
430: }
431: File_AddSlashToEndFileName(path);
432: reloaddir = TRUE;
433: /* Copy the path name to the dialog */
434: File_ShrinkName(dlgpath, path, DLGPATH_SIZE);
435: selection = -1; /* Remove old selection */
436: dlgfname[0] = 0;
437: ypos = 0;
438: }
439: else if (ZIP_FileNameIsZIP(tempstr) && zip_path != NULL)
440: {
441: /* open a zip file */
442: zipfiles = ZIP_GetFiles(tempstr);
443: if( zipfiles != NULL && browsingzip == FALSE )
444: {
445: selection = retbut-SGFSDLG_ENTRY1+ypos;
446: strcpy(fname, files[selection]->d_name);
447: File_ShrinkName(dlgfname, fname, DLGFNAME_SIZE);
448: browsingzip=TRUE;
449: strcpy(zipdir, "");
450: File_ShrinkName(dlgpath, zipdir, DLGPATH_SIZE);
451: reloaddir = refreshentries = TRUE;
452: ypos = 0;
453: }
454:
455: }
456: else
457: {
458: /* Select a file */
459: selection = retbut-SGFSDLG_ENTRY1+ypos;
460: strcpy(fname, files[selection]->d_name);
461: File_ShrinkName(dlgfname, fname, DLGFNAME_SIZE);
462: }
463:
464: } /* not browsingzip */
465:
466: free(tempstr);
467: }
468: else /* Has the user clicked on another button? */
469: {
470: switch(retbut)
471: {
472: case SGFSDLG_UPDIR: /* Change path to parent directory */
473:
474: if( browsingzip )
475: {
476: /* close the zip file */
477: if( strcmp(zipdir, "") == 0 )
478: {
479: reloaddir = refreshentries = TRUE;
480: /* free zip file entries */
481: ZIP_FreeZipDir(zipfiles);
482: zipfiles = NULL;
483: /* Copy the path name to the dialog */
484: File_ShrinkName(dlgpath, path, DLGPATH_SIZE);
485: browsingzip = FALSE;
486: reloaddir = TRUE;
487: selection = -1; /* Remove old selection */
488: fname[0] = 0;
489: dlgfname[0] = 0;
490: ypos = 0;
491: }
492: else
493: {
494: i=strlen(zipdir)-1;
495: n=0;
496: while(i > 0 && n < 2)
497: if( zipdir[i--] == '/' )
498: n++;
499: if(zipdir[i+1] == '/')
500: zipdir[i+2] = '\0';
501: else
502: zipdir[0] = '\0';
503:
504: File_ShrinkName(dlgpath, zipdir, DLGPATH_SIZE);
505: reloaddir = TRUE;
506: selection = -1; /* Remove old selection */
507: zipfilename[0] = '\0';
508: dlgfname[0] = 0;
509: ypos = 0;
510: }
511: } /* not a zip file: */
512: else if( strlen(path)>2 )
513: {
514: char *ptr;
515: File_CleanFileName(path);
516: ptr = strrchr(path, '/');
517: if(ptr)
518: *(ptr+1) = 0;
519: File_AddSlashToEndFileName(path);
520: reloaddir = TRUE;
521: File_ShrinkName(dlgpath, path, DLGPATH_SIZE); /* Copy the path name to the dialog */
522: selection = -1; /* Remove old selection */
523: fname[0] = 0;
524: dlgfname[0] = 0;
525: ypos = 0;
526: }
527: break;
528: case SGFSDLG_ROOTDIR: /* Change to root directory */
529: if( browsingzip )
530: {
531: /* free zip file entries */
532: ZIP_FreeZipDir(zipfiles);
533: zipfiles = NULL;
534: browsingzip = FALSE;
535: }
536:
537: strcpy(path, "/");
538: reloaddir = TRUE;
539: strcpy(dlgpath, path);
540: selection = -1; /* Remove old selection */
541: fname[0] = 0;
542: dlgfname[0] = 0;
543: ypos = 0;
544: break;
545: case SGFSDLG_UP: /* Scroll up */
1.1.1.2 ! root 546: DlgFileSelect_ScrollUp();
! 547: SDL_Delay(10);
1.1 root 548: break;
549: case SGFSDLG_DOWN: /* Scroll down */
1.1.1.2 ! root 550: DlgFileSelect_ScrollDown();
! 551: SDL_Delay(10);
1.1 root 552: break;
553: case SGFSDLG_FILENAME: /* User entered new filename */
554: strcpy(fname, dlgfname);
555: break;
1.1.1.2 ! root 556: case SDLGUI_UNKNOWNEVENT:
! 557: DlgFileSelect_HandleSdlEvents(&sdlEvent);
! 558: break;
1.1 root 559: } /* switch */
560: } /* other button code */
561:
562:
563: } /* do */
1.1.1.2 ! root 564: while (retbut!=SGFSDLG_OKAY && retbut!=SGFSDLG_CANCEL && retbut!=SDLGUI_QUIT && !bQuitProgram);
1.1 root 565:
566: if (oldcursorstate == SDL_DISABLE)
567: SDL_ShowCursor(SDL_DISABLE);
568:
569: File_makepath(path_and_name, path, fname, NULL);
570:
571: /* Free old allocated memory: */
572: if (files != NULL)
573: {
574: for(i=0; i<entries; i++)
575: {
576: free(files[i]);
577: }
578: free(files);
579: files = NULL;
580: }
581:
582: if (browsingzip)
583: {
584: /* free zip file entries */
585: ZIP_FreeZipDir(zipfiles);
586: zipfiles = NULL;
587: }
588:
589: if (zip_path != NULL)
590: {
591: if( browsingzip )
592: {
593: strcpy(zip_path, zipdir);
594: strcat(zip_path, zipfilename);
595: }
596: else
597: zip_path[0] = '\0';
598: }
599:
600: free(pStringMem);
601:
602: return(retbut == SGFSDLG_OKAY);
603: }
604:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.