/*
 * WindowDirect      (C) 1993 Software Sculptors
 *                       All Rights Under Copyright Reserved
 *
 * Version 1.0
 *
 * Written using SAS/C 6.2
 *
 * PROGRAM AND SOURCE CODE ARE FREELY (RE)DISTRIBUTABLE.
 *
 * By John M. Haubrich, Jr.
 */
#include <exec/types.h>
#include <exec/libraries.h>
#include <libraries/commodities.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/commodities_protos.h>
#include <clib/intuition_protos.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#ifdef LATTICE
int CXBRK(void) { return(0); }
int chkabort(void) { return(0); }
#endif

/*
 * VERSION STRING ===================================
 */
char *vers="$VER: WindowDirect 1.0 (16.06.93)";


/*
 * DEFINES ------------------------------------------
 */
#define BASENAME              "WindowDirect"

#define MAXINFOCHARS          128   // 'Information...' allows 128 chars max
                                    // in string gadget.

#define WARNLEVEL_IGNORE        0
#define WARNLEVEL_DISPLAYBEEP   1
#define WARNLEVEL_REQUEST       2


/*
 * STRUCTURE DEFINITIONS ----------------------------
 */
struct NewBroker newbroker = {
   NB_VERSION,
   "WindowDirect",
   "Jumps to user-defined screens and",
   "windows.",
   NBU_UNIQUE | NBU_NOTIFY,
   0, 0, 0, 0
};

struct MyGlobals {
   struct MsgPort *broker_mp;
   CxObj          *broker;
   ULONG           cxsigflag;  // our combined message port signal FLAG

   BOOL            fRunFromWB;
   BOOL            fBeQuiet;
   UBYTE           bWarningLevel;
   char            szHotkeyDesc[12][MAXINFOCHARS];
   char            szScrFHotkey[12][MAXINFOCHARS];    // F1-F12 screen
   char            szWdwFHotkey[12][MAXINFOCHARS];    // F1-F12 window
   char            szPatternBuf[MAXINFOCHARS * 3];
};


/*
 * GLOBAL VARIABLES ---------------------------------
 */
struct MyGlobals  MG;


/*
 * FUNCTION PROTOTYPES ------------------------------
 */
void main( int, char ** );
void ProcessMsg(void);
int  JMH_EasyReq( char *, char *, char *);
void JMH_Complain(char *);
void JMH_Info(char *);
int  JMH_Query(char *, char *);
void JMH_Quit(char *);
VOID JMH_InitVars(void);


/*
 * LIBRARY VARIABLES --------------------------------
 */
struct Library       *CxBase        = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct IconBase      *IconBase      = NULL;
struct DosBase       *DosBase       = NULL;


/*
 * FUNCTIONS ----------------------------------------
 */

/*
 * JMH_EasyReq(): pop up JMH_EasyRequest() with a custom title bar, message and
 *            response buttons.
 -------------------------------------------------------------------------*/
int JMH_EasyReq( char *title, char *text, char *resp ) {
   static struct EasyStruct es = {
      sizeof(struct EasyStruct),
      NULL,
      NULL,
      NULL,
      NULL
   };
   int stat;


   if ( MG.fBeQuiet == FALSE )
   {
      if ( MG.fRunFromWB == TRUE )
      {
         es.es_Title = title;
         es.es_TextFormat = text;
         es.es_GadgetFormat = resp;

         stat = EasyRequest(NULL, &es, NULL, TAG_DONE);
      }
      else
      {
         printf( "%s\n", text );
         stat = -1;
      }
   }
   return( stat );
}

/*
 * JMH_Complain(): pop up JMH_EasyRequest() with a JMH_Complaint message
 -------------------------------------------------------------------------*/
void JMH_Complain(char *t)
{
   char buf[100];


   if (t) {
      sprintf( buf, "%s Complaint...", BASENAME );
      JMH_EasyReq( buf, t, "OK" );
   }
}

