/****** DocumentViewer.c [1.1] ***************************************
*
* NAME
*    DocumentViewer.c
*
* DESCRIPTION
*    Show the User a directory of documents to view. 
*
* NOTES
*    No Tabs were used in the creation of this file (Tabs SHOULD BE
*    an endangered species!).
*
*    The following Default ToolTypes are used by this program:
*
*    ParentDir     = "RAM:"
*    FileViewer    = "MultiView"
*    FileExtFilter = "(#?.doc|#?.guide)"
*
*    GUI Designed by : Jim Steichen
*********************************************************************
*
*/

#include <string.h>
#include <stdlib.h>               // for abs() macro.

#include <exec/types.h>
#include <AmigaDOSErrs.h>         // Every error number in 1 file.

#include <intuition/intuition.h>
#include <intuition/classes.h>
#include <intuition/classusr.h>
#include <intuition/imageclass.h>
#include <intuition/gadgetclass.h>

#include <libraries/gadtools.h>
#include <libraries/asl.h>

#include <dos/dostags.h>

#include <utility/tagitem.h>

#include <graphics/displayinfo.h>
#include <graphics/gfxbase.h>

#include <workbench/workbench.h>
#include <workbench/startup.h>
#include <workbench/icon.h>

#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/graphics_protos.h>
#include <clib/utility_protos.h>
#include <clib/diskfont_protos.h>

#include "CPGM:GlobalObjects/CommonFuncs.h"

#define StrBfPtr( g ) (((struct StringInfo *)g->SpecialInfo)->Buffer)

#define DocListView 0
#define ASLBt       1
#define FileNameStr 2
#define ChangeDirBt 3
#define ViewFileBt  4

#define DV_CNT      5

#define FileNameGad DVGadgets[ FileNameStr ]
#define FILENAME    StrBfPtr( FileNameGad )

#define STRLENGTH   256

IMPORT Class *initGet( void );

IMPORT struct WBStartup *_WBenchMsg;

struct IntuitionBase *IntuitionBase;
struct GfxBase       *GfxBase;

struct Library       *GadToolsBase = NULL;
struct Library       *IconBase     = NULL;
struct Library       *UtilityBase  = NULL;

PRIVATE char v[] = "\0$VER: DocumentViewer 1.1 (08-Feb-2000) by J.T. Steichen";

PRIVATE char DefaultButtons[] = "CONTINUE|ABORT!";
PRIVATE char AarrgghButton[]  = "Aaarrgghh!!!";

PRIVATE struct DiskObject   *diskobj  = NULL;

PRIVATE struct IClass       *getClass = NULL;
PRIVATE struct _Object      *getImage = NULL;

PRIVATE struct Screen       *Scr      = NULL;
PRIVATE struct Window       *DVWnd    = NULL;
PRIVATE struct Gadget       *DVGList  = NULL;
PRIVATE struct Gadget       *DVGadgets[ DV_CNT ];
PRIVATE struct IntuiMessage  DVMsg;

PRIVATE struct TextFont *DVFont = NULL;
PRIVATE struct TextAttr *Font   = NULL;

PRIVATE struct TextAttr  Attr   = { 0, };
PRIVATE struct CompFont  CFont  = { 0, };

PRIVATE UBYTE *PubScreenName = "Workbench";
PRIVATE APTR   VisualInfo    = NULL;

PRIVATE UWORD  DVLeft   = 40;
PRIVATE UWORD  DVTop    = 16;
PRIVATE UWORD  DVWidth  = 505;
PRIVATE UWORD  DVHeight = 238;
PRIVATE UBYTE *DVWdt    = "Document Viewer ©1999:";
PRIVATE UBYTE *ScrTitle = "Document Viewer ©1999 by J.T. Steichen";

PRIVATE char   fn[256]     = "";
PRIVATE char   em[256]     = "";
PRIVATE char   cp[256]     = "";
PRIVATE char  *ErrMsg      = &em[0];
PRIVATE char  *CurrentPath = &cp[0];
PRIVATE char  *SpareFName  = &fn[0];

PRIVATE char   ParsedPattern[128] = ""; // For FileExtFilter ToolType use.

// --------------------------------------------------------------------

#define TXTLENGTH   80 // Reasonable length for the document names. 

PRIVATE struct MinList DocLVList    = { 0, };

PRIVATE struct Node    DocLVNode    = { 0, };
PRIVATE struct Node   *DocLVNodes   = NULL;

PRIVATE ULONG          NumDocuments = 0L;
PRIVATE ULONG          NumAllocated = 0L; // TXTLENGTH * NumDocuments.

PRIVATE UBYTE         *NodeStrs     = NULL;


PRIVATE struct FileInfoBlock *The_fib; // used in several functions.

