
HomePage | Optical Illusions | War Stories | QBasic | Dads Navy Days | Bristol | Bristol, USA | Bristol, Canada | Terre Haute | Miscellany | Web Stuff | About Ray | Site Map | Site Search | Messages | Credits | Links | Web Rings
QBasic | Errors | 40lb Weight | Bits | Chance | Colours | Dates | Delays | File Dialog | Files | Input | Matching | Menus | Mouse | Numbers | SeqNo | SIRDS | Sorts | Text | Timer | DLoads
File Dialog Box
QBasic is a basic language. What I mean by that is that there are no pre-written libraries or routines that come with the program. If you want something done, you have to do it yourself. Even things like the file dialog boxes that are prewritten in Visual Basic don't exist for QBasic. A search on the internet will find a couple of these written in QBasic, but I wasn't happy with the ones I found. Here is my attempt at writing one.

Screenshot of FileDir.bas
Using the TAB key you can navigate around the dialog box.
Pressing ENTER on a highlighted file will open that file
Pressing ENTER on a highlighted directory will change to that directory
Typing a new file name will open that file, or if it doesn't exist, attempt to create it
Typing a new directory name will change to that directory, or if it doesn't exist, attempt to create it
There are several subroutines to this program. In them you can see how to sort an array, how to draw a scrolling menu, how to manipulate strings, error checking, creating files and directories, changing drives and directories, trapping and using the non-printable keyboard characters, and other techniques.
The basis of the program is very simple though. Using SHELL "dir > c:\ZFileDir.tmp" the program sends a DIR listing to the file c:\ZFileDir.tmp, it then reads the file and extracts the drive, directory and file information from it. There is a couple of problems with this method, but without using interrupts I can't see a solution. One problem is this, getting the information from a removable drives is slow. The program was originally written so that the temporary file was written to the current directory, this created a problem when reading the disk information from CDs and DVDs.
Because you can't change the size of an array in QBasic without losing all the information in it, the size of the arrays to hold the file and directory information are fixed at 500 each. This means the program will not display all the files if there are more than 500 in a directory, and it will not show all the sub-directories is there are more than 500 of them. I tried using REDIM but because this has to be done from a subroutine then it doesn't work.
Yet another problem, I'm not sure if the line SHELL "dir > c:\ZFileDir.tmp" actually returns the same file format from different versions of QBasic on different platforms. Here's a partial listing from my QBasic directory. This is from DOS :-
Volume in drive C is BRISRAY
Volume Serial Number is 123A-4BC5
Directory of c:\qbasic
10/03/2001 12:21a <DIR> .
10/03/2001 12:21a <DIR> ..
01/22/2000 11:15a 8,943 40LBS.BAS
10/13/1998 02:37p 3,106 BUSY.BAS
11/13/2000 11:06a 6,569 Calandar.bas
07/31/1999 10:44a 1,534 CHANCE.BAS
11/08/2000 06:05a 2,276 WEEKDAY.BAS
10/19/1998 12:24p 9,178 XFLD.BAS
06/07/2000 12:47p 4,940 XHAIR.BAS
10/03/2001 12:21a <DIR> Martin
10/03/2001 12:21a <DIR> Harvey
10/03/2001 12:21a <DIR> Gene
02/28/2002 02:50p <DIR> Chriss
03/04/2002 02:18p 345 qprimes.bas
10/12/2001 12:01p 132 QBASIC.INI
10/27/2001 01:13a 2,546 PRINT.BAS
10/10/2001 12:02p <DIR> Stevie
03/04/2002 10:57p 43,136 qbprog.zip
01/07/2002 05:10p <DIR> Kesler
02/03/2002 02:16p 3,740 MENUCOL.BAS
03/04/2002 10:06p 35,074 FILEDIR.BAS
02/03/2002 03:06p 3,199 MENUALT.BAS
02/06/2002 07:00p 114 FILEMENU.BAS
02/08/2002 03:31p <DIR> wip
02/10/2002 03:43p 1,742 TICKER.BAS
82 File(s) 804,278 bytes
9 Dir(s) 34,701,246,464 bytes free
And this is from within QBasic :-
Volume in drive C is BRISRAY
Volume Serial Number is 123A-4BC5
Directory of C:\QBASIC
. <DIR> 10/03/01 12:21a
.. <DIR> 10/03/01 12:21a
40LBS BAS 8943 01/22/00 11:15a
BUSY BAS 3106 10/13/98 2:37p
CALANDAR BAS 6569 11/13/00 11:06a
CHANCE BAS 1534 07/31/99 10:44a
WEEKDAY BAS 2276 11/08/00 6:05a
XFLD BAS 9178 10/19/98 12:24p
XHAIR BAS 4940 06/07/00 12:47p
MARTIN <DIR> 10/03/01 12:21a
HARVEY <DIR> 10/03/01 12:21a
GENE <DIR> 10/03/01 12:21a
CHRISS <DIR> 02/28/02 2:50p
QPRIMES BAS 345 03/04/02 2:18p
QBASIC INI 132 10/12/01 12:01p
PRINT BAS 2546 10/27/01 1:13a
STEVIE <DIR> 10/10/01 12:02p
KESLER <DIR> 01/07/02 5:10p
MENUCOL BAS 3740 02/03/02 2:16p
FILEDIR BAS 35074 03/05/02 10:35a
QSORT BAS 3635 03/05/02 6:51a
QBPROG ZIP 45329 03/05/02 9:28a
TXTMENU BAS 1370 02/03/02 11:10a
MENUALT BAS 3199 02/03/02 3:06p
WIP <DIR> 02/08/02 3:31p
WAIT BAS 876 02/10/02 10:14p
PARSE BAS 1449 02/10/02 9:18p
TICKER BAS 1742 02/10/02 3:43p
92 file(s) 875928 bytes
1023932928 bytes free
If your directory listing is much different from this in layout then you are going to have to edit the GetCurrent subroutine .
Apart from that, the program works quite well, and there is enough error checking so that it shouldn't crash. But no promises on that as I'm sure someone, somewhere will manage to break it.
'FileDir.bas Ray Thomas February 2002
'A file / directory dialog / menu
DECLARE SUB GetCurrent () '*** Get the current drive and directory ***
DECLARE SUB FindDrives () '*** Find which drives are on the PC ***
DECLARE SUB SortArrays (SortArray$(), Low AS INTEGER, High AS INTEGER) '*** Put the files and directories in order ***
DECLARE SUB DrawMenu () '*** Draw the fixed menu items ***
DECLARE SUB FillMenu () '*** Fill the variable menu items ***
DECLARE SUB DoWriteMenu () '*** Do File, Drive, Directory menu items ***
DECLARE SUB DoFileMenu () '*** Do the scrolling file menu ***
DECLARE SUB DoDirMenu () '*** Do the scrolling directory menu ***
DECLARE SUB ChkFile () '*** Check that a file exists ***
DECLARE SUB ChkDrv () '*** Check that a drive exists ***
DECLARE SUB ChkDir () '*** Check that a directory exists ***
DECLARE SUB ChngDir () '*** Change a directory ***
OPTION BASE 1
'*** Global variables ***
DIM SHARED FileNo AS INTEGER 'File stream number
DIM SHARED DirName(500) AS STRING 'Directory names
DIM SHARED FileName(500) AS STRING 'File names
DIM SHARED CurrDrive AS STRING 'Current drive
DIM SHARED CurrDir AS STRING 'Current directory
DIM SHARED CurrPath AS STRING 'Current path
DIM SHARED NumDirs AS INTEGER 'Number of directories
DIM SHARED NumFiles AS INTEGER 'Number of files
DIM SHARED TabNum AS INTEGER 'Keep a track of what part of the menu
DIM SHARED YMenuPosn AS INTEGER 'Top right Y position of menu
DIM SHARED XMenuPosn AS INTEGER 'Top right X position of menu
DIM SHARED EndMenu AS INTEGER 'User pressed ESC or file opened properly
DIM SHARED HiLiteFile AS INTEGER 'Currently highlighted file
DIM SHARED HiLiteDir AS INTEGER 'Currently highlighted directory
DIM SHARED Message AS STRING 'Current field being edited
DIM SHARED Drive AS STRING 'Drives available on the computer
DIM SHARED ProgErr AS INTEGER 'Program error code
DIM SHARED OrigDrive AS STRING 'Original drive
DIM SHARED OrigDir AS STRING 'Original directory
DIM SHARED OrigPath AS STRING 'Original path
'*** Local variables ****
DIM Num AS INTEGER 'General integer variable
'*** The DIR command may give different results on different platforms ***
TabNum = 1
YMenuPosn = 3
XMenuPosn = 3
'*** Find the drives on the computer ***
ON ERROR GOTO DriveErr
FOR Count = 65 TO 90
ProgErr = 0
Message$ = CHR$(Count) + ":\"
FILES Message$
IF ProgErr = 0 THEN Drive$ = Drive$ + CHR$(Count)
NEXT Count
ON ERROR GOTO OopsError
GetCurrent '*** Get the current drive and directory ***
SortArrays FileName$(), 1, NumFiles
SortArrays DirName$(), 1, NumDirs
'*** Get the original info to reset on exit ***
OrigDrive$ = CurrDrive$
OrigDir$ = CurrDir$
OrigPath$ = CurrPath$
DrawMenu '*** Draw the fixed menu items ***
FillMenu '*** Fill the variable menu items ***
DO
'*** Relocate the cursor to the current field (by TabNum) ***
EndMenu = 0
IF TabNum <= 3 THEN
DoWriteMenu
END IF
IF TabNum = 4 THEN DoFileMenu
IF TabNum = 5 THEN DoDirMenu
LOOP UNTIL EndMenu > 0 '*** 1 = ESC was pressed, 2 = file found, 3 = file created ***
CLS
IF EndMenu = 1 THEN
PRINT
PRINT " Menu exited, ESC pressed"
END IF
IF EndMenu = 2 THEN
PRINT
PRINT " The file "; Message$; " was successfully found"
PRINT
PRINT " You may now open it or whatever"
END IF
IF EndMenu = 3 THEN
PRINT
PRINT " The file "; Message$; " was successfully created"
PRINT
PRINT " and now deleted"
KILL Message$
END IF
'*** reset the drive and directory to the original ***
SHELL OrigDrive$
CHDIR OrigDir$
CLOSE
END
DriveErr:
IF ERR = 68 THEN ProgErr = 1
RESUME NEXT
OopsError:
CLS
PRINT
PRINT " The program has reported a fatal error and will now end"
PRINT
PRINT " The Error Code reported was"; ERR
PRINT
PRINT " Press any key to continue . . ."
DO
LOOP UNTIL INKEY$ <> ""
CLOSE
SHELL OrigDrive$
CHDIR OrigDir$
END
FileErr:
IF ERR > 0 THEN ProgErr = 1
RESUME NEXT
SUB ChkDir
'*** There are two ways into this sub, either from a typed ***
'*** directory or one chosen from the directory menu ***
'*** Check that the chosen or typed directory exists ***
'*** It should already be in the FileName$ array,
'*** but refresh the array in case it was created while the program was running ***
DIM OldDir AS STRING 'A copy of the current directory name
DIM OldDrv AS STRING 'A copy of the current drive letter
DIM DirPosn AS INTEGER 'Place holder for splitting splitting directory string
OldDir$ = CurrDir$ '*** Keep a copy of the directory in case of errors ***
OldDrv$ = CurrDrive$ '*** Keep a copy of the drive in case of errors ***
IF TabNum = 5 THEN
GetCurrent
SortArrays FileName$(), 1, NumFiles
SortArrays DirName$(), 1, NumDirs
FOR Count = 1 TO UBOUND(DirName$)
IF UCASE$(DirName$(HiLiteDir)) = UCASE$(DirName$(Count)) THEN
IF DirName$(HiLiteDir) = ".." THEN
SHELL "CD .."
ELSE
IF RIGHT$(CurrDir$, 1) <> "\" THEN CurrDir$ = CurrDir$ + "\"
SHELL "CD " + CurrDir$ + DirName$(HiLiteDir)
END IF
EXIT FOR
END IF
NEXT Count
END IF
'*** Getting the directories from the typed in string is a bit more complicated
'*** If the string starts with a letter then a ":" then the drive needs changing too ***
'*** If the string sarts with a "\" then assume the ***
'*** directory list starts from the root of the drive ***
'*** If it doesn't then assume it starts from the current directory ***
ProgErr = 0
IF TabNum = 3 THEN
ON ERROR GOTO FileErr
IF RIGHT$(Message$, 1) = "\" THEN Message$ = LEFT$(Message$, LEN(Message$) - 1)
IF LEFT$(Message$, 1) = "\" THEN
CHDIR Message$
ELSE
IF MID$(Message$, 2, 2) = ":\" THEN
IF INSTR(UCASE$(Drive$), UCASE$(LEFT$(Message$, 1))) > 0 THEN
SHELL LEFT$(Message$, 2)
Message$ = MID$(Message$, 3)
CHDIR Message$
END IF
ELSE
Message$ = CurrDir$ + "/" + Message$
CHDIR Message$
END IF
END IF
'*** If possible make the typed directory ***
IF ProgErr = 1 THEN
ProgErr = 0
MKDIR Message$
IF ProgErr = 0 THEN
PRINT " created. Press any key"'
DO
LOOP UNTIL INKEY$ <> ""
END IF
END IF
IF ProgErr = 1 THEN
SHELL OldDrv$ + ":"
CHDIR OldDir$
END IF
ON ERROR GOTO OopsError
'*** Redo the command line ***
GetCurrent
LOCATE YMenuPosn + 5, XMenuPosn + 15
PRINT SPACE$(59);
Message$ = CurrDir$
LOCATE YMenuPosn + 5, XMenuPosn + 15
PRINT Message$;
END IF
HiLiteFile = 1
HiLiteDir = 1
GetCurrent
SortArrays FileName$(), 1, NumFiles
SortArrays DirName$(), 1, NumDirs
DrawMenu
FillMenu
END SUB
SUB ChkDrv
IF INSTR(1, Drive$, UCASE$(LEFT$(Message$, 1))) >= 1 THEN
Message$ = UCASE$(LEFT$(Message$, 1)) + ":"
SHELL Message$
GetCurrent '*** Get the current drive and directory ***
SortArrays FileName$(), 1, NumFiles
SortArrays DirName$(), 1, NumDirs
DrawMenu
FillMenu
END IF
END SUB
SUB ChkFile
DIM Count AS INTEGER
'*** Check that the chosen or typed file exists ***
'*** It should already be in the FileName$ array,
'*** but refresh the array in case it was created while the program was running ***
ProgErr = 0
GetCurrent
SortArrays FileName$(), 1, NumFiles
SortArrays DirName$(), 1, NumDirs
IF Message$ > "" THEN
FOR Count = 1 TO UBOUND(FileName$)
IF UCASE$(Message$) = UCASE$(FileName$(Count)) THEN
EndMenu = 2
EXIT FOR
END IF
NEXT Count
IF EndMenu = 0 THEN '*** Check the file can be created ***
ON ERROR GOTO FileErr
FileNo = FREEFILE
OPEN Message$ FOR OUTPUT ACCESS WRITE LOCK READ WRITE AS FileNo
CLOSE
IF ProgErr = 0 THEN EndMenu = 3
'*** If there was a problem creating this file then ProgErr = 1 ***
ON ERROR GOTO OopsError
END IF
END IF
END SUB
SUB DoDirMenu
'*** Scrolling directory menu ***
DIM UserIn AS STRING 'User input
DIM ChngTab AS INTEGER 'Tracks if menu movement keys are pressed
STATIC FirstDir AS INTEGER 'The first directory to be displayed
DIM YDirPosn AS INTEGER '
ChngTab = 0
IF HiLiteDir = 0 THEN HiLiteDir = 1
IF FirstDir = 0 THEN FirstDir = 1
XDirPosn = XMenuPosn + 59
LOCATE (YMenuPosn + 9) + HiLiteDir - FirstDir, XDirPosn
COLOR 0, 7
PRINT DirName$(HiLiteDir);
COLOR 7, 0
DO
'*** Print the up / down arrows ***
LOCATE YMenuPosn + 9, XMenuPosn + 71
PRINT " ";
LOCATE YMenuPosn + 18, XMenuPosn + 71
PRINT " ";
IF FirstDir > 10 THEN
LOCATE YMenuPosn + 9, XMenuPosn + 71
PRINT CHR$(24); CHR$(24)
END IF
IF FirstDir < NumDirs - 9 THEN
LOCATE YMenuPosn + 18, XMenuPosn + 71
PRINT CHR$(25); CHR$(25)
END IF
LOCATE (YMenuPosn + 9) + HiLiteDir - FirstDir, XDirPosn + LEN(DirName$(HiLiteDir))
DO
UserIn$ = INKEY$
LOOP UNTIL UserIn$ <> ""
'*** Process the key pressed ***
SELECT CASE UserIn$
CASE CHR$(27) '*** Esc pressed ***
EndMenu = 1
CASE CHR$(13) '*** Enter pressed ***
'*** Check the directory exists, if it does change it ***
'*** This should'nt be necessary but it may have ***
'*** been deleted while this program was running ***
ChkDir
CASE CHR$(9) '*** Tab pressed ***
TabNum = 1
ChngTab = 1
CASE CHR$(0) + CHR$(15) '*** Shift Tab pressed ***
TabNum = TabNum - 1
ChngTab = 1
CASE CHR$(0) + CHR$(80) '*** Down arrow pressed ***
IF DirName$(HiLiteDir + 1) <> "" THEN HiLiteDir = HiLiteDir + 1
CASE CHR$(0) + CHR$(72) '*** Up arrow pressed ***
IF HiLiteDir - 1 <> 0 THEN HiLiteDir = HiLiteDir - 1
CASE CHR$(0) + CHR$(71) '*** Home pressed ***
HiLiteDir = 1
CASE CHR$(0) + CHR$(79) '*** End pressed ***
HiLiteDir = NumDirs
CASE CHR$(0) + CHR$(73) '*** Page Up pressed ***
IF HiLiteDir > 10 THEN
HiLiteDir = HiLiteDir - 10
ELSE
HiLiteDir = 1
END IF
CASE CHR$(0) + CHR$(81) '*** Page Down pressed ***
IF HiLiteDir < NumDirs - 9 THEN
HiLiteDir = HiLiteDir + 10
ELSE
HiLiteDir = NumDirs
END IF
CASE ELSE
'*** For any other key press find the first directory ***
'*** starting with that key ***
FOR Count = 1 TO NumDirs
IF UCASE$(LEFT$(DirName$(Count), 1)) = UCASE$(UserIn$) THEN
HiLiteDir = Count
EXIT FOR
END IF
NEXT Count
END SELECT
'*** Clear the directory list area and reprint the new list ***
XDirPosn = XMenuPosn + 59
LOCATE YMenuPosn + 9, XDirPosn
FOR Count = FirstDir TO FirstDir + 9
PRINT SPACE$(LEN(DirName$(Count)));
LOCATE CSRLIN + 1, XDirPosn
NEXT Count
IF HiLiteDir > FirstDir + 9 THEN
DO
FirstDir = FirstDir + 10
LOOP UNTIL HiLiteDir < FirstDir + 10
END IF
IF HiLiteDir < FirstDir THEN
DO
FirstDir = FirstDir - 10
LOOP UNTIL HiLiteDir >= FirstDir
END IF
XDirPosn = XMenuPosn + 59
LOCATE YMenuPosn + 9, XDirPosn
FOR Count = FirstDir TO FirstDir + 9
PRINT DirName$(Count);
LOCATE CSRLIN + 1, XDirPosn
NEXT Count
XDirPosn = XMenuPosn + 59
LOCATE (YMenuPosn + 9) + HiLiteDir - FirstDir, XDirPosn
COLOR 0, 7
PRINT DirName$(HiLiteDir);
COLOR 7, 0
LOOP UNTIL EndMenu > 0 OR ChngTab = 1
END SUB
SUB DoFileMenu
'*** Scrolling Dir menu ***
DIM UserIn AS STRING 'User input
DIM ChngTab AS INTEGER 'Tracks if menu movement keys are pressed
DIM NumCols AS INTEGER 'Total number of Dir columns
STATIC FirstFile AS INTEGER 'The first file to be displayed
DIM XFilePosn AS INTEGER '
NumCols = FIX((NumFiles + 9) / 10) '*** calculates the number of columns ***
ChngTab = 0
IF HiLiteFile = 0 THEN HiLiteFile = 1
IF HiLiteFile = 1 THEN FirstFile = 1
IF FirstFile = 0 THEN FirstFile = 1
XFilePosn = XMenuPosn + 2
LOCATE (YMenuPosn + 9) + ((HiLiteFile - FirstFile) MOD 10), XFilePosn + (FIX((HiLiteFile - FirstFile) / 10) * 14)
COLOR 0, 7
PRINT FileName$(HiLiteFile);
COLOR 7, 0
DO
'*** Print the << and / or the >> ***
LOCATE YMenuPosn + 7, XMenuPosn + 2
PRINT " "
LOCATE YMenuPosn + 7, XMenuPosn + 54
PRINT " "
IF FirstFile > 10 THEN
LOCATE YMenuPosn + 7, XMenuPosn + 2
PRINT "<<"
END IF
IF FirstFile < NumFiles - 40 THEN
LOCATE YMenuPosn + 7, XMenuPosn + 54
PRINT ">>"
END IF
IF HiLiteFile > 1 THEN LOCATE (YMenuPosn + 9) + ((HiLiteFile - FirstFile) MOD 10), XFilePosn + (FIX((HiLiteFile - FirstFile) / 10) * 14) + LEN(FileName$(HiLiteFile))
IF HiLiteFile = 1 THEN LOCATE (YMenuPosn + 9), XFilePosn + LEN(FileName$(HiLiteFile))
DO
UserIn$ = INKEY$
LOOP UNTIL UserIn$ <> ""
'*** Clear the user entry file box ***
LOCATE CSRLIN, POS(0) - LEN(FileName$(HiLiteFile))
COLOR 7, 0
PRINT FileName$(HiLiteFile);
LOCATE YMenuPosn + 1, XFilePosn + 13
PRINT SPACE$(58)
'*** Process the key pressed ***
SELECT CASE UserIn$
CASE CHR$(27) '*** Esc pressed ***
EndMenu = 1
CASE CHR$(13) '*** Enter pressed ***
'*** ChkFile shouldn't be necessary but the chosen file ***
'*** may have been deleted while this program was running ***
ChkFile
CASE CHR$(9) '*** Tab pressed ***
TabNum = TabNum + 1
ChngTab = 1
CASE CHR$(0) + CHR$(15) '*** Shift Tab pressed ***
TabNum = TabNum - 1
ChngTab = 1
CASE CHR$(0) + CHR$(80) '*** Down arrow pressed ***
IF FileName$(HiLiteFile + 1) <> "" THEN HiLiteFile = HiLiteFile + 1
CASE CHR$(0) + CHR$(72) '*** Up arrow pressed ***
IF HiLiteFile - 1 <> 0 THEN HiLiteFile = HiLiteFile - 1
CASE CHR$(0) + CHR$(75) '*** Left arrow pressed ***
IF HiLiteFile > 10 THEN HiLiteFile = HiLiteFile - 10
CASE CHR$(0) + CHR$(77) '*** Right arrow pressed ***
IF HiLiteFile < (NumCols - 1) * 10 + 1 THEN HiLiteFile = HiLiteFile + 10
IF HiLiteFile > NumFiles THEN HiLiteFile = NumFiles
CASE CHR$(0) + CHR$(71) '*** Home pressed ***
HiLiteFile = 1
CASE CHR$(0) + CHR$(79) '*** End pressed ***
HiLiteFile = NumFiles
CASE CHR$(0) + CHR$(73) '*** Page Up pressed ***
IF HiLiteFile > 40 THEN
HiLiteFile = HiLiteFile - 40
ELSE
HiLiteFile = 1
END IF
CASE CHR$(0) + CHR$(81) '*** Page Down pressed ***
IF HiLiteFile < NumFiles - 40 THEN
HiLiteFile = HiLiteFile + 40
ELSE
HiLiteFile = NumFiles
END IF
CASE ELSE
'*** For any other key press find the first file ***
'*** starting with that key ***
FOR Count = 1 TO NumFiles
IF UCASE$(LEFT$(FileName$(Count), 1)) = UCASE$(UserIn$) THEN
HiLiteFile = Count
EXIT FOR
END IF
NEXT Count
END SELECT
'*** Reprint the user entry file box ***
Message$ = FileName$(HiLiteFile)
LOCATE YMenuPosn + 1, XFilePosn + 13
PRINT Message$
'*** Clear the file list area and reprint the new columns ***
XFilePosn = XMenuPosn + 2
LOCATE YMenuPosn + 9, XFilePosn
FOR Count = FirstFile TO FirstFile + 39
PRINT SPACE$(12);
LOCATE CSRLIN + 1, XFilePosn
IF Count MOD 10 = 0 THEN
XFilePosn = XFilePosn + 14
LOCATE YMenuPosn + 9, XFilePosn
END IF
NEXT Count
IF HiLiteFile > FirstFile + 39 THEN
DO
FirstFile = FirstFile + 10
LOOP UNTIL HiLiteFile < FirstFile + 40
END IF
IF HiLiteFile < FirstFile THEN
DO
FirstFile = FirstFile - 10
LOOP UNTIL HiLiteFile >= FirstFile
END IF
XFilePosn = XMenuPosn + 2
LOCATE YMenuPosn + 9, XFilePosn
FOR Count = FirstFile TO FirstFile + 39
PRINT FileName$(Count);
LOCATE CSRLIN + 1, XFilePosn
IF Count MOD 10 = 0 THEN
XFilePosn = XFilePosn + 14
LOCATE YMenuPosn + 9, XFilePosn
END IF
NEXT Count
XFilePosn = XMenuPosn + 2
IF HiLiteFile > 1 THEN LOCATE (YMenuPosn + 9) + ((HiLiteFile - FirstFile) MOD 10), XFilePosn + (FIX((HiLiteFile - FirstFile) / 10) * 14)
IF HiLiteFile = 1 THEN LOCATE (YMenuPosn + 9), XFilePosn
COLOR 0, 7
PRINT FileName$(HiLiteFile);
COLOR 7, 0
LOOP UNTIL EndMenu > 0 OR ChngTab = 1
END SUB
SUB DoWriteMenu
'*** Do the user input for the file, drive and directory ***
DIM UserIn AS STRING 'User input
DIM CsrPosn AS INTEGER 'Cursor position
DIM InsFlag AS INTEGER 'The state of the Insert key
DIM ChngTab AS INTEGER 'Tracks if menu movement keys are pressed
DIM OldMessage AS STRING 'A copy of the original message
ChngTab = 0
IF TabNum = 1 THEN
IF HiLiteFile = 0 THEN
Message$ = FileName$(1)
ELSE
Message$ = FileName$(HiLiteFile)
END IF
END IF
IF TabNum = 2 THEN Message$ = CurrDrive$
IF TabNum = 3 THEN Message$ = CurrDir$
CsrPosn = LEN(Message$) + 1
DO
LOCATE YMenuPosn + (TabNum * 2) - 1, XMenuPosn + 15
PRINT Message$;
CsrPosn = LEN(Message$) + 1
IF InsFlag >= 0 THEN
LOCATE , XMenuPosn + 14 + CsrPosn, 1, 1
ELSE
LOCATE , XMenuPosn + 14 + CsrPosn, 1, 1, 30
END IF
UserIn$ = ""
DO
UserIn$ = INKEY$
LOOP UNTIL UserIn$ <> ""
SELECT CASE UserIn$
CASE CHR$(27) '*** Esc pressed ***
EndMenu = 1
CASE CHR$(8) '*** Backspace pressed ***
LOCATE , XMenuPosn + 15
PRINT SPACE$(LEN(Message$))
IF CsrPosn > 1 THEN
Message$ = LEFT$(Message$, CsrPosn - 2) + MID$(Message$, CsrPosn)
CsrPosn = CsrPosn - 1
END IF
CASE CHR$(0) + CHR$(83) '*** Delete key pressed ***
LOCATE , XMenuPosn + 15
PRINT SPACE$(LEN(Message$))
Message$ = LEFT$(Message$, CsrPosn - 1) + MID$(Message$, CsrPosn + 1)
CASE CHR$(0) + CHR$(82) '*** Ins key pressed ***
InsFlag = NOT InsFlag '*** Boolean toggle ***
CASE CHR$(0) + CHR$(75) '*** Left arrow pressed ***
IF CsrPosn > 1 THEN CsrPosn = CsrPosn - 1
CASE CHR$(0) + CHR$(77) '*** Right arrow pressed ***
IF CsrPosn <= LEN(Message$) THEN CsrPosn = CsrPosn + 1
CASE CHR$(0) + CHR$(71) '*** Home pressed ***
CsrPosn = 1
CASE CHR$(0) + CHR$(79) '*** End pressed ***
CsrPosn = LEN(Message$) + 1
CASE CHR$(0) + CHR$(15), CHR$(0) + CHR$(72) '*** Up arrow or SHIFT Tab pressed ***
LOCATE , XMenuPosn + 15
PRINT SPACE$(59);
LOCATE , XMenuPosn + 15
IF HiLiteFile = 0 THEN HiLiteFile = 1
IF TabNum = 1 THEN Message$ = FileName$(HiLiteFile)
IF TabNum = 2 THEN Message$ = CurrDrive$
IF TabNum = 3 THEN Message$ = CurrDir$
PRINT Message$
TabNum = TabNum - 1
IF TabNum = 0 THEN TabNum = 5
ChngTab = 1
CASE CHR$(9), CHR$(0) + CHR$(80) '*** Down arrow or Tab pressed ***
LOCATE , XMenuPosn + 15
PRINT SPACE$(59);
LOCATE , XMenuPosn + 15
IF HiLiteFile = 0 THEN HiLiteFile = 1
IF TabNum = 1 THEN Message$ = FileName$(HiLiteFile)
IF TabNum = 2 THEN Message$ = CurrDrive$
IF TabNum = 3 THEN Message$ = CurrDir$
PRINT Message$
TabNum = TabNum + 1
ChngTab = 1
CASE ELSE '*** Add / insert the character to messages ***
IF LEN(Message$) < 59 THEN
IF LEN(UserIn$) = 1 AND UserIn$ > CHR$(34) THEN
IF InsFlag = 0 THEN
Message$ = LEFT$(Message$, CsrPosn - 1) + UserIn$ + MID$(Message$, CsrPosn)
ELSE
Message$ = LEFT$(Message$, CsrPosn - 1) + UserIn$ + MID$(Message$, CsrPosn + 1)
END IF
CsrPosn = CsrPosn + 1
END IF
END IF
END SELECT
IF UserIn$ = CHR$(13) THEN
SELECT CASE TabNum
CASE 1 '*** Check the file exists, if it does then exit menu ***
ChkFile
CASE 2 '*** Check the drive exists, if it does then change the drive ***
ChkDrv
CASE 3
'*** Check the directory exists, if it does then change it
ChkDir
END SELECT
END IF
LOOP UNTIL EndMenu > 0 OR ChngTab = 1
END SUB
SUB DrawMenu
'*** Draw the fixed menu items ***
DIM FileCols AS INTEGER 'Number of columns in the file section
DIM Count AS INTEGER 'Loop counter
FileCols = NumFiles / 10
XFilePosn = 7
YFilePosn = 9
XDirPosn = 65
YDirPosn = 9
CLS
LOCATE YMenuPosn, XMenuPosn
PRINT CHR$(201);
PRINT STRING$(12, CHR$(205));
PRINT CHR$(209);
PRINT STRING$(60, CHR$(205));
PRINT CHR$(187)
LOCATE , XMenuPosn
PRINT CHR$(186);
LOCATE , XMenuPosn + 2
PRINT "File";
LOCATE , XMenuPosn + 13
PRINT CHR$(179);
LOCATE , XMenuPosn + 74
PRINT CHR$(186)
LOCATE , XMenuPosn
PRINT CHR$(199);
PRINT STRING$(12, CHR$(196));
PRINT CHR$(197);
PRINT STRING$(60, CHR$(196));
PRINT CHR$(182)
LOCATE , XMenuPosn
PRINT CHR$(186);
LOCATE , XMenuPosn + 2
PRINT "Drive";
LOCATE , XMenuPosn + 13
PRINT CHR$(179);
LOCATE , XMenuPosn + 74
PRINT CHR$(186)
LOCATE , XMenuPosn
PRINT CHR$(199);
PRINT STRING$(12, CHR$(196));
PRINT CHR$(197);
PRINT STRING$(60, CHR$(196));
PRINT CHR$(182)
LOCATE , XMenuPosn
PRINT CHR$(186);
LOCATE , XMenuPosn + 2
PRINT "Directory";
LOCATE , XMenuPosn + 13
PRINT CHR$(179);
LOCATE , XMenuPosn + 74
PRINT CHR$(186)
LOCATE , XMenuPosn
PRINT CHR$(199);
PRINT STRING$(12, CHR$(196));
PRINT CHR$(193);
PRINT STRING$(43, CHR$(196));
PRINT CHR$(194);
PRINT STRING$(16, CHR$(196));
PRINT CHR$(182)
LOCATE , XMenuPosn
PRINT CHR$(186);
LOCATE , XMenuPosn + 7
PRINT NumFiles; "files in "; CurrPath$;
LOCATE , XMenuPosn + 57
PRINT CHR$(179);
LOCATE , XMenuPosn + 58
PRINT NumDirs; "Directories";
LOCATE , XMenuPosn + 74
PRINT CHR$(186)
LOCATE , XMenuPosn
PRINT CHR$(199);
PRINT STRING$(56, CHR$(196));
PRINT CHR$(197);
PRINT STRING$(16, CHR$(196));
PRINT CHR$(182)
FOR Count = 1 TO 10
LOCATE , XMenuPosn
PRINT CHR$(186);
LOCATE , XMenuPosn + 57
PRINT CHR$(179);
LOCATE , XMenuPosn + 74
PRINT CHR$(186)
NEXT Count
LOCATE , XMenuPosn
PRINT CHR$(200);
PRINT STRING$(56, CHR$(205));
PRINT CHR$(207);
PRINT STRING$(16, CHR$(205));
PRINT CHR$(188)
LOCATE YMenuPosn + 21, XMenuPosn
PRINT "Use the TAB key to move between fields"
PRINT
END SUB
SUB FillMenu
'*** Fill in the variable menu items ***
DIM XFilePosn AS INTEGER ' X Positon of file columns
XFilePosn = XMenuPosn + 2
'*** The user writable part of the menu ***
LOCATE YMenuPosn + 3, XMenuPosn + 15
PRINT CurrDrive$
LOCATE YMenuPosn + 5, XMenuPosn + 15
PRINT CurrDir$
'*** the scrolling file menu ***
LOCATE YMenuPosn + 9, XFilePosn
FOR Count = 1 TO 40
PRINT FileName$(Count)
LOCATE CSRLIN, XFilePosn
IF Count MOD 10 = 0 THEN
XFilePosn = XFilePosn + 14
LOCATE YMenuPosn + 9, XFilePosn
END IF
NEXT Count
'*** The scrolling directory menu ***
LOCATE YMenuPosn + 9, XMenuPosn + 59
FOR Count = 1 TO 10
PRINT DirName$(Count)
LOCATE CSRLIN, XMenuPosn + 59
NEXT Count
END SUB
SUB FindDrives
'*** Find what drives are present in the PC ***
ON ERROR GOTO DriveErr
FOR Count = 65 TO 90
DrvErr = 0
Drive$ = CHR$(Count) + ":\"
FILES Drive$
IF DrvErr = 0 THEN Drive$ = Drive$ + CHR$(Count)
NEXT Count
END SUB
SUB GetCurrent
'*** Get the files from the current drive and directory ***
DIM Num AS INTEGER 'General number variable
DIM FileInfo AS STRING 'Line from ZFileDir.tmp
'*** Clear the current contents of the file and directory arrays ***
ERASE FileName$, DirName$
'*** Open a temporary file to hold the DIR information ***
'*** Unfortunately, I can't find a way of getting ***
'*** the location of the temporary file folder ***
FileNo = FREEFILE
SHELL "dir > c:\ZFileDir.tmp"
OPEN "C:\ZFileDir.tmp" FOR INPUT ACCESS READ LOCK READ WRITE AS FileNo
DO
LINE INPUT #FileNo, FileInfo$
IF EOF(FileNo) THEN EXIT DO
LOOP UNTIL INSTR(FileInfo$, "Directory of ") > 1
Num = INSTR(FileInfo$, ":")
Num = Num - 1
CurrDrive$ = MID$(FileInfo$, Num, 2)
CurrPath$ = MID$(FileInfo$, Num)
Num = Num + 2
CurrDir$ = MID$(FileInfo$, Num)
NumDirs = 0
NumFiles = 0
DO
LINE INPUT #FileNo, FileInfo$
IF EOF(FileNo) THEN EXIT DO
IF INSTR(FileInfo$, "file(s)") > 0 THEN EXIT DO
IF INSTR(FileInfo$, "<DIR>") > 1 THEN
NumDirs = NumDirs + 1
Num = INSTR(FileInfo$, " ")
Num = Num - 1
DirName$(NumDirs) = LEFT$(FileInfo$, Num)
IF DirName$(NumDirs) = "." THEN
DirName$(NumDirs) = ""
NumDirs = NumDirs - 1
END IF
ELSE
IF LEN(FileInfo$) > 0 THEN
NumFiles = NumFiles + 1
Num = INSTR(FileInfo$, " ")
Num = Num - 1
FileName$(NumFiles) = LEFT$(FileInfo$, Num) + "."
FileName$(NumFiles) = FileName$(NumFiles) + MID$(FileInfo$, 10, 3)
IF UCASE$(FileName$(NumFiles)) = "ZFILEDIR.TMP" THEN
FileName$(NumFiles) = ""
NumFiles = NumFiles - 1
END IF
END IF
END IF
LOOP UNTIL INSTR(FileInfo$, "file(s)") > 1
CLOSE
KILL "C:\ZFileDir.tmp"
END SUB
SUB SortArrays (SortArray$(), Low AS INTEGER, High AS INTEGER)
'*** This is a QuickSort routine ***
DIM Lower AS INTEGER
DIM Higher AS INTEGER
DIM RandIndex AS INTEGER
'QuickSort works by picking a random "pivot" element in SortArray, then
'moving every element that is bigger to one side of the pivot, and every
'element that is smaller to the other side. QuickSort is then called
'recursively with the two subdivisions created by the pivot. Once the
'number of elements in a subdivision reaches two, the recursive calls end
'and the array is sorted.
IF Low < High THEN
' *** Only two elements in this subdivision ***
' *** Swap them if they are out of order, then end recursive calls: ***
IF High - Low = 1 THEN
IF SortArray$(Low) > SortArray$(High) THEN
SWAP SortArray$(Low), SortArray$(High)
END IF
ELSE
'*** Pick a pivot element at random, then move it to the end ***
RandIndex = INT(RND * (High - Low + 1)) + Low
SWAP SortArray$(High), SortArray$(RandIndex)
Partition$ = SortArray$(High)
DO
'*** Move in from both sides towards the pivot element ***
Lower = Low
Higher = High
DO WHILE (Lower < Higher) AND (SortArray$(Lower) <= Partition$)
Lower = Lower + 1
LOOP
DO WHILE (Higher > Lower) AND (SortArray$(Higher) >= Partition$)
Higher = Higher - 1
LOOP
'*** If pivot element not reached, it means that ***
'*** two elements on either side are out of order, ***
'*** so swap them ***
IF Lower < Higher THEN
SWAP SortArray$(Lower), SortArray$(Higher)
END IF
LOOP WHILE Lower < Higher
'*** Move the pivot element back to its proper place in the array ***
SWAP SortArray$(Lower), SortArray$(High)
'*** Recursively call the SortArray sub ***
'*** Pass the smaller subdivision first to use less stack space ***
IF (Lower - Low) < (High - Lower) THEN
SortArrays SortArray$(), Low, Lower - 1
SortArrays SortArray$(), Lower + 1, High
ELSE
SortArrays SortArray$(), Lower + 1, High
SortArrays SortArray$(), Low, Lower - 1
END IF
END IF
END IF
END SUB
Until I wrote this program I assumed that QBasic 1.1, which is the version I use, would run the same no matter what version of Windows is being used. This is erroneous. I use Windows 2000, but people using different versions of Windows have reported that the program fails when reading from the B drive. As a quick fix for this, rewrite the lines near the top of the program :-
ON ERROR GOTO DriveErr
FOR Count = 65 TO 90
ProgErr = 0
Message$ = CHR$(Count) + ":\"
FILES Message$
IF ProgErr = 0 THEN Drive$ = Drive$ + CHR$(Count)
NEXT Count
So that they become :-
Drive$ = "A"
ON ERROR GOTO DriveErr
For Count = 67 TO 90
ProgErr = 0
Message$ = CHR$(Count) + ":\"
etc. etc.
This isn't the best solution, as the drive list is now preset with an A drive, then misses B altogether before carrying from C, but it's the best I can do off the top of my head.
Another concern is the DIR listing. If the 8.3 file names aren't at the far left position as shown i the DIR listing from within QBasic then you are going to have to edit the subroutine GetCurrent. Find the lines :-
Num = INSTR(FileInfo$, " ") Num = Num - 1 FileName$(NumFiles) = LEFT$(FileInfo$, Num) + "." FileName$(NumFiles) = FileName$(NumFiles) + MID$(FileInfo$, 10, 3)
and replace them with :-
Num = INSTR(fns, FileInfo$, " ") Num = Num - 1 FileName$(NumFiles) = MID$(FileInfo$, fns, Num) + "." FileName$(NumFiles) = FileName$(NumFiles) + MID$(FileInfo$, fes, 3)
where "fns" is the start of the file name position and "fes" is the start of the file extension position inside temp.tmp
Do not use the long file names as the program is designed to use the short file names and I don't quite know what will happen, but I can guess they'll give some weird displays.
Here's what is happening, and why these differences are appearing.
As I understand it, part of the reason why Windows crashes is because of it's memory management. Programs ask for certain areas of memory to be reserved for it, Windows goes "OK, you can have such and such addresses and I'll reserve them for you". Unfortunately, Windows tells fibs, and the next program which wants to reserve memory gets all or part of the first programs reserved memory. Hence you get the blue screen of death, which blames the program for using an illegal function. The fact is, it happened because Windows didn't do what it was supposed to.
To help stop this happening, in protected mode, newer versions of Windows (and NT) protects memory so well it HAS to start a new version of command.com or cmd.exe depending on whether the program is 16 or 32 bit. For the most part this works very well, I haven't had a GPF since I got Windows 2000, the downside is that it limits what SHELL can do.
In April 2002, Robert Goddard sent me a copy of a filer30.bas that he has written. This gives a graphical interface to the file dialog. Bob writes about the program . . .
It can be easily tacked on to the front of any other program.
When running you use the arrow keys to move around.
Ctrl * to set a drive-e.g to go to A drive press CTRL a.
Return enables you to go into a folder or select a file.
Escape exits you
Pressing a letter by itself takes you to the next file or folder beginning with that letter.
To search for a folder use set("",0)
To search for a "***" file type use set ("***",-1) where
*** is a file extension e.g. exe txt doc etc
To search for any file use set("",-1)
When the subroutine exits the file or folder is printed on the screen, of course at this stage you can now write your own code to edit/delete the file or whatever.
In July 2002, Robert sent me Filer46, a completely updated version of Filer30. Bob says this about the program ...
The program should work with any version of DOS as it uses the Basic FILES command which I have found unlike the DOS dir command always give output in the same format regardless of the type of DOS being used.
One slight problem I found was that the program stops (unsurprisingly) if you try to delete a file with read only attributes. I did write an earlier version which used the DOS attrib command to remove the "R" attribute from the file first. For some reason this made hash of the screen when I was running it on my work Windows NT PC. So that the program runs ok on any version of DOS I have left the shell "attrib" command rem'd out on the final version but you can reinstate it if you wish.
Files and folders are displayed with simplified �icons�. The current folder is displayed on the top line of the screen. The files and folders contained in the folder are displayed on the screen below. The current file or folder is highlighted in inverse print. Key presses available are :-
Escape - The clipboard is cleared, and if the highlighted item is a *.exc folder then the project highlighted is loaded.
Shift Followed By A Letter - The drive is selected. E.g. to select the floppy disk drive press Shift a
A Lower Case Letter Or A Number From 0 To 9 - This takes the highlighter to the next file or folder beginning with the letter pressed.
Arrow keys (not those on the number pad) - Used to move the highlighter around the screen.
Page Up - This takes the screen up a level in the filing system towards the root of the drive.
Enter - Opens a folder or actions a *.shc file (if any are on the computer). A *.shc file is effectively a DOS shortcut. For example if a *.shc file containing the plain text C:\Projects\Site1 only is actioned then the computer would move to that part of the filing system. If desired *.shc DOS shortcuts can be created by the use of DOS Edit, Notepad or any other Word Processor.
Delete - Following confirmation in a dialogue box at the bottom right of the screen the currently highlighted file or folder is permanently deleted.
Ctrl-Pause - Pressing these together at any time ceases execution of any QBasic program and returns the user to QBasic.
Ctrl A - This copies all the files (not folders) to the clipboard.
Ctrl C - This copies the currently selected file or folder to the clipboard.
Ctrl D - Following confirmation in a dialogue box at the bottom right of the screen all files (not folders) are permanently deleted.
Ctrl E - This enables the renaming of a *.exc folder in a dialogue box at the bottom right of the screen.
Ctrl N - This enables the creation of a new folder in a dialogue box at the bottom right of the screen.
Ctrl V - This pastes the contents of the clipboard and empties the clipboard at the same time.
Ctrl R - This enables the renaming of a file in a dialogue box at the bottom right of the screen.
Ctrl S - When a folder is highlighted this creates a *.shc shortcut file for subsequent actioning if wished by selection with the enter key.
Ctrl F - This enables the currently highlighted file to be split into small segments for easy transfer to another computer via floppy disk.
Ctrl J - This enables the currently highlighted file to be rejoined following transfer to the computer via floppy disk. See last.
Ctrl Q - This enables the password-based encryption of a file. De-encryption is exactly the same process and must be done with exactly the same password.
Ctrl T - The currently highlighted file�s contents are printed on the screen.
Ctrl Z - This key press either compresses all the files into a *.cpd file or uncompresses a *.cpd file.
The programs on this page, like all the programs written for this site, can be downloaded from the DLoads page.
QBasic | Errors | 40lb Weight | Bits | Chance | Colours | Dates | Delays | File Dialog | Files | Input | Matching | Menus | Mouse | Numbers | SeqNo | SIRDS | Sorts | Text | Timer | DLoads
HomePage | Optical Illusions | War Stories | QBasic | Dads Navy Days | Bristol | Bristol, USA | Bristol, Canada | Terre Haute | Miscellany | Web Stuff | About Ray | Site Map | Site Search | Messages | Credits | Links | Web Rings