548 lines
15 KiB
C
548 lines
15 KiB
C
/* ** This file contains routines required to display a standard open
|
|
dialog box. Apps can directly link to object file or modify this
|
|
source for slightly different dialog box.
|
|
|
|
Note - in order to use these routines, the application must
|
|
export DlgfnOpen(). Also, an app that uses these routines must
|
|
be running ss=ds, since they use near pointers into stack.
|
|
*/
|
|
|
|
#include "cal.h"
|
|
|
|
#define ATTRDIRLIST 0xC010 /* include directories and drives in listbox */
|
|
#define ATTRFILELIST 0x0000
|
|
#define ID_LISTBOX 10
|
|
#define ID_EDIT 11
|
|
#define CBEXTMAX 6 /* Number of bytes in "\*.txt" */
|
|
|
|
CHAR szLastDir[120]; /* Dir where the last open occurred */
|
|
/* useful if file is picked up from other than current dir e.g path */
|
|
INT idEditSave;
|
|
INT idListboxSave;
|
|
INT idPathSave;
|
|
CHAR * szExtSave;
|
|
CHAR * szFileNameSave;
|
|
INT *pfpSave;
|
|
OFSTRUCT * rgbOpenSave;
|
|
INT cbRootNameMax;
|
|
#define CCHNG 15
|
|
CHAR rgchNg[CCHNG] = {'"', '\\', '/', '[', ']', ':', '|',
|
|
'<', '>', '+', '=', ';', ',', ' ', 0};
|
|
|
|
|
|
/*
|
|
* Function prototypes
|
|
*/
|
|
|
|
VOID cDlgAddCorrectExtension(CHAR *szEdit, WORD fSearching);
|
|
|
|
|
|
/*
|
|
* Functions
|
|
*/
|
|
|
|
#if 0
|
|
/************************* commented out unused code . L.Raman 12/12/90 */
|
|
|
|
/* int far DlgfnOpen(); */
|
|
/* void far cDlgCheckOkEnable(); */
|
|
/* BOOL far cDlgCheckFileName();*/
|
|
|
|
/* ** Display dialog box for opening files. Allow user to interact with
|
|
dialogbox, change directories as necessary, and try to open file if user
|
|
selects one. Automatically append extension to filename if necessary.
|
|
The open dialog box contains an edit field, listbox, static field,
|
|
and OK and CANCEL buttons.
|
|
|
|
This routine correctly parses filenames containing KANJI characters.
|
|
|
|
Input - hInstance if app module instance handle.
|
|
hwndParent is window handle of parent window
|
|
idDlgIn is dialog id
|
|
idEditIn is id of edit field
|
|
idListboxIn is id of listbox
|
|
idPathIn is id of static field which gets path name
|
|
szExtIn is pointer to zero terminated string containing
|
|
default extension to be added to filenames.
|
|
cbFileNameMaxIn is number of bytes in edit field buffer.
|
|
|
|
Output - *pfp gets value of file handle if file is opened.
|
|
or -1 if file could not be opened.
|
|
*rgbOpenIn is initialized with file info by OpenFile()
|
|
*szFileNameIn gets name of selected file (fully qualified)
|
|
Any leading blanks are removed from sszFileName.
|
|
Trailing blanks are replaced with a 0 terminator.
|
|
|
|
Returns - -1 if dialogbox() fails (out of memory).
|
|
0 if user presses cancel
|
|
1 if user enters legal filename and presses ok
|
|
2 if user enters illegal file name and presses ok
|
|
*/
|
|
INT APIENTRY cDlgOpen(
|
|
HANDLE hInstance,
|
|
HWND hwndParent,
|
|
INT idDlgIn,
|
|
INT idEditIn,
|
|
INT idListboxIn,
|
|
INT idPathIn,
|
|
CHAR *szExtIn,
|
|
INT cbFileNameMaxIn,
|
|
CHAR *szFileNameIn,
|
|
OFSTRUCT * rgbOpenIn,
|
|
INT *pfp)
|
|
{
|
|
BOOL fResult;
|
|
|
|
idEditSave = idEditIn;
|
|
idListboxSave = idListboxIn;
|
|
idPathSave = idPathIn;
|
|
szExtSave = szExtIn;
|
|
/* Limit for bytes in filename is max bytes in filename less
|
|
space for extension and 0 terminator. */
|
|
cbRootNameMax = cbFileNameMaxIn - CBEXTMAX - 1;
|
|
szFileNameSave = szFileNameIn;
|
|
rgbOpenSave = rgbOpenIn;
|
|
pfpSave = pfp;
|
|
fResult = (BOOL)DialogBox(hInstance, MAKEINTRESOURCE(idDlgIn), hwndParent, cDlgfnOpen );
|
|
return fResult;
|
|
}
|
|
|
|
|
|
/* ** Dialog function for Open File */
|
|
INT_PTR CALLBACK cDlgfnOpen(
|
|
HWND hwnd,
|
|
UINT msg,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
INT item;
|
|
CHAR rgch[256];
|
|
INT cchFile, cchDir;
|
|
CHAR *pchFile;
|
|
BOOL fWild;
|
|
static BOOL bRO=FALSE;
|
|
INT result = 2; /* Assume illegal filename */
|
|
INT len, nx;
|
|
|
|
switch (msg) {
|
|
case WM_INITDIALOG:
|
|
/* Save the global read-only status. */
|
|
bRO=vfOpenFileReadOnly;
|
|
|
|
/* Set edit field with default search spec */
|
|
SetDlgItemText(hwnd, idEditSave, g(szExtSave+1));
|
|
SendDlgItemMessage(hwnd, idEditSave, +++EM_SETSEL(use macros)+++, 0, MAKELONG(6,0));
|
|
|
|
/* Don't let user type more than cbRootNameMax bytes in edit ctl. */
|
|
SendDlgItemMessage(hwnd, idEditSave, EM_LIMITTEXT, cbRootNameMax-1, 0L);
|
|
|
|
/* fill list box with filenames that match spec, and fill static
|
|
field with path name */
|
|
if (!DlgDirList(hwnd, g(szExtSave+1), IDCN_LISTBOXDIR, idPathSave, ATTRDIRLIST))
|
|
EndDialog(hwnd, 0);
|
|
if (!DlgDirList(hwnd, g(szExtSave+1), IDCN_LISTBOX, idPathSave, ATTRFILELIST))
|
|
EndDialog(hwnd, 0);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
wParam = GET_WM_COMMAND_ID(wParam, lParam);
|
|
#ifdef NEVER
|
|
/* The following lines are commented out because they introduce this bug:
|
|
When the edit field becomes empty, OK button is not greyed
|
|
SANKAR 06-21-89.
|
|
*/
|
|
if (wParam == idEditSave)
|
|
wParam = ID_EDIT;
|
|
#endif
|
|
switch (wParam) {
|
|
case IDOK:
|
|
LoadIt:
|
|
vfOpenFileReadOnly=IsDlgButtonChecked(hwnd, IDCN_READONLY);
|
|
|
|
if (IsWindowEnabled(GetDlgItem(hwnd, IDOK)))
|
|
{
|
|
/* Get contents of edit field */
|
|
/* Add search spec if it does not contain one. */
|
|
len = 7 + GetWindowTextLength (GetDlgItem(hwnd, IDCN_EDIT));
|
|
GetDlgItemText(hwnd, idEditSave, gszFileNameSave, len);
|
|
lstrcpy(grgch, szFileNameSave);
|
|
|
|
/* Append appropriate extension to user's entry */
|
|
cDlgAddCorrectExtension(rgch, TRUE);
|
|
|
|
/* Try to open directory. If successful, fill listbox with
|
|
contents of new directory. Otherwise, open datafile. */
|
|
if (cFSearchSpec(rgch))
|
|
{
|
|
if (DlgDirList(hwnd, grgch, IDCN_LISTBOXDIR, idPathSave, ATTRDIRLIST))
|
|
{
|
|
lstrcpy(gszFileNameSave, rgch);
|
|
DlgDirList(hwnd, grgch, IDCN_LISTBOX, idPathSave, ATTRFILELIST);
|
|
SetDlgItemText(hwnd, idEditSave, gszFileNameSave);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
cDlgAddCorrectExtension(szFileNameSave, FALSE);
|
|
/* If no directory list and filename contained search spec,
|
|
honk and don't try to open. */
|
|
if (cFSearchSpec(szFileNameSave)) {
|
|
MessageBeep(0);
|
|
break;
|
|
}
|
|
|
|
/* Make filename upper case and if it's a legal dos
|
|
name, try to open the file. */
|
|
AnsiUpper(gszFileNameSave);
|
|
if (cDlgCheckFileName(szFileNameSave)) {
|
|
result = 1;
|
|
*pfpSave = MOpenFile(gszFileNameSave, (LPOFSTRUCT)rgbOpenSave, OF_PROMPT+OF_CANCEL);
|
|
if ((*pfpSave == -1) &&
|
|
(((LPOFSTRUCT)rgbOpenSave)->nErrCode == 0))
|
|
result = 2;
|
|
else { /* successful file open */
|
|
strcpy(szLastDir, ((LPOFSTRUCT)rgbOpenSave)->szPathName);
|
|
szLastDir[strlen(szLastDir)-strlen(szFileNameSave)] = 0;
|
|
}
|
|
}
|
|
EndDialog(hwnd, result);
|
|
}
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
/* User pressed cancel. Just take down dialog box. */
|
|
vfOpenFileReadOnly=bRO; /* And restore this. */
|
|
EndDialog(hwnd, 0);
|
|
break;
|
|
|
|
/* User single clicked or doubled clicked in listbox -
|
|
Single click means fill edit box with selection.
|
|
Double click means go ahead and open the selection. */
|
|
case IDCN_LISTBOX:
|
|
case IDCN_LISTBOXDIR:
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
|
|
|
|
/* Single click case */
|
|
case 1:
|
|
GetDlgItemText(hwnd, idEditSave, grgch, cbRootNameMax+1);
|
|
|
|
/* Get selection, which may be either a prefix to a new search
|
|
path or a filename. DlgDirSelectEx parses selection, and
|
|
appends a backslash if selection is a prefix */
|
|
|
|
if (wParam==IDCN_LISTBOXDIR)
|
|
SendDlgItemMessage(hwnd, IDCN_LISTBOX, LB_SETCURSEL, -1, 0L);
|
|
else
|
|
SendDlgItemMessage(hwnd, IDCN_LISTBOXDIR, LB_SETCURSEL, -1, 0L);
|
|
|
|
nx=DLGDIRSELECT(hwnd, szFileNameSave, +++nLen+++, wParam);
|
|
|
|
if (nx)
|
|
{
|
|
cchDir = lstrlen(gszFileNameSave);
|
|
cchFile = lstrlen(grgch);
|
|
pchFile = rgch+cchFile;
|
|
|
|
/* Now see if there are any wild characters (* or ?) in
|
|
edit field. If so, append to prefix. If edit field
|
|
contains no wild cards append default search spec
|
|
which is "*.TXT" for notepad. */
|
|
fWild = (*pchFile == '*' || *pchFile == ':');
|
|
while (pchFile > rgch) {
|
|
pchFile = (CHAR *)LOWORD((LONG)AnsiPrev(g(rgch), pchFile));
|
|
if (*pchFile == '*' || *pchFile == '?')
|
|
fWild = TRUE;
|
|
if (*pchFile == '\\' || *pchFile == ':') {
|
|
pchFile = (CHAR *)LOWORD((LONG)AnsiNext(gpchFile));
|
|
break;
|
|
}
|
|
}
|
|
if (fWild)
|
|
lstrcpy(gszFileNameSave + cchDir, pchFile);
|
|
else
|
|
lstrcpy(gszFileNameSave + cchDir, (szExtSave+1));
|
|
}
|
|
|
|
/* Set edit field to entire file/path name. */
|
|
SetDlgItemText(hwnd, idEditSave, gszFileNameSave);
|
|
|
|
break;
|
|
|
|
/* Double click case - first click has already been processed
|
|
as single click */
|
|
case 2:
|
|
/* Basically the same as ok. If new selection is directory,
|
|
open it and list it. Otherwise, open file. */
|
|
#if NEVER
|
|
/* None of this code is necessary. A double click is more than basically the
|
|
same as pressing OK, it is EXACTLY the same as pressing OK. No point in
|
|
duplicating all this code, especially since it will bring up 2 consecutive
|
|
System-Modal Dialogs if the path investigated references drive A with the
|
|
door open. Clark Cyr, 14 August 1989 */
|
|
|
|
DlgDirList(hwnd, szFileNameSave, IDCN_LISTBOX, idPathSave,ATTRFILELIST);
|
|
|
|
if (DlgDirList (hwnd, szFileNameSave, IDCN_LISTBOXDIR,IDCN_PATH, ATTRDIRLIST))
|
|
{
|
|
SetDlgItemText(hwnd, idEditSave, gszFileNameSave);
|
|
break;
|
|
}
|
|
#endif
|
|
goto LoadIt; /* go load it up */
|
|
}
|
|
break;
|
|
|
|
case IDCN_EDIT:
|
|
cDlgCheckOkEnable(hwnd, idEditSave, GET_WM_COMMAND_ID(wParam, lParam));
|
|
|
|
break;
|
|
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/* ** Enable ok button in a dialog box if and only if edit item
|
|
contains text. Edit item must have id of idEditSave */
|
|
VOID APIENTRY cDlgCheckOkEnable(
|
|
HWND hwnd,
|
|
INT idEdit,
|
|
WORD message)
|
|
{
|
|
if (message == EN_CHANGE) {
|
|
EnableWindow(GetDlgItem(hwnd, IDOK), (SendMessage(GetDlgItem(hwnd, idEdit), WM_GETTEXTLENGTH, 0, 0L)));
|
|
}
|
|
}
|
|
|
|
/* ** Given filename or partial filename or search spec or partial
|
|
search spec, add appropriate extension. */
|
|
VOID cDlgAddCorrectExtension(CHAR *szEdit, WORD fSearching)
|
|
{
|
|
register CHAR *pchLast;
|
|
register CHAR *pchT;
|
|
INT ichExt;
|
|
BOOL fDone = FALSE;
|
|
INT cchEdit;
|
|
|
|
pchT = pchLast = (CHAR *)LOWORD((LONG)AnsiPrev(gszEdit, (szEdit + (cchEdit = lstrlen(szEdit)))));
|
|
|
|
if ((*pchLast == '.' && *(AnsiPrev(gszEdit, pchLast)) == '.') && cchEdit == 2)
|
|
ichExt = 0;
|
|
else if (*pchLast == '\\' || *pchLast == ':')
|
|
ichExt = 1;
|
|
else {
|
|
ichExt = fSearching ? 0 : 2;
|
|
for (; pchT > szEdit; pchT = (CHAR *)LOWORD((LONG)AnsiPrev(gszEdit, pchT))) {
|
|
/* If we're not searching and we encounter a period, don't add
|
|
any extension. If we are searching, period is assumed to be
|
|
part of directory name, so go ahead and add extension. However,
|
|
if we are searching and find a search spec, do not add any
|
|
extension. */
|
|
if (fSearching) {
|
|
if (*pchT == '*' || *pchT == '?')
|
|
return;
|
|
} else if (*pchT == '.'){
|
|
return;
|
|
}
|
|
/* Quit when we get to beginning of last node. */
|
|
if (*pchT == '\\')
|
|
break;
|
|
}
|
|
/* Special case hack fix since AnsiPrev can not return value less than
|
|
szEdit. If first char is wild card, return without appending. */
|
|
if (fSearching && (*pchT == '*' || *pchT == '?'))
|
|
return;
|
|
}
|
|
#ifdef DBCS
|
|
lstrcpy(gAnsiNext(pchLast), (szExtSave+ichExt));
|
|
#else
|
|
lstrcpy(g(pchLast+1), (szExtSave+ichExt));
|
|
#endif
|
|
}
|
|
|
|
/* ** Check for legal filename. Strip leading blanks and
|
|
0 terminate */
|
|
BOOL APIENTRY cDlgCheckFilename(register CHAR *pch)
|
|
{
|
|
#ifndef CRISPY
|
|
|
|
OFSTRUCT ofT;
|
|
return (MOpenFile(gpch, (LPOFSTRUCT)&ofT, OF_PARSE) == 0);
|
|
}
|
|
#else
|
|
|
|
|
|
INT cchFN;
|
|
register INT cchT;
|
|
CHAR *pchIn;
|
|
CHAR *pchFirst;
|
|
CHAR *pchSave;
|
|
INT s;
|
|
BOOL fBackSlash;
|
|
|
|
s = 0;
|
|
fBackSlash = FALSE;
|
|
pchIn = pch;
|
|
for (;; pch = AnsiNext(gpch)) {
|
|
|
|
switch (s) {
|
|
|
|
/* Trim leading blanks */
|
|
case 0:
|
|
if (*pch == ' ')
|
|
break;
|
|
|
|
if (*pch == '\\') {
|
|
pchFirst = pch;
|
|
cchT = 0;
|
|
s = 2;
|
|
} else if (*pch == 0 || !cIsChLegal(*pch)) {
|
|
return FALSE;
|
|
|
|
} else {
|
|
pchFirst = pch;
|
|
cchT = 1;
|
|
if (*pch == '.')
|
|
s = 5;
|
|
else
|
|
s = 1;
|
|
}
|
|
break;
|
|
|
|
/* Volume, drive, subdirectory node or filename */
|
|
case 1:
|
|
if (*pch == ':' && cchT == 1) {
|
|
if (*(pch-1) < 'A' || *(pch-1) > 'Z')
|
|
return FALSE;
|
|
s = 2;
|
|
cchT--;
|
|
} else if (*pch == '\\') {
|
|
if (*(pch+1) == '\\')
|
|
return FALSE;
|
|
cchT = 0;
|
|
} else if (*pch == '.') {
|
|
cchT = 0;
|
|
s = 3;
|
|
} else if (!cIsChLegal(*pch))
|
|
return FALSE;
|
|
else if (*pch == 0)
|
|
goto RetGood;
|
|
else if (cchT++) {
|
|
s++;
|
|
}
|
|
break;
|
|
|
|
/* sub directory node or filename */
|
|
case 2:
|
|
if (*pch == '\\') {
|
|
if (*(pch+1) == '\\')
|
|
return FALSE;
|
|
fBackSlash = TRUE;
|
|
cchT = 0;
|
|
} else if (*pch == '.') {
|
|
if (*pch+1 == '.') {
|
|
if (fBackSlash || cchT)
|
|
return FALSE;
|
|
s = 5;
|
|
cchT = 1;
|
|
} else {
|
|
s++;
|
|
cchT = 0;
|
|
}
|
|
} else if (*pch == 0)
|
|
goto RetGood;
|
|
else if (cchT++ > 7 || *pch == ' ' || !IsChLegal(*pch))
|
|
return FALSE;
|
|
break;
|
|
|
|
/* up to three characters in extension */
|
|
case 3:
|
|
if (*pch == 0) {
|
|
goto RetGood;
|
|
}
|
|
|
|
if (cchT++ > 2 || *pch == '.' || !IsChLegal(*pch))
|
|
return FALSE;
|
|
|
|
if (*pch == ' ') {
|
|
pchSave = pch;
|
|
s++;
|
|
}
|
|
break;
|
|
|
|
/* Trim trailing blanks */
|
|
case 4:
|
|
if (*pch == 0) {
|
|
*pchSave = 0;
|
|
goto RetGood;
|
|
} else if (*pch != ' ')
|
|
return FALSE;
|
|
break;
|
|
|
|
/* check for ..\ */
|
|
case 5:
|
|
if (*pch++ != '.' || *pch != '\\')
|
|
return FALSE;
|
|
cchT = 0;
|
|
fBackSlash = TRUE;
|
|
s = 2;
|
|
break;
|
|
}
|
|
}
|
|
RetGood:
|
|
if (pchFirst != pchIn)
|
|
lstrcpy(gpchIn, pchFirst);
|
|
return TRUE;
|
|
}
|
|
|
|
/* ** Check for legal MS-DOS filename characters.
|
|
return TRUE if legal, FALSE otherwise. */
|
|
BOOL APIENTRY cIsChLegal(INT ch)
|
|
{
|
|
register CHAR *pch = rgchNg;
|
|
register INT ich = 0;
|
|
|
|
if (ch < ' ')
|
|
return FALSE;
|
|
|
|
rgchNg[CCHNG-1] = ch;
|
|
#ifdef DBCS
|
|
while (ch != *pch){
|
|
if( IsDBCSLeadByte( *pch ) )
|
|
ich++;
|
|
ich++;
|
|
pch = AnsiNext(pch);
|
|
}
|
|
#else
|
|
while (ch != *pch++)
|
|
ich++;
|
|
#endif
|
|
|
|
return (ich == CCHNG-1);
|
|
}
|
|
#endif
|
|
|
|
/* ** return TRUE iff 0 terminated string contains a '*' or '\' */
|
|
BOOL APIENTRY cFSearchSpec(register CHAR *sz)
|
|
{
|
|
#ifdef DBCS
|
|
for (; *sz;sz=AnsiNext(sz)){
|
|
if (*sz == '*' || *sz == '?')
|
|
return TRUE;
|
|
}
|
|
#else
|
|
for (; *sz;sz++) {
|
|
if (*sz == '*' || *sz == '?')
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
#endif
|