// ------------- ToolType variables: ----------------------------------

PRIVATE char ParentDir[]           = "PARENTDIR";
PRIVATE char FileExtFilter[]       = "FILEEXTFILTER";
PRIVATE char FileViewer[]          = "FILEVIEWER";

PRIVATE char DefParentDir[128]     = "RAM:";
PRIVATE char DefFileExtFilter[128] = "(#?.guide|#?.doc)";
PRIVATE char DefFileViewer[128]    = "MultiView";

PRIVATE char *TTParentDir          = &DefParentDir[0];
PRIVATE char *TTFileExtFilter      = &DefFileExtFilter[0];
PRIVATE char *TTFileViewer         = &DefFileViewer[0];

// --------------------------------------------------------------------

PRIVATE UWORD DVGTypes[] = {

   LISTVIEW_KIND, GENERIC_KIND, STRING_KIND,
   BUTTON_KIND,   BUTTON_KIND
};

PRIVATE int DocListViewClicked( int whichitem );
PRIVATE int ASLBtClicked(       int dummy     );
PRIVATE int FileNameStrClicked( int dummy     );
PRIVATE int ChangeDirBtClicked( int dummy     );
PRIVATE int ViewFileBtClicked(  int dummy     );

PRIVATE struct NewGadget DVNGad[] = {

     5,  18, 399, 195,  "Documents:", NULL, DocListView, 
   PLACETEXT_ABOVE, NULL, (APTR) DocListViewClicked,

   452, 217,  20,  17,          NULL, NULL, ASLBt, 
   0, NULL, (APTR) ASLBtClicked,

    95, 216, 351,  17,  "File Name:", NULL, FileNameStr, 
   PLACETEXT_LEFT, NULL, (APTR) FileNameStrClicked,

   411,  21,  85,  17, "_Change Dir", NULL, ChangeDirBt, 
   PLACETEXT_IN, NULL, (APTR) ChangeDirBtClicked,
 
   411,  45,  85,  17,  "_View File", NULL, ViewFileBt, 
   PLACETEXT_IN, NULL, (APTR) ViewFileBtClicked
};

PRIVATE ULONG DVGTags[] = {

   GTLV_ShowSelected, NULL, LAYOUTA_Spacing, 2, TAG_DONE,

   TAG_DONE,

   GTST_MaxChars, 256, 
   STRINGA_Justification, GACT_STRINGCENTER, TAG_DONE,

   GT_Underscore, '_', TAG_DONE,
   GT_Underscore, '_', TAG_DONE
};

PRIVATE struct TagItem FileTags[] = {

   ASLFR_Window,          (ULONG) NULL,
   ASLFR_TitleText,       (ULONG) "Select a file...",
   ASLFR_InitialHeight,   300,
   ASLFR_InitialWidth,    400,
   ASLFR_InitialTopEdge,  16,
   ASLFR_InitialLeftEdge, 100,
   ASLFR_PositiveText,    (ULONG) " OKAY! ",
   ASLFR_NegativeText,    (ULONG) " CANCEL! ",
   ASLFR_InitialPattern,  (ULONG) "#?.doc",
   ASLFR_InitialFile,     (ULONG) "",
   ASLFR_InitialDrawer,   (ULONG) "RAM:",
   ASLFR_Flags1,          FRF_DOPATTERNS,
   ASLFR_Flags2,          FRF_REJECTICONS,
   ASLFR_SleepWindow,     1,
   ASLFR_PrivateIDCMP,    1,
   TAG_END 
};

// ----------------------------------------------------------------------

PRIVATE void CloseDownScreen( void )
{
   if (getImage != NULL) 
      {
      DisposeObject( getImage );
      getImage = NULL;
      }

   if (getClass != NULL) 
      {
      FreeClass( getClass );
      getClass = NULL;
      }

   if (VisualInfo != NULL) 
      {
      FreeVisualInfo( VisualInfo );
      VisualInfo = NULL;
      }

   if (Scr != NULL) 
      {
      UnlockPubScreen( NULL, Scr );
      Scr = NULL;
      }

   return;
}

PRIVATE int SetupScreen( void )
{
   Font = &Attr;

   if ((Scr = LockPubScreen( PubScreenName )) == NULL)
      return( -1 );

   ComputeFont( Scr, Font, &CFont, 0, 0 );

   if ((VisualInfo = GetVisualInfo( Scr, TAG_DONE )) == NULL)
      return( -2 );

   if ((getClass = initGet()) == NULL)
      return( -3 );

   if ((getImage = NewObject( getClass, NULL, 
                              GT_VisualInfo, VisualInfo, 
                              TAG_DONE )) == NULL)
      return( -4 );

   return( 0 );
}

// --------------------------------------------------------------

PRIVATE BOOL FileFilter( char *pattern, struct FileInfoBlock *fib )
{
   BOOL rval = MatchPatternNoCase( pattern, fib->fib_FileName );

   return( rval );
}

PRIVATE int CountFiles( BPTR root )
{
   register BPTR mylock = NULL;
   int           rval   = 0;
   
   if (ParsePatternNoCase( TTFileExtFilter, ParsedPattern, 128 ) < 0)
      {
      // Error in parsing pattern!
      sprintf( ErrMsg, "%s Cannot be parsed by the system!", 
               TTFileExtFilter 
             );

      SetReqButtons( &AarrgghButton[0] );       

      (void) Handle_Problem( ErrMsg, 
                             "FileExtFilter ToolType broke:", NULL 
                           );

      SetReqButtons( &DefaultButtons[0] );       

      *ParsedPattern = '\0'; // Kill the match pattern.

      return( -1 );
      }

   mylock = DupLock( root );

   if ((Examine( mylock, The_fib ) == FALSE)  // Can't examine
         || (The_fib->fib_DirEntryType < 0))  // Found a file.
      {   
      // Not a directory, the User is a total SmegHead!
      UnLock( mylock );
      return( 0 );
      }

   // If we've made it this far, count the number of valid filenames:

   while ((ExNext( mylock, The_fib ) != 0)
           || (IoErr() != ERROR_NO_MORE_ENTRIES))
      {
      if (The_fib->fib_DirEntryType < 0)
         {
         // Got a filename:

         if (FileFilter( ParsedPattern, The_fib ) == TRUE)
            rval++; // File matched the pattern!
         }
      }             

   UnLock( mylock );

   return( rval );
}

PRIVATE int CountNumFiles( char *dirname )
{
   BPTR start = NULL;
   int  rval  = 0;

   if ((start = Lock( dirname, ACCESS_READ )) == NULL)
      {
      sprintf( ErrMsg, "Can't lock %s\n", dirname );

      SetReqButtons( &AarrgghButton[0] );       

      (void) Handle_Problem( ErrMsg, "What did YOU select???", NULL );

      SetReqButtons( &DefaultButtons[0] );       
      
      goto ExitCountNumFiles;
      }

   if ((rval = CountFiles( start )) <= 0)
      {
      if (rval == 0)
         {
         // User didn't select a directory for us to use:
         
         sprintf( ErrMsg, "%s is NOT a directory!", dirname );
         
         SetReqButtons( &AarrgghButton[0] );       

         (void) Handle_Problem( ErrMsg, "What did YOU select???", NULL );

         SetReqButtons( &DefaultButtons[0] );
         rval = 10; // an abritrary value.
         } 
      else if (rval == -1)
         {
         rval = 10; // We already informed the User of this problem.
         }
      else if (rval == -2)
         {
         // We're out of memory!
         SetReqButtons( &AarrgghButton[0] );

         (void) Handle_Problem( "We've run out of memory!", 
                                "System Problem:", NULL
                              );

         SetReqButtons( &DefaultButtons[0] );       
         rval = -1;
         }
      }

ExitCountNumFiles:

   UnLock( start );
   return( rval );
}

PRIVATE void KillLV( void )
{
#  ifdef DEBUG
   fprintf( stderr, "Entering KillLV( %d )\n", NumAllocated );
#  endif

   if (NodeStrs != NULL)
      {
      FreeMem( NodeStrs, TXTLENGTH * NumAllocated );
      NodeStrs = NULL;
      }

   if (DocLVNodes != NULL)
      {
      FreeMem( DocLVNodes, NumAllocated * sizeof( struct Node ) );
      DocLVNodes = NULL;
      }

   NumAllocated = 0; // Deactivate the Allocation guard.
   return;
}

PRIVATE int ChangeDirBtClicked( int dummy );

PRIVATE int AllocateLV( int numdox, char *dirname )
{
#  ifdef DEBUG
   fprintf( stderr, "Entering AllocateLV( %d, %s )\n", numdox, dirname );
#  endif

   if (numdox < 1)
      {
      int ans = 0;
      
      sprintf( ErrMsg, "No Files in %s!\nWhat do you want to do?",
               dirname 
             );

      SetReqButtons( "Select New Directory|Ignore" );

      ans = Handle_Problem( ErrMsg, "Select a Document Directory?", NULL );

      SetReqButtons( &DefaultButtons[0] );

      if (ans == 0)
         {
         return( ChangeDirBtClicked( 0 ) );
         }
      else
         {
         // Hopefully, the User is smarter than this:
         NumDocuments = 0;
         return( RETURN_WARN );
         }
      }

   if (numdox != NumAllocated)
      {
      /* Get rid of previous allocation.  This is safe because 
      ** KillLV() checks for NULL pointers & doesn't do anything if
      ** there's no memory allocated
      */
      KillLV();
      }
   else
      {
      NumDocuments = numdox;
      return( 0 ); // don't have to do any allocation!
      }

   // --------- GUARDED SECTION: -----------------------------------

   DocLVNodes = (struct Node*) AllocMem( numdox 
                                            * sizeof( struct Node ),
                                         MEMF_CLEAR   
                                       );
   if (DocLVNodes == NULL)
      {
      KillLV();
      return( -1 );
      }

   NodeStrs = (UBYTE *) AllocMem( numdox * TXTLENGTH,
                                  MEMF_CLEAR   
                                );
   if (NodeStrs == NULL)
      {
      KillLV();
      return( -2 );
      }

   // --------- END OF GUARDED SECTION: ----------------------------

   NumDocuments = numdox;
   NumAllocated = numdox; // Activate the Allocation Guard again. 

   return( 0 );
}

PRIVATE void SetSelectedItem( char *filename )
{
   char *filepart = FilePart( filename );
   int   i;
   
   HideListFromView( DVGadgets[ DocListView ], DVWnd );

   for (i = 0; i < NumDocuments; i++)
      {
      if (strcmp( &NodeStrs[ i * TXTLENGTH ], filepart ) == 0)
         {
         break;
         }
      }

   ModifyListView( DVGadgets[ DocListView ], DVWnd, 
                   (struct List *) &DocLVList, NULL 
                 );

   GT_SetGadgetAttrs( DVGadgets[ DocListView ], DVWnd, NULL,
                      GTLV_Selected, i, TAG_DONE
                    );
   return;
}

PRIVATE void ClearNodeStrs( int numdox )
{
   int i, len = TXTLENGTH * numdox;
   
   for (i = 0; i < len; i++)
      *(NodeStrs + i) = '\0'; 

   return;
}

PRIVATE void FilesToListView( char *dirname )
{
   register BPTR mylock;
   int           count = 0;
   
   HideListFromView( DVGadgets[ DocListView ], DVWnd );

   ClearNodeStrs( NumDocuments );

   // We know how many files & have already allocated 
   // memory if necessary, so re-build the list:
   
   if ((mylock = Lock( dirname, ACCESS_READ )) == NULL)
      {
      sprintf( ErrMsg, "Can't lock %s\n", dirname );

      SetReqButtons( &AarrgghButton[0] );       

      (void) Handle_Problem( ErrMsg, "What did YOU select???", NULL );

      SetReqButtons( &DefaultButtons[0] );       
      
      goto ExitFilesToListView;
      }

   if ((Examine( mylock, The_fib ) == FALSE)  // Can't examine
         || (The_fib->fib_DirEntryType < 0))  // Found a file.
      {   
      // Not a directory, the User is a total SmegHead!
      UnLock( mylock );

      sprintf( ErrMsg, "%s is NOT a directory!", dirname );

      SetReqButtons( &AarrgghButton[0] );       

      (void) Handle_Problem( ErrMsg, "What did YOU select???", NULL );

      SetReqButtons( &DefaultButtons[0] );       

      goto ExitFilesToListView;
      }

   while (((ExNext( mylock, The_fib ) != 0)
           || (IoErr() != ERROR_NO_MORE_ENTRIES)) 
           && (count < NumDocuments))
      {
      if (The_fib->fib_DirEntryType < 0)
         {
         // Got a filename:

         if (FileFilter( ParsedPattern, The_fib ) == TRUE)
            {
            // File matched the pattern, copy to the List buffer:
            strncpy( &NodeStrs[ count * TXTLENGTH ], 
                     The_fib->fib_FileName,
                     TXTLENGTH - 1 
                   );

            count++;
            }
         }
      }             

   UnLock( mylock );

ExitFilesToListView:

   ModifyListView( DVGadgets[ DocListView ], DVWnd, 
                   (struct List *) &DocLVList, NULL 
                 );
   return;
}

/****i* SetupLV() *************************************************
*
* NAME
*    SetupLV()
*
* DESCRIPTION
*    Do all that administration stuff that exec wants to see.
*******************************************************************
*
*/