/*
 * JMH_Info(): pop up JMH_EasyRequest() with a (nice) informative message
 *             if WB flag set else send output to stderr
 -------------------------------------------------------------------------*/
void JMH_Info(char *t)
{
   char buf[100];


   if (t) {
      sprintf( buf, "%s Information...", BASENAME );
      JMH_EasyReq( buf, t, "OK" );
   }
}

/*
 * JMH_Query(): are you sure... ? (1=yes, 0=no)
 -------------------------------------------------------------------------*/
int JMH_Query(char *t, char *response) {
   char  buf[100];
   char *resp;
   int   stat;


   if (t) {
      sprintf( buf, "%s Request...", BASENAME );

      if (response)
         resp = response;
      else
         resp = "Yes|No";
      stat = JMH_EasyReq( buf, t, resp );
      return(stat);
   }
}

/*
 * JMH_Quit(): close down everything we've opened and JMH_Quit
 -------------------------------------------------------------------------*/
void JMH_Quit( char *t )
{
   char  szQuitBuf[256+20];


   if ( t )
   {
      sprintf( szQuitBuf, "%s\nExiting...", t );
      JMH_Complain( szQuitBuf );
   }

   if (MG.broker)       DeleteCxObjAll(MG.broker);
   if (MG.broker_mp)    DeletePort(MG.broker_mp);

   if (DosBase)         CloseLibrary((struct Library *)DosBase);
   if (CxBase)          CloseLibrary(CxBase);
   if (IconBase)        CloseLibrary((struct Library *)IconBase);
   if (IntuitionBase)   CloseLibrary((struct Library *)IntuitionBase);

   exit( ( t ? TRUE : FALSE ) );
}


/*
 * JMH_InitVars(): initialize variables to default values
 -------------------------------------------------------------------------*/
VOID JMH_InitVars()
{
   USHORT  us;


   //
   // initialize variables...
   // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   MG.broker            = NULL;
   MG.broker_mp         = NULL;
   MG.fRunFromWB        = TRUE;
   MG.fBeQuiet          = FALSE;

   // initialize F1 thru F12 strings to "".
   for ( us = 0; us < 12; us++ )
   {
      strcpy( MG.szScrFHotkey[us], "" );
      strcpy( MG.szWdwFHotkey[us], "" );
   }
}


/**
 ** M A I N   P R O G R A M   -----------------------
 **/
