mirror of
https://github.com/Oibaf66/frodo-wii.git
synced 2024-11-22 11:29:24 +01:00
415 lines
11 KiB
C++
415 lines
11 KiB
C++
/*
|
|
* dlgFileselect.cpp - dialog for selecting files (fileselector box)
|
|
*
|
|
* This file is taken from the ARAnyM project which builds a new and powerful
|
|
* TOS/FreeMiNT compatible virtual machine running on almost any hardware.
|
|
*
|
|
* Originally taken from the hatari project, thanks Thothy!
|
|
*
|
|
* Copyright (c) 2003-2005 ARAnyM dev team (see AUTHORS)
|
|
*
|
|
* Frodo is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Frodo is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Frodo; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <SDL.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "dirent.h"
|
|
|
|
#include <fdebug.h>
|
|
|
|
#include "sdlgui.h"
|
|
#include "file.h"
|
|
|
|
#define SGFSDLG_FILENAME 5
|
|
#define SGFSDLG_UPDIR 6
|
|
#define SGFSDLG_ROOTDIR 7
|
|
#define SGFSDLG_FIRSTENTRY 10
|
|
#define SGFSDLG_LASTENTRY 24
|
|
#define SGFSDLG_UP 25
|
|
#define SGFSDLG_DOWN 26
|
|
#define SGFSDLG_OKAY 27
|
|
#define SGFSDLG_CANCEL 28
|
|
|
|
#define ENTRY_COUNT (SGFSDLG_LASTENTRY - SGFSDLG_FIRSTENTRY + 1)
|
|
#define ENTRY_LENGTH 30
|
|
|
|
|
|
static char dlgpath[39], dlgfname[33]; /* File and path name in the dialog */
|
|
static char dlgfilenames[ENTRY_COUNT][ENTRY_LENGTH + 1];
|
|
|
|
/* The dialog data: */
|
|
static SGOBJ fsdlg[] = {
|
|
{SGBOX, SG_BACKGROUND, 0, 0, 0, 35, 25, NULL},
|
|
{SGTEXT, 0, 0, 10, 1, 13, 1, "Choose a file"},
|
|
{SGTEXT, 0, 0, 1, 2, 7, 1, "Folder:"},
|
|
{SGTEXT, 0, 0, 1, 3, 33, 1, dlgpath},
|
|
{SGTEXT, 0, 0, 1, 4, 6, 1, "File:"},
|
|
{SGTEXT, 0, 0, 7, 4, 26, 1, dlgfname},
|
|
{SGBUTTON, SG_SELECTABLE | SG_EXIT, 0, 26, 1, 4, 1, ".."},
|
|
{SGBUTTON, SG_SELECTABLE | SG_EXIT, 0, 31, 1, 3, 1, "/"},
|
|
{SGBOX, 0, 0, 1, 6, 33, 15, NULL},
|
|
{SGBOX, 0, 0, 33, 6, 1, 15, NULL},
|
|
{SGTEXT, 0, 0, 2, 6, ENTRY_LENGTH, 1, dlgfilenames[0]},
|
|
{SGTEXT, 0, 0, 2, 7, ENTRY_LENGTH, 1, dlgfilenames[1]},
|
|
{SGTEXT, 0, 0, 2, 8, ENTRY_LENGTH, 1, dlgfilenames[2]},
|
|
{SGTEXT, 0, 0, 2, 9, ENTRY_LENGTH, 1, dlgfilenames[3]},
|
|
{SGTEXT, 0, 0, 2, 10, ENTRY_LENGTH, 1, dlgfilenames[4]},
|
|
{SGTEXT, 0, 0, 2, 11, ENTRY_LENGTH, 1, dlgfilenames[5]},
|
|
{SGTEXT, 0, 0, 2, 12, ENTRY_LENGTH, 1, dlgfilenames[6]},
|
|
{SGTEXT, 0, 0, 2, 13, ENTRY_LENGTH, 1, dlgfilenames[7]},
|
|
{SGTEXT, 0, 0, 2, 14, ENTRY_LENGTH, 1, dlgfilenames[8]},
|
|
{SGTEXT, 0, 0, 2, 15, ENTRY_LENGTH, 1, dlgfilenames[9]},
|
|
{SGTEXT, 0, 0, 2, 16, ENTRY_LENGTH, 1, dlgfilenames[10]},
|
|
{SGTEXT, 0, 0, 2, 17, ENTRY_LENGTH, 1, dlgfilenames[11]},
|
|
{SGTEXT, 0, 0, 2, 18, ENTRY_LENGTH, 1, dlgfilenames[12]},
|
|
{SGTEXT, 0, 0, 2, 19, ENTRY_LENGTH, 1, dlgfilenames[13]},
|
|
{SGTEXT, 0, 0, 2, 20, ENTRY_LENGTH, 1, dlgfilenames[14]},
|
|
{SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 33, 6, 1, 1, "\x01"},
|
|
/* Arrow up */
|
|
{SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 33, 20, 1, 1, "\x02"},
|
|
/* Arrow down */
|
|
{SGBUTTON, SG_SELECTABLE | SG_EXIT | SG_DEFAULT, 0, 7, 23, 8, 1,
|
|
"OK"},
|
|
{SGBUTTON, SG_SELECTABLE | SG_EXIT, 0, 21, 23, 8, 1, "Cancel"},
|
|
{-1, 0, 0, 0, 0, 0, 0, NULL}
|
|
};
|
|
|
|
struct listentry {
|
|
char *filename;
|
|
bool directory;
|
|
struct listentry *next;
|
|
};
|
|
|
|
/* Create a sorted list from the directory listing of path */
|
|
struct listentry *create_list(char *path)
|
|
{
|
|
struct listentry *list = NULL;
|
|
struct listentry *newentry;
|
|
DIR *dd;
|
|
struct dirent *direntry;
|
|
char tempstr[MAX_FILENAME_LENGTH];
|
|
struct stat filestat;
|
|
|
|
if ((dd = opendir(path)) == NULL)
|
|
return NULL;
|
|
|
|
while ((direntry = readdir(dd)) != NULL) {
|
|
// skip "." name
|
|
if (strcmp(direntry->d_name, ".") == 0)
|
|
continue;
|
|
|
|
/* Allocate enough memory to store a new list entry and
|
|
its filemane */
|
|
newentry =
|
|
(struct listentry *) malloc(sizeof(struct listentry) +
|
|
strlen(direntry->d_name) + 1);
|
|
|
|
/* Store filename */
|
|
newentry->filename = (char *) (newentry + 1);
|
|
strcpy(newentry->filename, direntry->d_name);
|
|
|
|
/* Is this entry a directory ? */
|
|
strcpy(tempstr, path);
|
|
strcat(tempstr, newentry->filename);
|
|
if (stat(tempstr, &filestat) == 0 && S_ISDIR(filestat.st_mode))
|
|
newentry->directory = true;
|
|
else
|
|
newentry->directory = false;
|
|
|
|
/* Search for right place to insert new entry */
|
|
struct listentry **prev = &list;
|
|
struct listentry *next = list;
|
|
while (next != NULL) {
|
|
/* directory first, then files */
|
|
if ((newentry->directory == true)
|
|
&& (next->directory == false))
|
|
break;
|
|
|
|
/* Sort by name */
|
|
if ((newentry->directory == next->directory)
|
|
&& (strcmp(newentry->filename, next->filename) < 0))
|
|
break;
|
|
|
|
prev = &(next->next);
|
|
next = next->next;
|
|
}
|
|
|
|
/* Insert new entry */
|
|
newentry->next = next;
|
|
*prev = newentry;
|
|
}
|
|
|
|
if (closedir(dd) != 0)
|
|
bug("Error on closedir.");
|
|
|
|
return list;
|
|
}
|
|
|
|
/* Free memory allocated for each member of list */
|
|
void free_list(struct listentry *list)
|
|
{
|
|
while (list != NULL) {
|
|
struct listentry *temp = list;
|
|
list = list->next;
|
|
free(temp);
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/*
|
|
Show and process a file selection dialog.
|
|
Returns TRUE if user selected "OK", FALSE if "cancel".
|
|
bAllowNew: TRUE if the user is allowed to insert new file names.
|
|
*/
|
|
int SDLGui_FileSelect(char *path_and_name, bool bAllowNew)
|
|
{
|
|
int i;
|
|
int ypos = 0;
|
|
struct listentry *list = NULL;
|
|
/* The actual file and path names */
|
|
char path[MAX_FILENAME_LENGTH], fname[128];
|
|
/* Do we have to reload the directory file list? */
|
|
bool reloaddir = true;
|
|
/* Do we have to update the file names in the dialog? */
|
|
bool refreshentries = true;
|
|
int retbut;
|
|
int oldcursorstate;
|
|
/* The actual selection, -1 if none selected */
|
|
int selection = -1;
|
|
bool eol = true;
|
|
|
|
if (bAllowNew)
|
|
fsdlg[SGFSDLG_FILENAME].type = SGEDITFIELD;
|
|
else
|
|
fsdlg[SGFSDLG_FILENAME].type = SGTEXT;
|
|
|
|
/* Prepare the path and filename variables */
|
|
File_splitpath(path_and_name, path, fname, NULL);
|
|
if (strlen(path) == 0) {
|
|
getcwd(path, sizeof(path));
|
|
File_AddSlashToEndFileName(path);
|
|
}
|
|
File_ShrinkName(dlgpath, path, 38);
|
|
File_ShrinkName(dlgfname, fname, 32);
|
|
|
|
/* Save old mouse cursor state and enable cursor anyway */
|
|
screenlock();
|
|
oldcursorstate = SDL_ShowCursor(SDL_QUERY);
|
|
if (oldcursorstate == SDL_DISABLE)
|
|
SDL_ShowCursor(SDL_ENABLE);
|
|
screenunlock();
|
|
|
|
do {
|
|
if (reloaddir) {
|
|
if (strlen(path) >= MAX_FILENAME_LENGTH) {
|
|
fprintf(stderr,
|
|
"SDLGui_FileSelect: Path name too long!\n");
|
|
return false;
|
|
}
|
|
|
|
free_list(list);
|
|
|
|
/* Load directory entries: */
|
|
list = create_list(path);
|
|
if (list == NULL) {
|
|
fprintf(stderr, "SDLGui_FileSelect: Path not found.\n");
|
|
/* reset path and reload entries */
|
|
strcpy(path, "/");
|
|
strcpy(dlgpath, path);
|
|
list = create_list(path);
|
|
if (list == NULL)
|
|
/* we're really lost if even root is
|
|
unreadable */
|
|
return false;
|
|
}
|
|
reloaddir = false;
|
|
refreshentries = true;
|
|
}
|
|
|
|
if (refreshentries) {
|
|
struct listentry *temp = list;
|
|
for (i = 0; i < ypos; i++)
|
|
temp = temp->next;
|
|
|
|
/* Copy entries to dialog: */
|
|
for (i = 0; i < ENTRY_COUNT; i++) {
|
|
if (temp != NULL) {
|
|
char tempstr[MAX_FILENAME_LENGTH];
|
|
/* Prepare entries: */
|
|
strcpy(tempstr, " ");
|
|
strcat(tempstr, temp->filename);
|
|
File_ShrinkName(dlgfilenames[i], tempstr,
|
|
ENTRY_LENGTH);
|
|
/* Mark folders: */
|
|
if (temp->directory)
|
|
dlgfilenames[i][0] = SGFOLDER;
|
|
fsdlg[SGFSDLG_FIRSTENTRY + i].flags =
|
|
(SG_SELECTABLE | SG_EXIT | SG_RADIO);
|
|
temp = temp->next;
|
|
}
|
|
else {
|
|
/* Clear entry */
|
|
dlgfilenames[i][0] = 0;
|
|
fsdlg[SGFSDLG_FIRSTENTRY + i].flags = 0;
|
|
}
|
|
fsdlg[SGFSDLG_FIRSTENTRY + i].state = 0;
|
|
}
|
|
|
|
if (temp == NULL)
|
|
eol = true;
|
|
else
|
|
eol = false;
|
|
|
|
refreshentries = false;
|
|
}
|
|
|
|
/* Show dialog: */
|
|
retbut = SDLGui_DoDialog(fsdlg);
|
|
|
|
/* Has the user clicked on a file or folder? */
|
|
if ((retbut >= SGFSDLG_FIRSTENTRY)
|
|
&& (retbut <= SGFSDLG_LASTENTRY)) {
|
|
char tempstr[MAX_FILENAME_LENGTH];
|
|
struct stat filestat;
|
|
struct listentry *temp = list;
|
|
|
|
strcpy(tempstr, path);
|
|
for (int i = 0; i < ((retbut - SGFSDLG_FIRSTENTRY) + ypos);
|
|
i++)
|
|
temp = temp->next;
|
|
strcat(tempstr, temp->filename);
|
|
if (stat(tempstr, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
|
|
/* Set the new directory */
|
|
strcpy(path, tempstr);
|
|
if (strlen(path) >= 3) {
|
|
if ((path[strlen(path) - 2] == '/')
|
|
&& (path[strlen(path) - 1] == '.'))
|
|
/* Strip a single dot at the
|
|
end of the path name */
|
|
path[strlen(path) - 2] = 0;
|
|
if ((path[strlen(path) - 3] == '/')
|
|
&& (path[strlen(path) - 2] == '.')
|
|
&& (path[strlen(path) - 1] == '.')) {
|
|
/* Handle the ".." folder */
|
|
char *ptr;
|
|
if (strlen(path) == 3)
|
|
path[1] = 0;
|
|
else {
|
|
path[strlen(path) - 3] = 0;
|
|
ptr = strrchr(path, '/');
|
|
if (ptr)
|
|
*(ptr + 1) = 0;
|
|
}
|
|
}
|
|
}
|
|
File_AddSlashToEndFileName(path);
|
|
reloaddir = true;
|
|
/* Copy the path name to the dialog */
|
|
File_ShrinkName(dlgpath, path, 38);
|
|
selection = -1; /* Remove old selection */
|
|
// fname[0] = 0;
|
|
dlgfname[0] = 0;
|
|
ypos = 0;
|
|
}
|
|
else {
|
|
/* Select a file */
|
|
selection = retbut - SGFSDLG_FIRSTENTRY + ypos;
|
|
strcpy(fname, temp->filename);
|
|
File_ShrinkName(dlgfname, fname, 32);
|
|
}
|
|
}
|
|
else {
|
|
/* Has the user clicked on another button? */
|
|
switch (retbut) {
|
|
case SGFSDLG_UPDIR:
|
|
/* Change path to parent directory */
|
|
if (strlen(path) > 2) {
|
|
char *ptr;
|
|
File_CleanFileName(path);
|
|
ptr = strrchr(path, '/');
|
|
if (ptr)
|
|
*(ptr + 1) = 0;
|
|
File_AddSlashToEndFileName(path);
|
|
reloaddir = true;
|
|
/* Copy the path name to the dialog */
|
|
File_ShrinkName(dlgpath, path, 38);
|
|
/* Remove old selection */
|
|
selection = -1;
|
|
fname[0] = 0;
|
|
dlgfname[0] = 0;
|
|
ypos = 0;
|
|
}
|
|
break;
|
|
case SGFSDLG_ROOTDIR:
|
|
/* Change to root directory */
|
|
strcpy(path, "/");
|
|
reloaddir = true;
|
|
strcpy(dlgpath, path);
|
|
/* Remove old selection */
|
|
selection = -1;
|
|
fname[0] = 0;
|
|
dlgfname[0] = 0;
|
|
ypos = 0;
|
|
break;
|
|
case SGFSDLG_UP:
|
|
/* Scroll up */
|
|
if (ypos > 0) {
|
|
--ypos;
|
|
refreshentries = true;
|
|
}
|
|
SDL_Delay(20);
|
|
break;
|
|
case SGFSDLG_DOWN:
|
|
/* Scroll down */
|
|
if (eol == false) {
|
|
++ypos;
|
|
refreshentries = true;
|
|
}
|
|
SDL_Delay(20);
|
|
break;
|
|
} /* switch */
|
|
} /* other button code */
|
|
|
|
}
|
|
while ((retbut != SGFSDLG_OKAY) && (retbut != SGFSDLG_CANCEL)
|
|
&& (retbut != -1));
|
|
|
|
screenlock();
|
|
if (oldcursorstate == SDL_DISABLE)
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
screenunlock();
|
|
|
|
{
|
|
/* if user edited filename, use new one */
|
|
char dlgfname2[33];
|
|
File_ShrinkName(dlgfname2, fname, 32);
|
|
if (strcmp(dlgfname, dlgfname2) != 0)
|
|
strcpy(fname, dlgfname);
|
|
}
|
|
|
|
File_makepath(path_and_name, path, fname, NULL);
|
|
|
|
free_list(list);
|
|
|
|
return (retbut == SGFSDLG_OKAY);
|
|
}
|
|
|
|
/*
|
|
vim:ts=4:sw=4:
|
|
*/
|