PRIVATE void SetupLV( void )
{
   int i = 0;

   DocLVNode.ln_Succ = (struct Node *) DocLVList.mlh_Tail;
   DocLVNode.ln_Pred = (struct Node *) DocLVList.mlh_Head;
   DocLVNode.ln_Type = NT_USER;

   DocLVNodes[0]         = DocLVNode;
   DocLVNodes[0].ln_Name = &NodeStrs[ 0 ];

   // change to signed char range:
   DocLVNodes[0].ln_Pri  = NumDocuments - 129;

   for (i = 1; i < NumDocuments; i++)
      {
      DocLVNodes[i].ln_Name = &NodeStrs[ i * TXTLENGTH ];
      DocLVNodes[i].ln_Pri  = NumDocuments - i - 129;
      DocLVNodes[i].ln_Type = NT_USER;
      }

   NewList( (struct List *) &DocLVList );

   for (i = 0; i < NumDocuments; i++)
      Enqueue( (struct List *) &DocLVList, &DocLVNodes[i] );

   return;
}

// ---------------------------------------------------------------

PRIVATE void SetCurrentPath( char *file_and_path )
{
   char *path_end = PathPart( file_and_path );
   int   len      = abs( path_end - file_and_path );

   if (len == 0)
      {
      // No Path????
      *CurrentPath = '\0';
      return;
      }   

   strncpy( CurrentPath, file_and_path, len );

   *(CurrentPath + len) = '\0'; // Fix strncpy() problem.

   return;
}

PRIVATE int ChangeDirBtClicked( int dummy )
{
   char title[] = "Enter a dummy filename from your directory...";
   char UserFileName[ STRLENGTH ];

   SetTagItem( &FileTags[0], ASLFR_Window, (ULONG) DVWnd );

   SetTagItem( &FileTags[0], ASLFR_InitialDrawer, 
               (ULONG) &TTParentDir[0]
             );

   SetTagItem( &FileTags[0], ASLFR_InitialPattern,
               (ULONG) &TTFileExtFilter[0]
             );

   SetTagItem( &FileTags[0], ASLFR_TitleText, 
               (ULONG) &title[0]
             );

   if (FileReq( UserFileName, &FileTags[0] ) > 1)
      {
      ULONG SaveNumDocs = NumDocuments;
      
      sprintf( SpareFName, "%s", &UserFileName[0] );

#     ifdef DEBUG
      fprintf( stderr, "CurrentPath is %s\n", CurrentPath );
#     endif

      SetCurrentPath( UserFileName );

#     ifdef DEBUG
      fprintf( stderr, "CurrentPath changed to:  %s\n", CurrentPath );
#     endif

      NumDocuments = CountNumFiles( CurrentPath );

      if (NumDocuments < 0)
         {
         int ans = TRUE;

         ans = Handle_Problem( "Should we continue?", 
                               "System Problem:", NULL
                             );
         
         if (ans == 0)
            return( (int) TRUE );
         else
            return( (int) FALSE );
         }
         
      if (NumDocuments > SaveNumDocs)
         {
         // Get more memory for the bigger directory &
         // re-build the ListView:
         int rval = 0;
         
         KillLV();
         
         rval = AllocateLV( NumDocuments, CurrentPath );

         if (rval == 0)
            {
            TTParentDir = CurrentPath; //????????
            SetupLV();
            FilesToListView( CurrentPath );
            }
         else if (rval < 0)
            {
            // Ran out of memory:
            int ans = TRUE;
 
            ans = Handle_Problem( "Ran out of Memory, should we continue?",
                                  "System Problem:", NULL
                                );
         
            if (ans == 0)
               return( (int) TRUE );
            else
               return( (int) FALSE );
            }
         else
            {
            // got RETURN_WARN:
            SetupLV();
            FilesToListView( CurrentPath );
            }
         }
      else
         {
         // Just erase the old strings & redisplay the ListView:
         SetupLV();
         FilesToListView( CurrentPath );
         }

      GT_SetGadgetAttrs( FileNameGad, DVWnd, NULL,
                         GTST_String, (STRPTR) SpareFName, TAG_END 
                       );
      
      SetSelectedItem( FILENAME );
      }

   return( (int) TRUE );
}

PRIVATE int DocListViewClicked( int whichitem )
{
   strcpy( SpareFName, &NodeStrs[ TXTLENGTH * whichitem ] );

   GT_SetGadgetAttrs( FileNameGad, DVWnd, NULL,
                      GTST_String, (STRPTR) SpareFName, TAG_END 
                    );

   return( (int) TRUE );
}

PRIVATE int ASLBtClicked( int dummy )
{
   char title[] = "Get the Document file name...";
   char UserFileName[ STRLENGTH ];

   SetTagItem( &FileTags[0], ASLFR_Window, (ULONG) DVWnd );

   SetTagItem( &FileTags[0], ASLFR_InitialPattern,
               (ULONG) &TTFileExtFilter[0]
             );

   SetTagItem( &FileTags[0], ASLFR_InitialDrawer, 
               (ULONG) CurrentPath // &TTParentDir[0]
             );

   SetTagItem( &FileTags[0], ASLFR_TitleText, 
               (ULONG) &title[0]
             );

   if (FileReq( UserFileName, &FileTags[0] ) > 1)
      {
      sprintf( SpareFName, "%s", &UserFileName[0] );

#     ifdef DEBUG
      fprintf( stderr, "CurrentPath is %s\n", CurrentPath );
#     endif

      SetCurrentPath( UserFileName );

#     ifdef DEBUG
      fprintf( stderr, "CurrentPath changed to:  %s\n", CurrentPath );
#     endif
      
      GT_SetGadgetAttrs( FileNameGad, DVWnd, NULL,
                         GTST_String, (STRPTR) SpareFName, TAG_END 
                       );
      }

   return( (int) TRUE );
}