void main( int argc, char *argv[] )
{
   UBYTE            **ttypes,
                     *temp;
   CxMsg             *msg;
   CxObj             *filter[12];
   LONG               errorcode;
   USHORT             us;
   char               szErrBuf[256], szHotkeyDesc[25], szTempBuf[10], *psz;
   BOOL               fCreationError;


   //
   // open libraries
   // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   IntuitionBase = (struct IntuitionBase *)
      OpenLibrary("intuition.library",39L);
   if (!IntuitionBase) JMH_Quit("Cannot open intuition.library 39+");

   IconBase = (struct IconBase *)OpenLibrary("icon.library",37L);
   if (!IconBase) JMH_Quit("Cannot open icon.library 37+");

   CxBase = OpenLibrary("commodities.library",37L);
   if (!CxBase)   JMH_Quit("Cannot open commodities.library 37+");

   DosBase = (struct DosBase *)OpenLibrary("dos.library",37L);
   if (!DosBase)   JMH_Quit("Cannot open dos.library 37+");

   //
   // initialize variables
   // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   JMH_InitVars();

   if (!(MG.broker_mp = CreateMsgPort()))
      JMH_Quit( "Cannot create broker message port." );

   //
   // user may only run from Workbench
   // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   MG.fRunFromWB = TRUE;

   // get opts from TOOLTYPES in icon
   ttypes = ArgArrayInit( argc, argv );

   // user-specified priority for the commodity
   newbroker.nb_Pri = (BYTE)ArgInt( ttypes, "CX_PRIORITY", 0 );

   // report errors?
   temp = ArgString( ttypes, "QUIET", "FALSE" );
   if ( strcmpi( temp, "TRUE" ) == 0 )
   {
      MG.fBeQuiet = TRUE;
   }
   else
   {
      MG.fBeQuiet = FALSE;
   }

   // set warning level
   temp = ArgString( ttypes, "WARNINGLEVEL", "IGNORE" );
   if      ( strcmpi( temp, "IGNORE"      ) == 0 ) MG.bWarningLevel = WARNLEVEL_IGNORE;
   else if ( strcmpi( temp, "DISPLAYBEEP" ) == 0 ) MG.bWarningLevel = WARNLEVEL_DISPLAYBEEP;
   else if ( strcmpi( temp, "REQUEST"     ) == 0 ) MG.bWarningLevel = WARNLEVEL_REQUEST;

   // read hotkey descriptions
   for ( us = 0; us < 12; us++ )
   {
      sprintf( szHotkeyDesc, "HOTKEY%d", us+1 );
      temp = ArgString( ttypes, szHotkeyDesc, "" );

      // find first delimeter
      psz = strchr( temp, '~' );
      if ( psz != NULL )
      {
         *psz = 0;
         psz++;

         // copy hotkey spec
         strcpy( MG.szHotkeyDesc[us], temp );

         // find second delimeter
         temp = psz;
         psz = strchr( temp, '~' );
         if ( psz != NULL )
         {
            *psz = 0;
            psz++;

            // copy screen parameter
            strcpy( MG.szScrFHotkey[us], temp );

            // copy window parameter
            strcpy( MG.szWdwFHotkey[us], psz );
         }
      }
   }

   ArgArrayDone();

   newbroker.nb_Port = MG.broker_mp;
   MG.cxsigflag = 1L << MG.broker_mp->mp_SigBit;

   if ( MG.broker = CxBroker(&newbroker, &errorcode) )
   {
      //
      // Establish all non-NULL filters
      //
      strcpy( szErrBuf, "Error creating filters for keys:\n");
      for ( fCreationError = FALSE, us = 0; us < 12; us++ )
      {
         if ( ( strcmp( MG.szHotkeyDesc[us], "" ) != 0 ) &&
              ( strcmp( MG.szScrFHotkey[us], "" ) != 0 ) &&
              ( strcmp( MG.szWdwFHotkey[us], "" ) != 0 ) )
         {
            // us+1 = hotkeys 1..12
            if ( filter[us] = HotKey( MG.szHotkeyDesc[us], MG.broker_mp, us+1 ))
            {
               AttachCxObj( MG.broker, filter[us] );
            }
            if ( CxObjError( filter[us] ) )
            {
               fCreationError = TRUE;
               sprintf( szTempBuf, "%d, ", us+1 );
               strcat( szErrBuf, szTempBuf );
            }
         }
      }
      if ( fCreationError )
      {
         JMH_Quit( szErrBuf );
      }
      else
      {
         ActivateCxObj( MG.broker, TRUE );
         ProcessMsg();  // process msgs until user quits...
      }
   }
   else
   {
      //
      // an error has occured.. show user!
      //
      switch ( errorcode )
      {
         case CBERR_SYSERR:
            JMH_Quit( "Broker SYSTEM error.\nNo memory?" );
            break;
         case CBERR_DUP:
            //
            // this message is displayed in ProcessMsg(): 'already running...'
            //
            break;
         case CBERR_VERSION:
            JMH_Quit( "Broker VERSION error." );
            break;
      }
   }

   //
   // Empty message port of all remaining messages
   //
   while ( msg = (CxMsg *)GetMsg( MG.broker_mp ) )
      ReplyMsg( (struct Message *)msg );

   JMH_Quit( NULL );
}

