home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Database / CLIPR503.W96 / FILEMAN.PR_ / FILEMAN.PR
Text File  |  1995-06-20  |  37KB  |  1,390 lines

  1. /***
  2. *
  3. *  Fileman.prg
  4. *
  5. *  Sample file manager that can be linked into your programs.
  6. *
  7. *  Copyright (c) 1993-1995, Computer Associates International Inc.
  8. *  All rights reserved.
  9. *
  10. *  NOTE: Compile with /n /w
  11. *
  12. */
  13.  
  14. #include "Fileman.ch"
  15. #include "Directry.ch"
  16. #include "Inkey.ch"
  17. #include "Memoedit.ch"
  18. #include "Achoice.ch"
  19.  
  20. #define GV_BLOCKSIZE    50000    // GenericViewer() file buffer size
  21.  
  22. STATIC aFileMan      // Stores all of FileMan()'s important information
  23. STATIC aFileList     // Stores the current directory's information
  24. STATIC hScrollBar    // FileMan() scroll bar information
  25. STATIC nMenuItem     // Currently selected menu item
  26. STATIC nTagged       // Number of currently tagged files
  27. STATIC nEl           // Current element (for ACHOICE())
  28. STATIC nRel          // Relative row (for ACHOICE())
  29. STATIC lReloadDir    // Indicates if directory info should be refreshed
  30. STATIC nFileItem     // Current selection in aFileList
  31. MEMVAR GetList       // Declares PUBLIC GetList to the compiler
  32.  
  33. /***
  34. *
  35. *  FileMan( <nRowTop>, <nColumnTop>, <nRowBottom>,
  36. *           [<cColorString>], [<cDefaultPath>] ) --> cDOSFileName
  37. *
  38. *  Description:
  39. *          FileMan() is a high-level function that displays a list of
  40. *          files in a scrolling pick list.  It is best implemented when
  41. *          a user is required to select a file from a disk and perform
  42. *          some type of action on it.
  43. *
  44. *          Navigating through the list of files is accomplished by the
  45. *          up and down arrow keys.  The left and right arrow keys are
  46. *          used to move through the menu options.  Pressing Return will
  47. *          activate the highlighted menu option for the file that is
  48. *          highlighted from the pick list.  The Copy and
  49. *          Delete options can also be performed on tagged files.  Tagging
  50. *          a file is accomplished by pressing the Space Bar while the
  51. *          file is highlighted.  A "tag all" toggle, F5, can be used to
  52. *          tag and untag all the files in the current pick list.
  53. *
  54. *          The menu items in FileMan() are:
  55. *
  56. *          Look, Copy, Rename, Delete, Print and Options
  57. *
  58. *          a brief description of each follows:
  59. *
  60. *          Look:    Views the currently highlighted file in a "raw text"
  61. *                   window, allowing the user to scroll though it.  If
  62. *                   their is a file viewer for the extension of that
  63. *                   file, a file viewer is loaded and the file is viewed
  64. *                   in it's native form.  This version has viewers for:
  65. *                   DBF (Clipper/dBASE database file), NTX (Clipper index),
  66. *                   LBL (Label form) and FRM (Report form).
  67. *
  68. *          Copy:    Copies the selected file(s) to a specific location,
  69. *                   either on another drive or in another directory.
  70. *
  71. *          Rename:  Renames the selected file to a new name.
  72. *
  73. *          Delete:  Deletes the selected file(s).
  74. *
  75. *          Print:   Prints the selected files to the printer in raw form.
  76. *
  77. *          Options: Displays a second menu with choices for sorting the
  78. *                   files, a call to internal help and an "about
  79. *                   FileMan()" screen.
  80. *
  81. *  Parameters:
  82. *     nRowTop      - Upper window coordinate
  83. *     nColumnTop   - Left window coordinate
  84. *     nRowBottom   - Lower window coordinate
  85. *     cColorString - Optional color string: files are displayed in the
  86. *                    standard color, highlight is in enhanced color, and
  87. *                    hidden/system files are displayed in the unselected
  88. *                    color.  If <cColorString> is not specified, the
  89. *                    current default color string will be used.
  90. *     cDefaultPath - Optional initial file path
  91. *
  92. *
  93. *  Example call:
  94. *
  95. *          FileMan( 1, 5, 18, "C:\DBFILES\*.EXE" )
  96. *
  97. *          Displays a FileMan() file chooser from row 1, column 5 to
  98. *          row 18.  The FileMan() menu only displays the files in the
  99. *          C:\DBFILES\ directory that have an EXE extension.
  100. *
  101. *
  102. *  Returns:
  103. *          FileMan() returns the full path and file name of the file
  104. *          selected.  For example, selecting DBU.EXE from the
  105. *          \CLIPPER5\BIN directory on the C: drive would result in a
  106. *          return character string of "C:\CLIPPER5\BIN\DBU.EXE"
  107. *
  108. *          If no file was selected a null ("") string is returned.
  109. *
  110. */
  111. FUNCTION FileMan( nRowTop, nColumnTop, nRowBottom, ;
  112.                   cColorString, cDefaultPath       )
  113.  
  114.    LOCAL lSetScore
  115.  
  116.    // Set the default values
  117.    nMenuItem   := 1
  118.    nTagged     := 0
  119.    nFileItem   := 1
  120.    nEl         := 1
  121.    nRel        := 1
  122.    lReloadDir  := .T.
  123.    aFileMan    := {}
  124.    aFileList   := {}
  125.  
  126.    // Create the array
  127.    aFileMan := ARRAY( FM_ELEMENTS )
  128.  
  129.    // Resolve parameters
  130.    IF nRowTop = NIL
  131.       nRowTop := 0
  132.    ELSE
  133.       IF nRowTop > (MAXROW() - 7)
  134.          nRowTop := MAXROW() - 7
  135.       ENDIF
  136.    ENDIF
  137.    aFileMan[ FM_ROWTOP ] := nRowTop
  138.  
  139.    IF nColumnTop = NIL
  140.       nColumnTop := 0
  141.    ELSE
  142.       IF nColumnTop > (MAXCOL() - 52)
  143.          nColumnTop := MAXROW() - 52
  144.       ENDIF
  145.    ENDIF
  146.    aFileMan[ FM_COLTOP ] := nColumnTop
  147.  
  148.    IF nRowBottom = NIL
  149.       nRowBottom := 0
  150.    ELSE
  151.       IF nRowBottom > MAXROW()
  152.          nRowBottom := MAXROW()
  153.       ENDIF
  154.    ENDIF
  155.    aFileMan[ FM_ROWBOTTOM ] := nRowBottom
  156.    aFileMan[ FM_COLBOTTOM ] := nColumnTop + 51
  157.  
  158.    // Color string for FileMan()
  159.    IF cColorString = NIL
  160.       cColorString := SETCOLOR()
  161.    ENDIF
  162.    aFileMan[ FM_COLOR ] := cColorString
  163.  
  164.    // Initial path information
  165.    IF cDefaultPath = NIL
  166.       cDefaultPath := "\" + CURDIR() + "\*.*"
  167.       cDefaultPath := STRTRAN( cDefaultPath, "\\", "\" )
  168.    ENDIF
  169.    aFileMan[ FM_PATH ] := cDefaultPath
  170.  
  171.    // Save the old color
  172.    aFileMan[ FM_OLDCOLOR ] := SETCOLOR( aFileMan[ FM_COLOR ] )
  173.  
  174.    // Save the old work area
  175.    aFileMan[ FM_OLDSELECT ] := SELECT()
  176.  
  177.    // Set the scoreboard
  178.    lSetScore := SET( _SET_SCOREBOARD, .F. )
  179.  
  180.    // Save the screen
  181.    aFileMan[ FM_OLDSCREEN ] := SAVESCREEN( aFileMan[ FM_ROWTOP    ], ;
  182.                                            aFileMan[ FM_COLTOP    ], ;
  183.                                            aFileMan[ FM_ROWBOTTOM ], ;
  184.                                            aFileMan[ FM_COLBOTTOM ] )
  185.  
  186.    //
  187.    // Here's the heart of the program
  188.    //
  189.    CreateScreen()       // Create the initial screen, etc.
  190.    GetFiles()           // Call the actual file chooser
  191.  
  192.    // Restore the screen
  193.    RESTSCREEN( aFileMan[ FM_ROWTOP    ], ;
  194.                aFileMan[ FM_COLTOP    ], ;
  195.                aFileMan[ FM_ROWBOTTOM ], ;
  196.                aFileMan[ FM_COLBOTTOM ], ;
  197.                aFileMan[ FM_OLDSCREEN ] )
  198.  
  199.    // Restore the color
  200.    SetColor( aFileMan[ FM_OLDCOLOR ] )
  201.  
  202.    // Reset the old scoreboard stuff
  203.    SET( _SET_SCOREBOARD, lSetScore )
  204.  
  205.    // Restore the work area
  206.    SELECT ( aFileMan[ FM_OLDSELECT ] )
  207.  
  208.    // Back to the real world!
  209.    RETURN ( aFileMan[ FM_RETURNFILE ] )
  210.  
  211.  
  212.  
  213. /***
  214. *
  215. *  GetFiles() --> NIL
  216. *
  217. *
  218. */
  219. STATIC FUNCTION GetFiles()
  220.    
  221.    LOCAL lDone       := .F.            // Primary loop point
  222.    LOCAL nCurrent    := 0              // ACHOICE() result
  223.    LOCAL nLastKey    := 0              // Last value in LASTKEY()
  224.  
  225.    DO WHILE !lDone
  226.       IF lReloadDir
  227.          nEl   := 1
  228.          nRel  := 1
  229.          IF !LoadFiles()
  230.             
  231.             // A problem occured loading the file names; tell the user
  232.             ErrorBeep()
  233.             Message( "ERROR: No files found!  Press any key..." )
  234.             INKEY( 300 )
  235.             IF YesOrNo( "Would you like to try another path? (Y/N)", "Y" )
  236.                GetNewPath( aFileMan[ FM_PATH ] )
  237.                IF LASTKEY() == K_ESC
  238.                   lDone := .T.
  239.                ELSE
  240.                   LOOP
  241.                ENDIF
  242.             ELSE
  243.                lDone := .T.
  244.             ENDIF
  245.          ELSE
  246.             lReloadDir := .F.
  247.          ENDIF
  248.       ENDIF
  249.  
  250.       // Time to display the files and act on the response's
  251.       TabUpdate( hScrollBar, nEl, LEN( aFileList ), .T. )
  252.       nCurrent := ACHOICE( aFileMan[ FM_ROWTOP ] + 3, ;
  253.                            aFileMan[ FM_COLTOP ] + 2, ;
  254.                            aFileMan[ FM_ROWBOTTOM ] - 3, ;
  255.                            aFileMan[ FM_COLBOTTOM ] - 4, ;
  256.                            aFileList, .T., "ProcessKey", nEl, nRel )
  257.  
  258.       nFileItem := nCurrent
  259.       nLastKey := LASTKEY()
  260.  
  261.       DO CASE
  262.          CASE UPPER(CHR(nLastKey)) $ "LCRDPO"
  263.             // They selected a menu item ; move the highlight
  264.             nMenuItem := AT( UPPER(CHR(nLastKey)), "LCRDPO" )
  265.             DisplayMenu()
  266.  
  267.          CASE nLastKey == K_RIGHT
  268.             nMenuItem++
  269.             IF nMenuItem > 6
  270.                TONE( 900, 1 )
  271.                nMenuItem := 6
  272.             ENDIF
  273.             DisplayMenu()
  274.  
  275.          CASE nLastKey == K_LEFT
  276.             nMenuItem--
  277.             IF nMenuItem < 1
  278.                TONE( 900, 1 )
  279.                nMenuItem := 1
  280.             ENDIF
  281.             DisplayMenu()
  282.  
  283.          CASE nLastKey == K_ESC
  284.             aFileMan[ FM_RETURNFILE ] := ""
  285.             lDone := .T.
  286.  
  287.          CASE nLastKey == K_ENTER
  288.             
  289.             // First let's assign the filename and path to aFileMan
  290.             aFileMan[ FM_RETURNFILE ] := ;
  291.                      SUBSTR( aFileMan[ FM_PATH ], 1, ;
  292.                      RAT( "\", aFileMan[ FM_PATH ] ) ) + ;
  293.                      TRIM( SUBSTR( aFileList[ nCurrent ], 1, 12 ) )
  294.  
  295.             // Ok, here's the biggee
  296.             DO CASE
  297.                CASE nMenuItem == MN_LOOK
  298.                   LookAtFile()
  299.  
  300.                CASE nMenuItem == MN_COPY
  301.                   CopyFile()
  302.  
  303.                CASE nMenuItem == MN_RENAME
  304.                   RenameFile()
  305.  
  306.                CASE nMenuItem == MN_DELETE
  307.                   DeleteFile()
  308.  
  309.                CASE nMenuItem == MN_PRINT
  310.                   PrintFile()
  311.  
  312.                CASE nMenuItem == MN_OPEN
  313.                   IF AT( '<dir>', aFileList[ nFileItem ] ) = 0
  314.                      lDone := .T.
  315.                   ELSE
  316.                      LookAtFile()
  317.                   ENDIF
  318.  
  319.             ENDCASE
  320.  
  321.          CASE nLastKey == K_DEL
  322.             DeleteFile()
  323.  
  324.          CASE nLastKey == K_F5
  325.             TagAllFiles()
  326.  
  327.          CASE nLastKey == K_F6
  328.             UnTagAllFiles()
  329.  
  330.          CASE nLastKey == K_SPACE
  331.             // Can't tag directories
  332.             IF AT( "D", SUBSTR( aFileList[ nCurrent ], 43, 6 ) ) == 0
  333.                IF SUBSTR( aFileList[ nCurrent ], 14, 1 ) == " "
  334.                   // It isn't tagged, let's tag it
  335.                   aFileList[ nCurrent ] := STUFF( aFileList[ nCurrent ], ;
  336.                                            14, 1, FM_CHECK )
  337.                   nTagged++
  338.                ELSE
  339.                   // It's already tagged, let's remove the check mark
  340.                   aFileList[ nCurrent ] := STUFF( aFileList[ nCurrent ], ;
  341.                                            14, 1, " " )
  342.                   nTagged--
  343.                ENDIF
  344.             ENDIF
  345.  
  346.       ENDCASE
  347.  
  348.    ENDDO
  349.  
  350.    RETURN ( NIL )
  351.  
  352.  
  353.  
  354. /***
  355. *
  356. *  LoadFiles() --> lReturnValue 
  357. *
  358. *
  359. */
  360. STATIC FUNCTION LoadFiles()
  361.    
  362.    LOCAL aDirectory     := {}
  363.    LOCAL nItem          := 0
  364.    LOCAL lReturnValue   := .T.
  365.    LOCAL nNumberOfItems := 0
  366.    LOCAL cFileString    := ""
  367.  
  368.    // Let the user know what's going on
  369.    Message( "Loading the current directory..." )
  370.    @ aFileMan[ FM_ROWTOP ] + 3, aFileMan[ FM_COLTOP ] + 2 CLEAR TO ;
  371.      aFileMan[ FM_ROWBOTTOM ] - 3, aFileMan[ FM_COLBOTTOM ] - 4
  372.  
  373.    // Load up aFileList with the current directory information
  374.    aDirectory := DIRECTORY( aFileMan[ FM_PATH ], "D" )
  375.    nNumberOfItems := IF( VALTYPE( aDirectory ) != "A", 0, LEN( aDirectory ) )
  376.    aFileList := {}                  // Wipe out the old aFileList
  377.  
  378.    // Check to see if any files actually made it
  379.    IF nNumberOfItems < 1
  380.       
  381.       // Problem!
  382.       lReturnValue := .F.
  383.  
  384.    ELSE
  385.       // Let the user know what's going on
  386.       Message( "Sorting the current directory..." )
  387.  
  388.       // Sort the current aDirectory array
  389.       ASORT( aDirectory,,, { | x, y | x[ F_NAME ] < y[ F_NAME ] } )
  390.  
  391.       // Let the user know what's going on
  392.       Message( "Processing the current directory..." )
  393.  
  394.       // Now drop it into the array to be displayed with ACHOICE()
  395.       FOR nItem := 1 TO nNumberOfItems
  396.          AADD( aFileList, PADR( aDirectory[ nItem, F_NAME ], 15 ) + ;
  397.                           IF( SUBSTR( aDirectory[ nItem, F_ATTR ], ;
  398.                           1, 1 ) == "D", "   <dir>", ;
  399.                           STR( aDirectory[ nItem, F_SIZE ], 8 ) ) + "  " + ;
  400.                           DTOC( aDirectory[ nItem, F_DATE ] ) + "  " + ;
  401.                           SUBSTR( aDirectory[ nItem, F_TIME ], 1, 5) + "  " + ;
  402.                           SUBSTR( aDirectory[ nItem, F_ATTR ], 1, 4 ) + "  " )
  403.       NEXT
  404.  
  405.    ENDIF
  406.  
  407.    // Clean up the message area before we leave
  408.    Message( aFileMan[ FM_PATH ] )
  409.  
  410.    RETURN ( lReturnValue )
  411.  
  412.  
  413.  
  414. /***
  415. *
  416. *  ProcessKey( <nStatus>, <nElement>, <nRelative> ) --> nReturnValue
  417. *
  418. *
  419. */
  420. FUNCTION ProcessKey( nStatus, nElement, nRelative )
  421.    
  422.    LOCAL nReturnValue := AC_CONT    // Set the default handler to continue
  423.  
  424.    // Update the global element/relative with the passed versions
  425.    nEl  := nElement
  426.    nRel := nRelative
  427.  
  428.    DO CASE
  429.    CASE nStatus == AC_IDLE
  430.       // Update the scroll bar
  431.       TabUpdate( hScrollBar, nElement, LEN( aFileList ) )
  432.       Message( aFileMan[ FM_PATH ] )
  433.  
  434.    CASE nStatus == AC_HITTOP .OR. nStatus == AC_HITBOTTOM
  435.       // Tried to go too far!
  436.       TONE( 900, 1 )
  437.  
  438.    CASE nStatus == AC_EXCEPT
  439.       // Keystroke exception
  440.       DO CASE
  441.       CASE LASTKEY() == K_ESC
  442.          nReturnValue := AC_ABORT
  443.  
  444.       CASE LASTKEY() == K_HOME
  445.          KEYBOARD CHR( K_CTRL_PGUP )
  446.          nReturnValue := AC_CONT
  447.  
  448.       CASE LASTKEY() == K_END
  449.          KEYBOARD CHR( K_CTRL_PGDN )
  450.          nReturnValue := AC_CONT
  451.  
  452.       CASE LASTKEY() == K_LEFT .OR. LASTKEY() == K_RIGHT
  453.          nReturnValue := AC_SELECT
  454.  
  455.       CASE UPPER(CHR(LASTKEY())) $ ;
  456.          "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 " .OR. ;
  457.          LASTKEY() == K_DEL .OR. LASTKEY() == K_ENTER .OR. ;
  458.          LASTKEY() == K_F5 .OR. LASTKEY() == K_F6
  459.  
  460.          nReturnValue := AC_SELECT
  461.  
  462.       ENDCASE
  463.  
  464.    ENDCASE
  465.  
  466.    RETURN ( nReturnValue )
  467.  
  468.  
  469.  
  470. /***
  471. *
  472. *  Message( cString ) --> nil
  473. *
  474. *
  475. */
  476. STATIC FUNCTION Message( cString )
  477.    
  478.    LOCAL cOldColor := SETCOLOR( aFileMan[ FM_COLOR ] )
  479.  
  480.    ClearMessage()
  481.    @ aFileMan[ FM_ROWBOTTOM ] - 1, aFileMan[ FM_COLTOP ] + 2 SAY ;
  482.       SUBSTR( cString, 1, (aFileMan[FM_COLBOTTOM] - aFileMan[FM_COLTOP] - 6 ))
  483.  
  484.    SETCOLOR( cOldColor )
  485.  
  486.    RETURN ( NIL )
  487.  
  488.  
  489.  
  490. /***
  491. *
  492. *  GetNewPath( <cPath> ) --> cNewPath
  493. *
  494. *
  495. */
  496. STATIC FUNCTION GetNewPath( cPath )
  497.    
  498.    LOCAL cOldColor := SETCOLOR( aFileMan[ FM_COLOR ] )
  499.  
  500.    ClearMessage()
  501.    cPath := PADR( cPath, 45 )
  502.    @ aFileMan[ FM_ROWBOTTOM ] - 1, aFileMan[ FM_COLTOP ] + 2 GET ;
  503.      cPath PICTURE "@!@S45@K"
  504.    READ
  505.  
  506.    cPath := LTRIM(TRIM(cPath))
  507.  
  508.    IF RIGHT( cPath, 1 ) == "\"
  509.       cPath += "*.*"
  510.    ENDIF
  511.    IF RIGHT( cPath, 1 ) == ":"
  512.       cPath += "\*.*"
  513.    ENDIF
  514.  
  515.    aFileMan[ FM_PATH ] := cPath
  516.  
  517.    Message( cPath )
  518.  
  519.    SETCOLOR( cOldColor )
  520.  
  521.    RETURN ( TRIM( cPath ) )
  522.  
  523.  
  524.  
  525. /***
  526. *
  527. *  YesOrNo( <cMessage>, <cDefault> ) --> lYesOrNo
  528. *
  529. *
  530. */
  531. STATIC FUNCTION YesOrNo( cMessage, cDefault )
  532.    
  533.    LOCAL cOldColor := SETCOLOR( aFileMan[ FM_COLOR ] )
  534.    LOCAL lYesOrNo
  535.  
  536.    @ aFileMan[ FM_ROWBOTTOM ] - 1, aFileMan[ FM_COLTOP ] + 2 SAY ;
  537.      TRIM( SUBSTR( cMessage, 1, ;
  538.          (aFileMan[FM_COLBOTTOM] - aFileMan[FM_COLTOP] - 8 )) ) GET ;
  539.          cDefault PICTURE "Y"
  540.    READ
  541.  
  542.    lYesOrNo := (UPPER( cDefault ) == "Y")
  543.    SETCOLOR( cOldColor )
  544.  
  545.    RETURN ( lYesOrNo )
  546.  
  547.  
  548.  
  549. /***
  550. *
  551. *  ClearMessage() --> NIL
  552. *
  553. *
  554. */
  555. STATIC FUNCTION ClearMessage()
  556.    
  557.    LOCAL cOldColor := SETCOLOR( aFileMan[ FM_COLOR ] )
  558.  
  559.    @ aFileMan[ FM_ROWBOTTOM ] - 1, aFileMan[ FM_COLTOP ] + 2 CLEAR TO ;
  560.      aFileMan[ FM_ROWBOTTOM ] - 1, aFileMan[ FM_COLBOTTOM ] - 4
  561.  
  562.    SETCOLOR( cOldColor )
  563.  
  564.    RETURN ( NIL )
  565.  
  566.  
  567.  
  568. /***
  569. *
  570. *  ErrorBeep() --> NIL
  571. *
  572. *
  573. */
  574. STATIC FUNCTION ErrorBeep()
  575.    
  576.    LOCAL nCount := 0
  577.  
  578.    FOR nCount := 1 TO 2
  579.       TONE( 300, 1 )
  580.       TONE( 499, 1 )
  581.    NEXT
  582.  
  583.    RETURN ( NIL )
  584.  
  585.  
  586.  
  587. /***
  588. *
  589. *  CreateScreen() --> NIL
  590. *
  591. *
  592. */
  593. STATIC FUNCTION CreateScreen()
  594.  
  595.    LOCAL cFrameType  := FM_SINGLEFRAME
  596.    LOCAL cBorderType := FM_SINGLEBORDER
  597.    LOCAL nRow        := 0
  598.  
  599.  
  600.    //
  601.    // Draw the primary box
  602.    //
  603.    @ aFileMan[ FM_ROWTOP ], aFileMan[ FM_COLTOP ] CLEAR TO ;
  604.      aFileMan[ FM_ROWBOTTOM ], aFileMan[ FM_COLBOTTOM ]
  605.  
  606.    @ aFileMan[ FM_ROWTOP ], aFileMan[ FM_COLTOP ], ;
  607.      aFileMan[ FM_ROWBOTTOM ], aFileMan[ FM_COLBOTTOM ] BOX cFrameType
  608.  
  609.  
  610.    //
  611.    // Draw the horizontal line under the menus
  612.    //
  613.    @ aFileMan[ FM_ROWTOP ] + 2, aFileMan[ FM_COLTOP ];
  614.      SAY SUBSTR( cBorderType, FM_LEFT, 1 )
  615.  
  616.    @ aFileMan[ FM_ROWTOP ] + 2, aFileMan[ FM_COLBOTTOM ];
  617.      SAY SUBSTR( cBorderType, FM_RIGHT, 1 )
  618.  
  619.    @ aFileMan[ FM_ROWTOP ] + 2, aFileMan[ FM_COLTOP ] + 1;
  620.      SAY REPLICATE( SUBSTR( cFrameType, FM_HORIZONTAL, 1 ),;
  621.          ( aFileMan[ FM_COLBOTTOM ] - aFileMan[ FM_COLTOP ] - 1 )  )
  622.  
  623.  
  624.    //
  625.    // Draw the vertical line next to the scroll bar
  626.    //
  627.    FOR nRow := (aFileMan[ FM_ROWTOP ] + 3) TO (aFileMan[ FM_ROWBOTTOM ] - 1)
  628.       @ nRow, aFileMan[ FM_COLBOTTOM ] - 2 ;
  629.         SAY SUBSTR( cFrameType, FM_VERTICAL, 1 )
  630.    NEXT
  631.  
  632.    @ aFileMan[ FM_ROWTOP ] + 2, aFileMan[ FM_COLBOTTOM ] - 2 SAY ;
  633.      SUBSTR( cBorderType, FM_TOP, 1 )
  634.  
  635.    @ aFileMan[ FM_ROWBOTTOM ], aFileMan[ FM_COLBOTTOM ] - 2 SAY ;
  636.      SUBSTR( cBorderType, FM_BOTTOM, 1 )
  637.  
  638.  
  639.    //
  640.    // Draw the horizontal line under the file display area
  641.    //
  642.    @ aFileMan[ FM_ROWBOTTOM ] - 2, aFileMan[ FM_COLTOP ] ;
  643.      SAY SUBSTR( cBorderType, FM_LEFT, 1 )
  644.  
  645.    @ aFileMan[ FM_ROWBOTTOM ] - 2, aFileMan[ FM_COLBOTTOM ] -2 ;
  646.      SAY SUBSTR( cBorderType, FM_RIGHT, 1 )
  647.  
  648.    @ aFileMan[ FM_ROWBOTTOM ] - 2, aFileMan[ FM_COLTOP ] + 1 ;
  649.      SAY REPLICATE( SUBSTR( cFrameType, FM_HORIZONTAL, 1 ), ;
  650.          ( aFileMan[ FM_COLBOTTOM ] - aFileMan[ FM_COLTOP ] - 3 )  )
  651.  
  652.    // Create the scrolling thumb tab and assign it to our global static
  653.    hScrollBar := TabNew( aFileMan[ FM_ROWTOP ] + 3, ;
  654.                          aFileMan[ FM_COLBOTTOM ] - 1, ;
  655.                          aFileMan[ FM_ROWBOTTOM ] - 1, ;
  656.                          aFileMan[ FM_COLOR ], 1 )
  657.  
  658.    TabDisplay( hScrollBar )
  659.  
  660.    DisplayMenu()
  661.  
  662.    RETURN ( NIL )
  663.  
  664.  
  665.  
  666. /***
  667. *
  668. *  DisplayMenu() --> NIL
  669. *
  670. *
  671. */
  672. STATIC FUNCTION DisplayMenu()
  673.  
  674.    LOCAL cOldColor := SETCOLOR()
  675.    LOCAL nCol      := aFileMan[ FM_COLTOP ] + 2
  676.    LOCAL cItemName
  677.  
  678.    @ aFileMan[ FM_ROWTOP ] + 1, aFileMan[ FM_COLTOP ] + 2 SAY ;
  679.      "Look  Copy  Rename  Delete  Print  Open"
  680.  
  681.    SETCOLOR( "I" )
  682.  
  683.    DO CASE
  684.    CASE nMenuItem == MN_LOOK
  685.       nCol := aFileMan[ FM_COLTOP ] + 2
  686.       cItemName := "Look"
  687.  
  688.    CASE nMenuItem == MN_COPY
  689.       nCol := aFileMan[ FM_COLTOP ] + 8
  690.       cItemName := "Copy"
  691.  
  692.    CASE nMenuItem == MN_RENAME
  693.       nCol := aFileMan[ FM_COLTOP ] + 14
  694.       cItemName := "Rename"
  695.  
  696.    CASE nMenuItem == MN_DELETE
  697.       nCol := aFileMan[ FM_COLTOP ] + 22
  698.       cItemName := "Delete"
  699.  
  700.    CASE nMenuItem == MN_PRINT
  701.       nCol := aFileMan[ FM_COLTOP ] + 30
  702.       cItemName := "Print"
  703.  
  704.    CASE nMenuItem == MN_OPEN
  705.       nCol := aFileMan[ FM_COLTOP ] + 37
  706.       cItemName := "Open"
  707.  
  708.    ENDCASE
  709.  
  710.    @ aFileMan[ FM_ROWTOP ] + 1, nCol SAY cItemName
  711.  
  712.    Message( aFileMan[ FM_PATH ] )
  713.  
  714.    SETCOLOR( cOldColor )
  715.  
  716.    RETURN ( NIL )
  717.  
  718.  
  719.  
  720. /***
  721. *
  722. *    TabNew()
  723. *
  724. */
  725. STATIC FUNCTION TabNew( nTopRow, nTopColumn, nBottomRow, ;
  726.                         cColorString, nInitPosition      )
  727.  
  728.    // Creates a new "thumb tab" or scroll bar for the specified coordinates
  729.    LOCAL aTab := ARRAY( TB_ELEMENTS )
  730.  
  731.    aTab[ TB_ROWTOP ]    := nTopRow
  732.    aTab[ TB_COLTOP ]    := nTopColumn
  733.    aTab[ TB_ROWBOTTOM ] := nBottomRow
  734.    aTab[ TB_COLBOTTOM ] := nTopColumn
  735.  
  736.    // Set the default color to White on Black if none specified
  737.    IF cColorString == NIL
  738.       cColorString := "W/N"
  739.    ENDIF
  740.    aTab[ TB_COLOR ]     := cColorString
  741.  
  742.    // Set the starting position
  743.    IF nInitPosition == NIL
  744.       nInitPosition := 1
  745.    ENDIF
  746.    aTab[ TB_POSITION ]    := nInitPosition
  747.  
  748.    RETURN ( aTab )
  749.  
  750.  
  751.  
  752. /***
  753. *
  754. *    TabDisplay()
  755. *
  756. */
  757. STATIC FUNCTION TabDisplay( aTab )
  758.    
  759.    LOCAL cOldColor
  760.    LOCAL nRow
  761.  
  762.    cOldColor := SETCOLOR( aTab[ TB_COLOR ] )
  763.  
  764.    // Draw the arrows
  765.    @ aTab[ TB_ROWTOP ], aTab[ TB_COLTOP ] SAY TB_UPARROW
  766.    @ aTab[ TB_ROWBOTTOM ], aTab[ TB_COLBOTTOM ] SAY TB_DNARROW
  767.  
  768.    // Draw the background
  769.    FOR nRow := (aTab[ TB_ROWTOP ] + 1) TO (aTab[ TB_ROWBOTTOM ] - 1)
  770.       @ nRow, aTab[ TB_COLTOP ] SAY TB_BACKGROUND
  771.    NEXT
  772.  
  773.    SETCOLOR( cOldColor )
  774.  
  775.    RETURN ( aTab )
  776.  
  777.  
  778.  
  779. /***
  780. *
  781. *    TabUpdate()
  782. *
  783. */
  784. STATIC FUNCTION TabUpdate( aTab, nCurrent, nTotal, lForceUpdate )
  785.    
  786.    LOCAL cOldColor
  787.    LOCAL nNewPosition
  788.    LOCAL nScrollHeight := ( aTab[TB_ROWBOTTOM] - 1 ) - ( aTab[TB_ROWTOP] )
  789.  
  790.    IF nTotal < 1
  791.       nTotal := 1
  792.    ENDIF
  793.  
  794.    IF nCurrent < 1
  795.       nCurrent := 1
  796.    ENDIF
  797.  
  798.    IF nCurrent > nTotal
  799.       nCurrent := nTotal
  800.    ENDIF
  801.  
  802.    IF lForceUpdate == NIL
  803.       lForceUpdate := .F.
  804.    ENDIF
  805.  
  806.    cOldColor := SETCOLOR( aTab[ TB_COLOR ] )
  807.  
  808.    // Determine the new position
  809.    nNewPosition := ROUND( (nCurrent / nTotal) * nScrollHeight, 0 )
  810.  
  811.    // Resolve algorythm oversights
  812.    nNewPosition := IF( nNewPosition < 1, 1, nNewPosition )
  813.    nNewPosition := IF( nCurrent == 1, 1, nNewPosition )
  814.    nNewPosition := IF( nCurrent >= nTotal, nScrollHeight, nNewPosition )
  815.  
  816.    // Overwrite the old position (if different), then draw in the new one
  817.    IF nNewPosition <> aTab[ TB_POSITION ] .OR. lForceUpdate
  818.       
  819.       @ (aTab[ TB_POSITION ] + aTab[ TB_ROWTOP ]), aTab[ TB_COLTOP ] SAY ;
  820.         TB_BACKGROUND
  821.  
  822.       @ (nNewPosition + aTab[ TB_ROWTOP ]), aTab[ TB_COLTOP ] SAY;
  823.         TB_HIGHLIGHT
  824.  
  825.       aTab[ TB_POSITION ] := nNewPosition
  826.  
  827.    ENDIF
  828.  
  829.    SETCOLOR( cOldColor )
  830.  
  831.    RETURN ( aTab )
  832.  
  833.  
  834.  
  835. /***
  836. *
  837. *  UpPath( <cPath> ) --> cPath
  838. *
  839. *
  840. */
  841. STATIC FUNCTION UpPath( cPath )
  842.    
  843.    LOCAL cFileSpec
  844.  
  845.    cFileSpec := RIGHT( cPath, LEN( cPath ) - RAT( "\", cPath ) )
  846.    cPath     := LEFT( cPath, RAT( "\", cPath ) - 1 )
  847.    cPath     := LEFT( cPath, RAT( "\", cPath ) )
  848.    cPath     += cFileSpec
  849.  
  850.    RETURN ( cPath )
  851.  
  852.  
  853.  
  854. /***
  855. *
  856. *  GetFileExtension( <cFile> ) --> cFileExtension
  857. *
  858. *
  859. */
  860. STATIC FUNCTION GetFileExtension( cFile )
  861.    RETURN( UPPER( SUBSTR( cFile, AT( ".", cFile ) + 1, 3 ) ) )
  862.  
  863.  
  864.  
  865. /***
  866. *
  867. *  LookAtFile() --> NIL
  868. *
  869. *
  870. */
  871. STATIC FUNCTION LookAtFile()
  872.    
  873.    LOCAL cExtension := ""
  874.    LOCAL cOldScreen := SAVESCREEN( 0, 0, MAXROW(), MAXCOL() )
  875.  
  876.    IF AT( "D", SUBSTR( aFileList[ nFileItem ], 43, 6 ) ) <> 0
  877.       
  878.       // Looks like a directory, let's load it...
  879.       DO CASE
  880.       CASE SUBSTR( aFileList[ nFileItem ], 1, 3 ) == ".  "
  881.          // That's the current directory!
  882.          GetNewPath( aFileMan[ FM_PATH ] )
  883.       CASE SUBSTR( aFileList[ nFileItem ], 1, 3 ) == ".. "
  884.          GetNewPath( UpPath( aFileMan[ FM_PATH ]))
  885.  
  886.       OTHERWISE
  887.          GetNewPath( SUBSTR( aFileMan[ FM_PATH ], 1, ;
  888.             RAT( "\", aFileMan[ FM_PATH ])) + ;
  889.             TRIM(SUBSTR(aFileList[nFileItem],1,12)) + "\*.*")
  890.       ENDCASE
  891.  
  892.       lReloadDir := .T.
  893.  
  894.    ELSE
  895.       
  896.       // Must be a file.  Let's load the proper viewer and take a look
  897.       cExtension := GetFileExtension( SUBSTR( aFileList[nFileItem], 1, 12 ))
  898.  
  899.       DO CASE
  900.       CASE cExtension == "DBF"
  901.          DBFViewer( aFileMan[ FM_RETURNFILE ] )
  902.  
  903.       OTHERWISE
  904.          GenericViewer( aFileMan[ FM_RETURNFILE ] )
  905.  
  906.       ENDCASE
  907.  
  908.       // Restore the screen
  909.       RESTSCREEN( 0, 0, MAXROW(), MAXCOL(), cOldScreen )
  910.  
  911.    ENDIF
  912.  
  913.    RETURN ( NIL )
  914.  
  915.  
  916.  
  917. /***
  918. *
  919. *  CopyFile() --> NIL
  920. *
  921. *
  922. */
  923. STATIC FUNCTION CopyFile()
  924.    
  925.    LOCAL cNewName    := ""
  926.    LOCAL cOldName    := ""
  927.    LOCAL lKeepGoing  := .F.
  928.    LOCAL cNewFile    := ""
  929.    LOCAL nCurrent    := 0
  930.    LOCAL cCurrentFil := ""
  931.    LOCAL nCount      := 0
  932.    LOCAL cOldScreen  := SAVESCREEN( aFileMan[ FM_ROWTOP ] + 3, ;
  933.                                     aFileMan[ FM_COLTOP ] + 2, ;
  934.                                     aFileMan[ FM_ROWTOP ] + 6, ;
  935.                                     aFileMan[ FM_COLTOP ] + 51 )
  936.    
  937.    IF AT( "<dir>", aFileList[ nFileItem ] ) = 0
  938.  
  939.       TONE( 800, 1 )
  940.  
  941.       IF nTagged > 0
  942.          
  943.          IF YesOrNo( "Copy marked files? (Y/N)", "N" )
  944.             lKeepGoing := .T.
  945.          ENDIF
  946.  
  947.       ELSE
  948.          
  949.          @ aFileMan[ FM_ROWTOP ] + 3 + nRel, aFileMan[ FM_COLTOP ] + 1 SAY;
  950.            CHR( 16 )
  951.          @ aFileMan[ FM_ROWTOP ] + 3 + nRel, aFileMan[ FM_COLBOTTOM ] - 3 SAY;
  952.            CHR( 17 )
  953.          IF YesOrNo( "Copy this file? (Y/N)", "N" )
  954.             lKeepGoing := .T.
  955.          ENDIF
  956.  
  957.       ENDIF
  958.  
  959.       ClearMessage()
  960.  
  961.       // Draw the box
  962.       @ aFileMan[ FM_ROWTOP ] + 3, aFileMan[ FM_COLTOP ] + 2, ;
  963.         aFileMan[ FM_ROWTOP ] + 6, aFileMan[ FM_COLTOP ] + 51 BOX;
  964.         FM_DOUBLEFRAME
  965.  
  966.       @ aFileMan[ FM_ROWTOP ] + 4, aFileMan[ FM_COLTOP ] + 3 CLEAR TO ;
  967.         aFileMan[ FM_ROWTOP ] + 5, aFileMan[ FM_COLTOP ] + 50
  968.  
  969.       cNewName := cOldName := PADR( SUBSTR( aFileMan[ FM_PATH ], 1, ;
  970.                               RAT( "\", aFileMan[ FM_PATH ] ) ) + ;
  971.                               TRIM( SUBSTR( aFileList[ nFileItem ], 1, 12 ) ),;
  972.                               45 )
  973.  
  974.       IF lKeepGoing
  975.  
  976.          IF nTagged > 0
  977.  
  978.             cNewName := PADR( SUBSTR( aFileMan[ FM_PATH ], 1, RAT( "\", ;
  979.                                 aFileMan[ FM_PATH ] ) ), 45 )
  980.  
  981.             @ aFileMan[ FM_ROWTOP ]+4, aFileMan[ FM_COLTOP ]+4 SAY;
  982.               "Copy marked files to..."
  983.  
  984.             @ aFileMan[ FM_ROWTOP ]+5, aFileMan[ FM_COLTOP ]+4 GET;
  985.               cNewName PICTURE "@!@S46@K"
  986.  
  987.             READ
  988.  
  989.             IF LASTKEY() <> K_ESC
  990.                
  991.                cNewName := TRIM( cNewName )
  992.                IF RIGHT( cNewName, 1 ) <> "\"
  993.                   cNewName += "\"
  994.                ENDIF
  995.  
  996.                FOR nCurrent := 1 TO LEN( aFileList )
  997.                   
  998.                   IF SUBSTR( aFileList[ nCurrent ], 14, 1 ) == FM_CHECK
  999.                      cCurrentFile := SUBSTR( aFileMan[ FM_PATH ], 1, ;
  1000.                                      RAT( "\", aFileMan[ FM_PATH ])) + ;
  1001.                                      TRIM( SUBSTR( aFileList[ nCurrent ], 1, 12))
  1002.  
  1003.                      cNewFile := cNewName + ;
  1004.                                  TRIM( SUBSTR( aFileList[ nCurrent ], 1, 12))
  1005.  
  1006.                      Message( "Copying " + TRIM( cCurrentFile ) )
  1007.                      COPY FILE ( cCurrentFile ) TO ( cNewFile )
  1008.                      aFileList[ nCurrent ] := STUFF( aFileList[ nCurrent ], ;
  1009.                                               14, 1, " " )
  1010.                      nTagged--
  1011.                      nCount++
  1012.                      IF INKEY() = K_ESC
  1013.                         EXIT
  1014.                      ENDIF
  1015.  
  1016.                   ENDIF
  1017.  
  1018.                NEXT
  1019.  
  1020.                @ aFileMan[ FM_ROWTOP ] + 4, aFileMan[ FM_COLTOP ] + 3 CLEAR TO ;
  1021.                  aFileMan[ FM_ROWTOP ] + 5, aFileMan[ FM_COLTOP ] + 50
  1022.  
  1023.                @ aFileMan[ FM_ROWTOP ]+4, aFileMan[ FM_COLTOP ]+4 SAY;
  1024.                  LTRIM(STR( nCount )) + IF( nCount > 1, " files copied.  ", ;
  1025.                                         " file copied.  " ) + "Press any key..."
  1026.                INKEY(0)
  1027.             ENDIF
  1028.  
  1029.          ELSE
  1030.             
  1031.             @ aFileMan[ FM_ROWTOP ]+4, aFileMan[ FM_COLTOP ]+4 SAY;
  1032.               "Copy current file to..."
  1033.  
  1034.             @ aFileMan[ FM_ROWTOP ]+5, aFileMan[ FM_COLTOP ]+4 GET;
  1035.               cNewName PICTURE "@!@S46@K"
  1036.  
  1037.             READ
  1038.  
  1039.             IF LASTKEY() <> K_ESC
  1040.                
  1041.                IF RIGHT( cNewName, 1 ) == "\"
  1042.                   cNewName += TRIM( SUBSTR( cOldName, RAT( "\", cOldName) ;
  1043.                               + 1, 12 ))
  1044.                ENDIF
  1045.                COPY FILE ( cOldName ) TO ( cNewName )
  1046.                @ aFileMan[ FM_ROWTOP ] + 4, aFileMan[ FM_COLTOP ] + 3 CLEAR TO ;
  1047.                  aFileMan[ FM_ROWTOP ] + 5, aFileMan[ FM_COLTOP ] + 50
  1048.  
  1049.                @ aFileMan[ FM_ROWTOP ]+4, aFileMan[ FM_COLTOP ]+4 SAY;
  1050.                  "1 file copied.  Press any key..."
  1051.                INKEY(0)
  1052.  
  1053.             ENDIF
  1054.  
  1055.          ENDIF
  1056.  
  1057.          lReloadDir := .T.
  1058.  
  1059.       ENDIF
  1060.    ENDIF
  1061.  
  1062.    RESTSCREEN( aFileMan[ FM_ROWTOP ] + 3, ;
  1063.                aFileMan[ FM_COLTOP ] + 2, ;
  1064.                aFileMan[ FM_ROWTOP ] + 6, ;
  1065.                aFileMan[ FM_COLTOP ] + 51,;
  1066.                cOldScreen )
  1067.  
  1068.    @ aFileMan[ FM_ROWTOP ] + 3 + nRel, aFileMan[ FM_COLTOP ] + 1 SAY;
  1069.      CHR( 32 )
  1070.  
  1071.    @ aFileMan[ FM_ROWTOP ] + 3 + nRel, aFileMan[ FM_COLBOTTOM ] - 3 SAY;
  1072.      CHR( 32 )
  1073.  
  1074.    RETURN ( NIL )
  1075.  
  1076.  
  1077.  
  1078. /***
  1079. *
  1080. *  RenameFile() --> NIL
  1081. *
  1082. *
  1083. */
  1084. STATIC FUNCTION RenameFile()
  1085.    
  1086.    LOCAL cNewName   := ""
  1087.    LOCAL cOldName   := ""
  1088.    LOCAL cOldScreen := SAVESCREEN( aFileMan[ FM_ROWTOP ] + 3,;
  1089.                                    aFileMan[ FM_COLTOP ] + 2,;
  1090.                                    aFileMan[ FM_ROWTOP ] + 6,;
  1091.                                    aFileMan[ FM_COLTOP ] + 51 )
  1092.  
  1093.    IF AT( "<dir>", aFileList[ nFileItem ] ) = 0
  1094.  
  1095.       // Draw the box
  1096.       @ aFileMan[ FM_ROWTOP ] + 3, aFileMan[ FM_COLTOP ] + 2, ;
  1097.         aFileMan[ FM_ROWTOP ] + 6, aFileMan[ FM_COLTOP ] + 51 BOX;
  1098.         FM_DOUBLEFRAME
  1099.  
  1100.       @ aFileMan[ FM_ROWTOP ] + 4, aFileMan[ FM_COLTOP ] + 3 CLEAR TO ;
  1101.         aFileMan[ FM_ROWTOP ] + 5, aFileMan[ FM_COLTOP ] + 50
  1102.  
  1103.       cNewName := cOldName := PADR( SUBSTR( aFileMan[ FM_PATH ], 1, ;
  1104.                               RAT( "\", aFileMan[ FM_PATH ] ) ) + ;
  1105.                               TRIM( SUBSTR( aFileList[ nFileItem ], 1, 12 ) ),;
  1106.                               45 )
  1107.  
  1108.       TONE( 800, 1 )
  1109.  
  1110.       @ aFileMan[ FM_ROWTOP ] + 4, aFileMan[ FM_COLTOP ] + 4 SAY "Rename " +;
  1111.         SUBSTR( cNewName, 1, 38 )
  1112.  
  1113.       @ aFileMan[ FM_ROWTOP ] + 5, aFileMan[ FM_COLTOP ] + 4 SAY "To" GET;
  1114.         cNewName PICTURE "@!@S43@K"
  1115.  
  1116.       READ
  1117.  
  1118.       IF LASTKEY() <> K_ESC
  1119.          
  1120.          IF FILE( cNewName )
  1121.             ErrorBeep()
  1122.             @ aFileMan[ FM_ROWTOP ] + 4, aFileMan[ FM_COLTOP ] + 3 CLEAR TO ;
  1123.               aFileMan[ FM_ROWTOP ] + 5, aFileMan[ FM_COLTOP ] + 50
  1124.             @ aFileMan[ FM_ROWTOP ] + 4, aFileMan[ FM_COLTOP ] + 4 SAY ;
  1125.               "ERROR: That file already exists!"
  1126.             @ aFileMan[ FM_ROWTOP ] + 5, aFileMan[ FM_COLTOP ] + 4 SAY ;
  1127.                "Press any key..."
  1128.             INKEY( 0 )
  1129.          ELSE
  1130.             lReloadDir := .T.
  1131.             RENAME ( TRIM( cOldName ) ) TO ( TRIM( cNewName ) )
  1132.          ENDIF
  1133.  
  1134.       ENDIF
  1135.  
  1136.    ENDIF
  1137.  
  1138.    RESTSCREEN( aFileMan[ FM_ROWTOP ] + 3, ;
  1139.                aFileMan[ FM_COLTOP ] + 2, ;
  1140.                aFileMan[ FM_ROWTOP ] + 6, ;
  1141.                aFileMan[ FM_COLTOP ] + 51,;
  1142.                cOldScreen )
  1143.  
  1144.    RETURN ( NIL )
  1145.  
  1146.  
  1147.  
  1148. /***
  1149. *
  1150. *  DeleteFile() --> NIL
  1151. *
  1152. *
  1153. */
  1154. STATIC FUNCTION DeleteFile()
  1155.  
  1156.    LOCAL nCurrentFile := 0
  1157.    LOCAL cFile        := ""
  1158.  
  1159.    TONE( 800, 1 )
  1160.    IF nTagged > 0
  1161.       
  1162.       IF YesOrNo( "Delete marked files? (Y/N)", "N" )
  1163.          lReloadDir := .T.
  1164.  
  1165.          FOR nCurrentFile := 1 TO LEN( aFileList )
  1166.             
  1167.             cFile := SUBSTR( aFileMan[ FM_PATH ], 1, ;
  1168.                      RAT( "\", aFileMan[ FM_PATH ] ) ) + ;
  1169.                      TRIM( SUBSTR( aFileList[ nCurrentFile ], 1, 12 ) )
  1170.  
  1171.             IF SUBSTR( aFileList[ nCurrentFile ], 14, 1 ) == FM_CHECK
  1172.                ERASE ( cFile )
  1173.                Message( "Deleting " + TRIM( cFile ) )
  1174.             ENDIF
  1175.  
  1176.          NEXT
  1177.  
  1178.          Message( LTRIM( STR( nTagged ) ) + " file(s) deleted.  Press any key..." )
  1179.          INKEY( 300 )
  1180.          nTagged := 0
  1181.  
  1182.       ENDIF
  1183.  
  1184.    ELSE
  1185.       
  1186.       IF AT( "<dir>", aFileList[ nFileItem ] ) = 0
  1187.          cFile := SUBSTR( aFileMan[ FM_PATH ], 1, ;
  1188.                   RAT( "\", aFileMan[ FM_PATH ] ) ) + ;
  1189.                   TRIM( SUBSTR( aFileList[ nFileItem ], 1, 12 ) )
  1190.          @ aFileMan[ FM_ROWTOP ] + 3 + nRel, aFileMan[ FM_COLTOP ] + 1 SAY;
  1191.            CHR( 16 )
  1192.          @ aFileMan[ FM_ROWTOP ] + 3 + nRel, aFileMan[ FM_COLBOTTOM ] - 3 SAY;
  1193.            CHR( 17 )
  1194.          IF YesOrNo( "Delete this file? (Y/N)", "N" )
  1195.             ERASE ( cFile )
  1196.             lReloadDir := .T.
  1197.          ENDIF
  1198.  
  1199.       ENDIF
  1200.  
  1201.    ENDIF
  1202.  
  1203.    @ aFileMan[ FM_ROWTOP ] + 3 + nRel, aFileMan[ FM_COLTOP ] + 1 SAY;
  1204.      CHR( 32 )
  1205.  
  1206.    @ aFileMan[ FM_ROWTOP ] + 3 + nRel, aFileMan[ FM_COLBOTTOM ] - 3 SAY;
  1207.      CHR( 32 )
  1208.  
  1209.    Message( aFileMan[ FM_PATH ] )
  1210.  
  1211.    RETURN ( NIL )
  1212.  
  1213.  
  1214.  
  1215. /***
  1216. *
  1217. *  PrintFile() --> NIL
  1218. *
  1219. *
  1220. */
  1221. STATIC FUNCTION PrintFile()
  1222.    
  1223.    LOCAL cFile := SUBSTR( aFileMan[ FM_PATH ], 1,               ;
  1224.                   RAT( "\", aFileMan[ FM_PATH ] ) ) +           ;
  1225.                   TRIM( SUBSTR( aFileList[ nFileItem ], 1, 12 ) )
  1226.  
  1227.    TONE( 800, 1 )
  1228.  
  1229.    @ aFileMan[ FM_ROWTOP ] + 3 + nRel, aFileMan[ FM_COLTOP ] + 1 SAY ;
  1230.      CHR( 16 )
  1231.  
  1232.    @ aFileMan[ FM_ROWTOP ] + 3 + nRel, aFileMan[ FM_COLBOTTOM ] - 3 SAY ;
  1233.      CHR( 17 )
  1234.  
  1235.    IF YesOrNo( "Print this file?", "N" )
  1236.  
  1237.       IF ISPRINTER()
  1238.          Message( "Printing " + TRIM( cFile ) )
  1239.          COPY FILE ( cFile ) TO PRN
  1240.          EJECT
  1241.       ELSE
  1242.          ErrorBeep()
  1243.          Message( "ERROR: Printer not responding!" )
  1244.          INKEY( 20 )
  1245.       ENDIF
  1246.  
  1247.    ENDIF
  1248.  
  1249.    ClearMessage()
  1250.  
  1251.    @ aFileMan[ FM_ROWTOP ] + 3 + nRel, aFileMan[ FM_COLTOP ] + 1 SAY ;
  1252.      CHR( 32 )
  1253.  
  1254.    @ aFileMan[ FM_ROWTOP ] + 3 + nRel, aFileMan[ FM_COLBOTTOM ] - 3 SAY ;
  1255.      CHR( 32 )
  1256.  
  1257.    Message( aFileMan[ FM_PATH ] )
  1258.  
  1259.    RETURN ( NIL )
  1260.  
  1261.  
  1262.  
  1263. /***
  1264. *
  1265. *  DBFViewer( <cDatabase> ) --> cDatabase
  1266. *  View the contents of a database file in a window
  1267. *
  1268. */
  1269. STATIC FUNCTION DBFViewer( cDatabase )
  1270.    
  1271.    LOCAL cRecords := ""
  1272.  
  1273.    USE ( cDatabase ) ALIAS LookFile SHARED NEW READONLY
  1274.  
  1275.    IF !NETERR()
  1276.  
  1277.       @ 0, 0, MAXROW(), MAXCOL() BOX FM_DOUBLEFRAME
  1278.  
  1279.       cRecords := "Number of records: " + LTRIM( STR( RECCOUNT() ) )
  1280.  
  1281.       @ 0, MAXCOL() - 2 SAY "]"
  1282.       @ 0, (MAXCOL()-2)-LEN( cRecords )-3 SAY "[" + SPACE( LEN( cRecords ) + 2 )
  1283.       @ 0, (MAXCOL()-2)-LEN( cRecords )-1 SAY cRecords
  1284.       @ 0, 1 SAY "[ " + TRIM(cDatabase) + " ]"
  1285.       @ MAXROW(),  INT((MAXCOL()-48)/2) SAY ;
  1286.       "[ Use " + CHR(27) + CHR(18) + CHR(26)+" to move through data.  (Esc to Exit) ]"
  1287.  
  1288.       DBEDIT( 1, 1, MAXROW()-1, MAXCOL()-1 )
  1289.  
  1290.       // Close the file and select the old work area
  1291.       USE
  1292.       SELECT ( aFileMan[ FM_OLDSELECT ] )
  1293.  
  1294.    ENDIF
  1295.  
  1296.    RETURN ( cDatabase )
  1297.  
  1298.  
  1299.  
  1300. /***
  1301. *
  1302. *  GenericViewer( <cFile> ) --> cFile
  1303. *
  1304. *  View the contents of a text file (?)
  1305. *
  1306. */
  1307. STATIC FUNCTION GenericViewer( cFile )
  1308.  
  1309.    LOCAL cBuffer := ""
  1310.    LOCAL nHandle := 0
  1311.    LOCAL nBytes  := 0
  1312.  
  1313.    cBuffer := SPACE( GV_BLOCKSIZE )
  1314.    nHandle := FOPEN( cFile )
  1315.  
  1316.    IF FERROR() != 0
  1317.       cBuffer := "Error reading file!"
  1318.    ELSE
  1319.       nBytes = FREAD( nHandle, @cBuffer, GV_BLOCKSIZE )
  1320.    ENDIF
  1321.    FCLOSE( nHandle )
  1322.  
  1323.    cBuffer := RTRIM( cBuffer )
  1324.  
  1325.    @ 0, 0 CLEAR TO MAXROW(), MAXCOL()
  1326.    @ 0, 0, MAXROW(), MAXCOL() BOX FM_DOUBLEFRAME
  1327.    @ 0, 1 SAY "[ " + TRIM(cFile) + " ]"
  1328.    @ MAXROW(),  INT((MAXCOL()-48)/2) SAY ;
  1329.    "[ Use "+CHR(27)+CHR(18)+CHR(26)+" to move through data.  (Esc to Exit) ]"
  1330.    MEMOEDIT( cBuffer, 1, 2, MAXROW() - 1, MAXCOL() - 1, .F., "MemoUDF" , 300 )
  1331.  
  1332.    RETURN ( cFile )
  1333.  
  1334.  
  1335.  
  1336. /***
  1337. *
  1338. *  MemoUDF( <nMode>, <nLine>, <nColumn> ) --> nMemoEditDefaultAction
  1339. *  
  1340. *
  1341. */
  1342. FUNCTION MemoUDF( nMode, nLine, nColumn )
  1343.    RETURN( ME_DEFAULT )
  1344.  
  1345.  
  1346.  
  1347. /***
  1348. *
  1349. *  TagAllFiles() --> NIL
  1350. *
  1351. *  Tag all files in the current directory
  1352. *
  1353. */
  1354. STATIC FUNCTION TagAllFiles()
  1355.    
  1356.    LOCAL nCurrent
  1357.  
  1358.    nTagged := 0
  1359.    
  1360.    FOR nCurrent := 1 TO LEN( aFileList )
  1361.       IF AT( "D", SUBSTR( aFileList[ nCurrent ], 43, 6 ) ) == 0
  1362.          aFileList[ nCurrent ] := STUFF( aFileList[ nCurrent ], ;
  1363.                                          14, 1, FM_CHECK )
  1364.          nTagged++
  1365.       ENDIF
  1366.    NEXT
  1367.    
  1368.    RETURN ( NIL )
  1369.  
  1370.  
  1371.  
  1372. /***
  1373. *
  1374. *  UnTagAllFiles() --> NIL
  1375. *
  1376. *  Untag all tagged files in the current directory
  1377. *
  1378. */
  1379. STATIC FUNCTION UnTagAllFiles()
  1380.  
  1381.    LOCAL nCurrent
  1382.  
  1383.    nTagged := 0
  1384.  
  1385.    FOR nCurrent := 1 TO LEN( aFileList )
  1386.       aFileList[ nCurrent ] := STUFF( aFileList[ nCurrent ], 14, 1, " " )
  1387.    NEXT
  1388.  
  1389.    RETURN ( NIL )
  1390.