PRIVATE int FileNameStrClicked( int dummy )
{
   if (strlen( FILENAME ) > 0)
      {
#     ifdef DEBUG
      fprintf( stderr, "CurrentPath is %s\n", CurrentPath );
#     endif

      SetCurrentPath( FILENAME );

#     ifdef DEBUG
      fprintf( stderr, "CurrentPath changed to:  %s\n", CurrentPath );
#     endif
      }

   return( (int) TRUE );
}

PRIVATE char command[256] = "";

PRIVATE int ViewFileBtClicked( int dummy )
{
   if (strlen( FILENAME ) > 0)
      {
#     ifdef DEBUG
      fprintf( stderr, "CurrentPath:  %s\n", CurrentPath );
      fprintf( stderr, "FILENAME   :  %s\n", FILENAME );
      fprintf( stderr, "FilePart   :  %s\n", FilePart( FILENAME ) );
#     endif

      if (strlen( CurrentPath ) == 0)
         {
         sprintf( command, "%s %s", TTFileViewer, FilePart( FILENAME ) );
         }
      else if (CurrentPath[ strlen( CurrentPath ) - 1 ] != ':' )
         {
         sprintf( command, "%s %s/%s", TTFileViewer, 
                  CurrentPath, FilePart( FILENAME ) 
                );
         }
      else // Volume name or logical device is the Path:
         {
         sprintf( command, "%s %s%s", TTFileViewer, 
                  CurrentPath, FilePart( FILENAME ) 
                );
         }

      // Tell the OS to run our file viewing command:
      if (System( &command[0], TAG_DONE ) != RETURN_OK)
         {
         sprintf( ErrMsg, "%s\n couldn't be run by the System,\n"
                  "check your spelling!", command
                );

         SetReqButtons( &AarrgghButton[0] );
         (void) Handle_Problem( ErrMsg, "Invalid ToolType?", NULL );
         SetReqButtons( &DefaultButtons[0] );
         }
      }
   else
      {
      sprintf( ErrMsg, "Enter a file name or select one\n"
                       "from the List Viewer first!" 
             );
             
      SetReqButtons( &AarrgghButton[0] );
      (void) Handle_Problem( ErrMsg, "User ERROR:", NULL );
      SetReqButtons( &DefaultButtons[0] );
      }   

   return( (int) TRUE );
}

PRIVATE void CloseDVWindow( void )
{
   if (DVWnd != NULL) 
      {
      CloseWindow( DVWnd );
      DVWnd = NULL;
      }

   if (DVGList != NULL) 
      {
      FreeGadgets( DVGList );
      DVGList = NULL;
      }

   if (DVFont != NULL) 
      {
      CloseFont( DVFont );
      DVFont = NULL;
      }

   return;
}

PRIVATE int DVCloseWindow( void )
{
   CloseDVWindow();
   return( (int) FALSE );
}

PRIVATE void ShowInfoReq( void )
{
   char msg[] = "DocumentViewer is a GUI for finding & displaying\n"
                "text files or AmigaGuide files.\n"
                "Change the ToolTypes & you can select which files\n"
                "to list & choose from.  You can also set the program\n"
                "that displays the file.\n\n"
                "   Written using GadToolsBox V37.300 & SAS-C V6.58\n\n"
                "   by Jim Steichen (email:  jsteic1957@aol.com)";
   
   SetReqButtons( "OKAY!" );
   (void) Handle_Problem( msg, "About DocumentViewer:", NULL );   
   SetReqButtons( &DefaultButtons[0] );
   
   return;
}

PRIVATE int DVVanillaKey( int whichkey )
{
   BOOL rval = TRUE;
   
   switch (whichkey)
      {
      case 'q': // quit
      case 'Q':
      case 'e': // end
      case 'E':
      case 'x': // exit
      case 'X':
         rval = FALSE; // In other words, die!!
         break;
         
      case 'c':
      case 'C':
         rval = ChangeDirBtClicked( 0 );
         break;
         
      case 'v':
      case 'V':
         rval = ViewFileBtClicked( 0 );
         break;

      case 'i':
      case 'I':
         ShowInfoReq();         // Information Requester.         
         break;

      default:
         break;
      }

   return( rval );
}

PRIVATE int OpenDVWindow( void )
{
   struct NewGadget   ng;
   struct Gadget   *g;
   UWORD      lc, tc;
   UWORD      wleft = DVLeft, wtop = DVTop, ww, wh;

   ComputeFont( Scr, Font, &CFont, DVWidth, DVHeight );

   ww = ComputeX( CFont.FontX, DVWidth );
   wh = ComputeY( CFont.FontY, DVHeight );

   if ((wleft + ww + CFont.OffX + Scr->WBorRight) > Scr->Width) 
      wleft = Scr->Width - ww;

   if ((wtop + wh + CFont.OffY + Scr->WBorBottom) > Scr->Height) 
      wtop = Scr->Height - wh;

   if ((DVFont = OpenDiskFont( Font )) == NULL)
      return( -5 );

   if ((g = CreateContext( &DVGList )) == NULL)
      return( -1 );

   for (lc = 0, tc = 0; lc < DV_CNT; lc++) 
      {
      CopyMem( (char *) &DVNGad[ lc ], (char *) &ng, 
               (long) sizeof( struct NewGadget )
             );

      ng.ng_VisualInfo = VisualInfo;
      ng.ng_TextAttr   = Font;

      ng.ng_LeftEdge   = CFont.OffX + ComputeX( CFont.FontX, 
                                                ng.ng_LeftEdge 
                                              );

      ng.ng_TopEdge    = CFont.OffY + ComputeY( CFont.FontY, 
                                                ng.ng_TopEdge 
                                              );

      if (DVGTypes[ lc ] != GENERIC_KIND) 
         {
         ng.ng_Width   = ComputeX( CFont.FontX, ng.ng_Width );
         ng.ng_Height  = ComputeY( CFont.FontY, ng.ng_Height);
         }

      DVGadgets[ lc ] = g = CreateGadgetA( (ULONG) DVGTypes[ lc ], 
                              g, 
                              &ng, 
                              (struct TagItem *) &DVGTags[ tc ] );

      if (DVGTypes[ lc ] == GENERIC_KIND) 
         {
         g->Flags        |= GFLG_GADGIMAGE | GFLG_GADGHIMAGE;
         g->Activation   |= GACT_RELVERIFY;
         g->GadgetRender  = (APTR)getImage;
         g->SelectRender  = (APTR)getImage;
         }

      while (DVGTags[ tc ] != NULL) 
         tc += 2;
      
      tc++;

      if (g == NULL)
         return( -2 );
      }

   if ((DVWnd = OpenWindowTags( NULL,

                  WA_Left,        wleft,
                  WA_Top,         wtop,
                  WA_Width,       ww + CFont.OffX + Scr->WBorRight, 
                  WA_Height,      wh + CFont.OffY + Scr->WBorBottom,

                  WA_IDCMP,       LISTVIEWIDCMP | IDCMP_GADGETUP
                    | STRINGIDCMP | BUTTONIDCMP | IDCMP_CLOSEWINDOW
                    | IDCMP_VANILLAKEY | IDCMP_REFRESHWINDOW,

                  WA_Flags,       WFLG_DRAGBAR | WFLG_DEPTHGADGET
                    | WFLG_CLOSEGADGET | WFLG_SMART_REFRESH
                    | WFLG_ACTIVATE | WFLG_RMBTRAP,

                  WA_Gadgets,     DVGList,
                  WA_Title,       DVWdt,
                  WA_ScreenTitle, ScrTitle,
                  TAG_DONE )
      ) == NULL)
      return( -4L );

   GT_RefreshWindow( DVWnd, NULL );

   return( 0 );
}

PRIVATE int HandleDVIDCMP( void )
{
   struct IntuiMessage *m;
   int                (*func)( int code );
   BOOL                 running = TRUE;

   while (running == TRUE)
      {
      if ((m = GT_GetIMsg( DVWnd->UserPort )) == NULL) 
         {
         (void) Wait( 1L << DVWnd->UserPort->mp_SigBit );
         continue;
         }

      CopyMem( (char *) m, (char *) &DVMsg, 
               (long) sizeof( struct IntuiMessage )
             );

      GT_ReplyIMsg( m );

      switch (DVMsg.Class) 
         {
         case IDCMP_REFRESHWINDOW:
            GT_BeginRefresh( DVWnd );
            GT_EndRefresh( DVWnd, TRUE );
            break;

         case IDCMP_CLOSEWINDOW:
            running = DVCloseWindow();
            break;

         case IDCMP_VANILLAKEY:
            running = DVVanillaKey( DVMsg.Code );
            break;

         case IDCMP_GADGETUP:
         case IDCMP_GADGETDOWN:
            func = (void *) ((struct Gadget *)DVMsg.IAddress)->UserData;
   
            if (func != NULL)                // Just in case.
               running = func( DVMsg.Code );
   
            break;
         }
      }
   
   return( running );
}

// --------------------------------------------------------------


PRIVATE void ShutdownProgram( void )
{
   KillLV(); // Did'n wanna do it, but it had to be done.

   if (The_fib != NULL)
      {
      FreeMem( The_fib, sizeof( struct FileInfoBlock ) );
      The_fib = NULL;
      }

   CloseDVWindow();
   CloseDownScreen();

   if (UtilityBase != NULL)
      CloseLibrary( (struct Library *) UtilityBase );

   if (IconBase != NULL)
      CloseLibrary( IconBase );

   CloseLibs();

   return;
}

PRIVATE int SetupProgram( void )
{
   int rval = 0;
   
   if (OpenLibs() < 0)
      return( -1 );
      
   if ((UtilityBase = OpenLibrary( "utility.library", 39 )) == NULL)
      {
      CloseLibs();
      return( -2 );
      }

   if (SetupScreen() < 0)
      {
      CloseLibs();
      CloseLibrary( UtilityBase );
      CloseLibrary( IconBase );
      return( -5 );
      }   

   if (OpenDVWindow() < 0)
      {
      ShutdownProgram();
      return( -6 );
      }   

   SetNotifyWindow( DVWnd );   

   if ((The_fib = (struct FileInfoBlock *) 
                   AllocMem( sizeof( struct FileInfoBlock ), 
                             MEMF_CLEAR )) == NULL)
      {
      SetReqButtons( &AarrgghButton[0] );

      (void) Handle_Problem( "Ran out of Memory, bailing out!",
                             "System Problem:", NULL
                           );
      ShutdownProgram();
      return( -7 );
      }

   NumDocuments = CountNumFiles( TTParentDir );
   
   if ((rval = AllocateLV( NumDocuments, TTParentDir )) != 0)
      {
      if (rval < 0)
         {
         // Memory allocation failure:
         SetReqButtons( &AarrgghButton[0] );

         (void) Handle_Problem( "Ran out of Memory, bailing out!",
                                "System Problem:", NULL
                              );
         ShutdownProgram();
         return( -8 );
         }
      else   
         {
         // One file name or less(??):
         SetReqButtons( "I understand, oh Great One!" );

         (void) Handle_Problem( "Select a NEW directory ASAP!",
                                "ParentDir ToolType Problem:", NULL
                              );

         SetReqButtons( &DefaultButtons[0] );
         }
      }

   SetupLV();
   strcpy( CurrentPath, TTParentDir );
   FilesToListView( TTParentDir );

   return( 0 );
}

PRIVATE void *processToolTypes( char **toolptr )
{
   if (toolptr == NULL)
      return( NULL );

   TTParentDir     = GetToolStr( toolptr, ParentDir,  DefParentDir  );
   TTFileViewer    = GetToolStr( toolptr, FileViewer, DefFileViewer );

   TTFileExtFilter = GetToolStr( toolptr, FileExtFilter, DefFileExtFilter);

   return( NULL );
}

PUBLIC int main( int argc, char **argv )
{
   struct WBArg  *wbarg;
   char         **toolptr = NULL;

   if ((IconBase = OpenLibrary( "icon.library", 37L )) == NULL)
      {
      fprintf( stderr, "Couldn't open the icon.library!\n" );
      return( ERROR_INVALID_RESIDENT_LIBRARY );
      }

   /* We need the ToolTypes in order to setup the program, so open 
   ** icon.library first & get the tooltypes before calling
   ** SetupProgram():
   */

   if (argc > 0)    /* from CLI:       */
      {
      // We prefer to use the ToolTypes: 
      (void) FindIcon( &processToolTypes, diskobj, argv[0] );
      }
   else             /* from Workbench: */
      {
      // argc = _WBArgc;  // Internal SAS variables.
      // argv = _WBArgv;
      
      wbarg   = &(_WBenchMsg->sm_ArgList[ _WBenchMsg->sm_NumArgs - 1 ]);
      toolptr = FindTools( diskobj, wbarg->wa_Name, wbarg->wa_Lock );

      processToolTypes( toolptr );
      }

   if (SetupProgram() < 0)
      {
      fprintf( stderr, "Couldn't setup %s!\n", argv[0] );
      return( RETURN_FAIL );
      }
      
   (void) HandleDVIDCMP();

   FreeDiskObject( diskobj );

   ShutdownProgram();

   return( RETURN_OK );
}

/* -------------- END of DocumentViewer.c file! ----------------- */