void ProcessMsg()
{
   CxMsg             *msg;
   ULONG              sigrcvd,
                      msgid,
                      msgtype;
   BOOL               fKeepGoing = TRUE;
   struct Screen     *scr;
   struct Window     *wdw;
   char              *pszTitle;
   BOOL               fFound;


   while ( fKeepGoing == TRUE )
   {
      sigrcvd = Wait( SIGBREAKF_CTRL_C | MG.cxsigflag );

      while ( msg = (CxMsg *)GetMsg( MG.broker_mp ) )
      {
         msgid = CxMsgID( msg );
         msgtype = CxMsgType( msg );
         ReplyMsg( (struct Message *)msg );

         switch ( msgtype )
         {
            case CXM_IEVENT:
               if ( ( msgid >= 1 ) && ( msgid <= 12 ) )
               {
                  //
                  // find screen/window using DOS pattern-matching;
                  // activate, bring screen/window to front and
                  // bring into view using ScreenPosition() if using
                  // Intuition V39 or higher.
                  //
                  if ( ParsePattern( MG.szScrFHotkey[msgid-1], MG.szPatternBuf, sizeof( MG.szPatternBuf ) ) != -1 )
                  {
                     // scan screenlist...
                     fFound = FALSE;
                     for ( scr = IntuitionBase->FirstScreen; scr; scr = scr->NextScreen )
                     {
                        // use Default Title if there's no on-screen title
                        if ( scr->Title == NULL )  pszTitle = scr->DefaultTitle;
                        else                       pszTitle = scr->Title;

                        if ( MatchPattern( MG.szPatternBuf, pszTitle ) == TRUE )
                        {
                           if ( ParsePattern( MG.szWdwFHotkey[msgid-1], MG.szPatternBuf, sizeof( MG.szPatternBuf ) ) != -1 )
                           {
                              // ...then windowlist
                              for ( wdw = scr->FirstWindow; wdw; wdw = wdw->NextWindow )
                              {
                                 if ( MatchPattern( MG.szPatternBuf, wdw->Title ) == TRUE )
                                 {
                                    fFound = TRUE;
                                    ScreenToFront( scr );
                                    WindowToFront( wdw );
                                    ActivateWindow( wdw );

                                    if ( IntuitionBase->LibNode.lib_Version >= 39 )
                                    {
                                       ScreenPosition( scr,
                                          SPOS_MAKEVISIBLE | SPOS_FORCEDRAG,
                                          wdw->LeftEdge,
                                          wdw->TopEdge,
                                          wdw->LeftEdge + wdw->Width,
                                          wdw->TopEdge  + wdw->Height
                                       );
                                    }

                                    scr = NULL;    // break outer loop
                                    break;
                                 }
                              }
                           }
                        }
                     }
                     if ( !fFound )
                     {
                        // show the WARNING!!!
                        switch( MG.bWarningLevel )
                        {
                           case WARNLEVEL_IGNORE:
                              break;
                           case WARNLEVEL_DISPLAYBEEP:
                              DisplayBeep( NULL );
                              break;
                           case WARNLEVEL_REQUEST:
                              JMH_Info( "Cannot find screen/window." );
                              break;
                        }
                     }
                  }
               }
               break;

            case CXM_COMMAND:
               switch ( msgid )
               {
                  case CXCMD_DISABLE:
                     ActivateCxObj( MG.broker, FALSE );
                     break;

                  case CXCMD_ENABLE:
                     ActivateCxObj( MG.broker, TRUE );
                     break;

                  case CXCMD_KILL:
                     fKeepGoing = FALSE;
                     break;

                  case CXCMD_UNIQUE:
                     JMH_Complain( "WindowDirect is already running!" );
                     break;
               }
               break;
         }
         if ( fKeepGoing == FALSE ) break;
      }
      if ( sigrcvd & SIGBREAKF_CTRL_C )
      {
         fKeepGoing = FALSE;
      }
   }
}
