📄 fselect.c
字号:
/*---------------------------------------------------------------------- File : fselect.c Contents: file selector box with Athena widgets Author : Christian Borgelt History : 28.10.1997 file created 29.10.1997 first version completed 30.10.1997 user callback interface programmed 31.10.1997 minor improvements 02.11.1997 shell type changed to transient 04.11.1997 keyboard focus added 09.11.1997 bug in function getdir removed 04.01.1998 match function simplified 06.04.1998 bug in translations removed----------------------------------------------------------------------*/#define _POSIX_SOURCE#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <dirent.h>#include <sys/stat.h>#include <X11/Intrinsic.h>#include <X11/StringDefs.h>#include <X11/Shell.h>#include <X11/Xaw/Form.h>#include <X11/Xaw/Label.h>#include <X11/Xaw/AsciiText.h>#include <X11/Xaw/List.h>#include <X11/Xaw/Viewport.h>#include <X11/Xaw/Command.h>#include "fselect.h"/*---------------------------------------------------------------------- Preprocessor Definitions----------------------------------------------------------------------*/#define LISTWIDTH 250 /* initial width of list (pixels) */#define LISTHEIGHT 6 /* initial height of list (lines) */#define BLKSIZE 256 /* block size for file name vector */#define FILT_MAX 256 /* maximal length of filter string *//*---------------------------------------------------------------------- Type Definitions----------------------------------------------------------------------*/typedef struct dirent DIRENT; /* directory entry */typedef struct stat FSTAT; /* file status *//*---------------------------------------------------------------------- Global Variables----------------------------------------------------------------------*/static void fs_dclick (Widget w, XEvent *e, String *s, Cardinal *c);static void fs_focus (Widget w, XEvent *e, String *s, Cardinal *c);static void fs_next (Widget w, XEvent *e, String *s, Cardinal *c);static void fs_close (Widget w, XEvent *e, String *s, Cardinal *c);static XtActionsRec actions[] = { { "fs_dclick", fs_dclick }, /* double click on list entry */ { "fs_focus", fs_focus }, /* set focus on input field */ { "fs_next", fs_next }, /* switch to next input field */ { "fs_close", fs_close } }; /* close file select dialog box *//*---------------------------------------------------------------------- Global Variables----------------------------------------------------------------------*/static Widget w_shell = NULL; /* file selector shell */static Widget w_path; /* file name filter */static Widget w_list; /* file list */static Widget w_file; /* file selection */static XtCallbackProc cb_user; /* user callback function */static FSTAT status; /* file status buffer */static char cwd [PATH_MAX+2];/* current working directory */static char cpath[PATH_MAX+FILT_MAX+2] = "\0";static char *cpend = cpath; /* current path name and its end */static char cfilt[FILT_MAX+1] = "\0"; /* current file name filter */static int dclick = 0; /* whether to process a double click */static char **fnames = NULL; /* file name vector */static int fncnt = 0; /* number of names in vector */static int fnvsz = 0; /* size of file name vector *//*---------------------------------------------------------------------- Auxiliary Functions----------------------------------------------------------------------*/static void cleanup (DIR *dir){ /* --- clean up on error */ char **p = fnames; /* to traverse file name vector */ if (dir) { /* if a directory is opened, */ closedir(dir); /* close this directory and */ chdir(cwd); /* reset current working directory */ } if (fnames) { /* if file name vector allocated */ while (--fncnt >= 0) { free(*p); p++; } free(fnames); /* delete all file names */ } /* and the file name vector */ fnames = NULL; fncnt = fnvsz = 0;} /* cleanup() *//*--------------------------------------------------------------------*/static int match (const char *pattern, const char *string){ /* --- match string to pattern */ switch (*pattern) { /* evaluate character in pattern */ case '*' : if (match(pattern+1, string)) return 1; if (*string == '\0') return 0; return match(pattern, string+1); case '?' : if (*string == '\0') return 0; return match(pattern+1, string+1); default : if (*pattern != *string) return 0; if (*pattern == '\0') return 1; return match(pattern+1, string+1); } /* recursively match string */} /* match() *//*--------------------------------------------------------------------*/static int fncmp (const void *p1, const void *p2){ /* --- compare two file names */ return strcmp(*(const char**)p1, *(const char**)p2);} /* fncmp() *//*--------------------------------------------------------------------*/static int getlist (const char *path){ /* --- get file list */ DIR *dir; /* directory to read */ DIRENT *entry; /* current directory entry */ const char *name; /* name of current entry */ char *copy; /* copy of entry name */ char **tmp; /* buffer for new file name vector */ int isdir; /* whether entry is a directory */ int len; /* length of entry name */ cleanup(NULL); /* clean up file name vector */ if ((getcwd(cwd, PATH_MAX) == NULL) || (chdir(path) != 0)) /* note current working directory */ return -1; /* and change to directory to read */ dir = opendir(path); /* open the directory */ if (!dir) return -1; /* named by path */ while (1) { /* directory read loop */ entry = readdir(dir); /* get next directory entry */ if (!entry) break; /* if all entries read, abort loop */ name = entry->d_name; /* get name of directory entry */ if ( (name[0] == '.') /* if current directory link */ && ((name[1] != '.') /* or hidden file */ || (name[2] != '\0'))) /* (i.e. not matching \.\.|[^.].*) */ continue; /* skip directory entry */ if (stat(name, &status) != 0) { cleanup(dir); return -1;} /* get file status */ isdir = S_ISDIR(status.st_mode) ? 1 : 0; if (!isdir && (cfilt[0] != '\0') && !match(cfilt, name)) /* if file does not match filter, */ continue; /* skip directory entry */ if (fncnt >= fnvsz) { /* if file name vector is full, */ fnvsz += BLKSIZE; /* increase vector size */ tmp = (char**)realloc(fnames, fnvsz *sizeof(char**)); if (!tmp) { cleanup(dir); return -1; } fnames = tmp; /* allocate and set */ } /* new file name vector */ len = strlen(name); /* get length of entry name */ fnames[fncnt] = copy = (char*)malloc(len +1 +isdir); if (!copy) { cleanup(dir); return -1; } while (*name) *copy++ = *name++; if (isdir) *copy++ = '/'; /* copy entry name, */ *copy = '\0'; /* add a '/' for directories and */ fncnt++; /* insert it into file name vector */ } closedir(dir); /* close directory read and */ chdir(cwd); /* reset current working directory */ qsort(fnames, fncnt, sizeof(char*), fncmp); return 0; /* sort names and return 'ok' */} /* getlist() *//*--------------------------------------------------------------------*/static void update (const char *path, int dialog){ /* --- update filter and file list */ const char *filter; /* current path and filter */ int len; /* length of path name */ char *dst; /* to traverse filter */ filter = strrchr(path, '/'); /* get current path */ if (filter) filter++; /* and file name filter */ else filter = path; /* (starts after last '/') */ len = (filter -path < PATH_MAX) ? (filter -path) : PATH_MAX; for (dst = cfilt; *filter; ) *dst++ = *filter++; *dst = '\0'; /* copy filter to buffer */ for (cpend = cpath; --len >= 0; ) *cpend++ = *path++; if (cpend <= cpath) *cpend++ = '/'; *cpend = '\0'; /* copy (minimal) path to buffer */ if (dialog) { /* if to update dialog box */ if (getlist(cpath) != 0) /* get new list of files */ fprintf(stderr, "cannot open/read %s\n", cpath); XawListChange(w_list, fnames, fncnt, 0, True); strcpy(cpend, cfilt); /* append filter to path */ XtVaSetValues(w_path, XtNstring, cpath, NULL); } /* set new directory/filter */} /* update() *//*---------------------------------------------------------------------- Callback Functions----------------------------------------------------------------------*/static void cb_update (Widget w, XtPointer client, XtPointer call){ /* --- callback for filter button */ const char *path; /* current path in input field */ XtVaGetValues(w_path, XtNstring, &path, NULL); update(path, 1); /* set new path and filename filter */} /* cb_update() *//*--------------------------------------------------------------------*/static void cb_list (Widget w, XtPointer client, XtPointer call){ /* --- callback for list selection */ char *name; /* name of selected entry */ int len; /* name length of selected entry */ name = ((XawListReturnStruct*)call)->string; len = strlen(name); /* get selected entry and its length */ if (len <= 0) return; /* if entry is empty, abort function */ if (name[len-1] != '/') { /* if entry is not a directory */ XtVaSetValues(w_file, XtNstring, name, NULL); dclick = 1; return; /* set new selection */ } /* and abort function */ if (strcmp(name, "../") == 0){/* if no to go up one level */ cpend -= 2; /* get pointer to char before '/' */ while ((cpend >= cpath) && (*cpend != '/')) cpend--; /* search for previous '/' */ if (cpend >= cpath) /* if '/' found, */ cpend++; /* shorten path name */ else { /* if '/' not found */ cpend = cpath; *cpend++ = '/'; } } /* set minimal path */ else { /* if to go down one level */ len += cpend -cpath; /* add lengths of path and entry name */ if (len >= PATH_MAX) /* if path name would get too long, */ return; /* abort function */ while (*name) *cpend++ = *name++; } /* append directory name to path */ *cpend = '\0'; /* terminate path name */ if (getlist(cpath) != 0) /* get new list of files */ fprintf(stderr, "cannot open %s\n", cpath); XawListChange(w_list, fnames, fncnt, 0, True); strcpy(cpend, cfilt); /* append filter to path */ XtVaSetValues(w_path, XtNstring, cpath, NULL); dclick = 0; /* inhibit double click processing */} /* cb_list() *//*--------------------------------------------------------------------*/static void cb_button (Widget w, XtPointer client, XtPointer call){ /* --- callback for button press */ const char *path, *fname; /* names of path and file */ XtPopdown(w_shell); /* close dialog box */ if ((int)client) { /* if 'ok' button pressed */ XtVaGetValues(w_path, XtNstring, &path, NULL); update(path, 0); /* set new path and filename filter */ XtVaGetValues(w_file, XtNstring, &fname, NULL); strncpy(cpend, fname, FILT_MAX); cpend[FILT_MAX] = '\0'; /* build final selection */ } /* (file name including path) */ cleanup(NULL); /* clean up file name vector */ if (cb_user) /* call user callback function */ cb_user(w, client, ((int)client) ? cpath : NULL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -