/*************************************<+>*************************************
 *****************************************************************************
 **
 **  File:        HelpCache.c
 **
 **  Project:	   dthelpdemo demo program
 **
 **  Description: Contains the Help Callbacks and Utility functions for our
 **               demo tool dthelpdemo.
 **
 **
 **  (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994
 **      Hewlett-Packard Company
 **  (c) Copyright 1993, 1994 International Business Machines Corp.
 **  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
 **  (c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of
 **      Novell, Inc.
 **
 ****************************************************************************
 ************************************<+>*************************************/

/* System Include Files  */


#include <Xm/Xm.h>
#include <Xm/XmP.h>


#include <Dt/Help.h>
#include <Dt/HelpDialog.h>
#include <Dt/HelpQuickD.h>

/* Local Includes */
#include "Main.h"
#include "HelpCacheI.h"
#include "HourGlassI.h"


/********    Static Function Declarations    ********/

#ifdef _NO_PROTO
static void CloseHelpCB(); 
static Boolean GetFromCache();

#else

static void CloseHelpCB (
    Widget w,
    XtPointer clientData,
    XtPointer callData);
static Boolean GetFromCache(
    Widget parent,
    CacheListStruct **pCurrentNode);
#endif /* _NO_PROTO */



/* Global Main Help Dialog Widget */
static Widget helpMain=NULL;
static Widget versionMain=NULL;


/*****************************************************************************
 * Function:	    void HelpMapCB()
 *                   
 *                            
 *
 * Parameters:      clientData is the widget in reference to
 *                  which widget w is placed
 *
 * Return Value:    Void.
 *
 * Purpose: 	    Determins where a new child dialog should be mapped in
 *                  relation to its parent.
 *
 * Algorithm:       1. attempt left or right placement with no overlap
 *                  2. if fails, attempt up or down placement with no overlap
 *                  3. if fails, determines location with least
 *                     amount of overlap, and places there.
 *
 *****************************************************************************/
XtCallbackProc HelpMapCB(
    Widget w,
    XtPointer clientData,
    XtPointer callData)

{

    Arg         args[2];
    Widget      parent;
    Position    centeredY, bestX, bestY, pX, pY; 
    Dimension   pHeight, myHeight, pWidth, myWidth;
    Dimension   maxX, maxY;
    int	        rhsX, lhsX, topY, botY;   /* needs to be int, not Dimension */
    Display *   display;
    Screen *    screen;
    int         screenNumber;

    parent = (Widget)clientData;
    display = XtDisplay(w);
    screen = XtScreen(w);
    screenNumber = XScreenNumberOfScreen(screen);
    pX = XtX(parent);
    pY = XtY(parent);
    if (pX < 0) pX = 0;
    if (pY < 0) pY = 0;
    pHeight = XtHeight(parent);
    pWidth = XtWidth(parent);
    myHeight = XtHeight(w);
    myWidth = XtWidth(w);
    maxX = XDisplayWidth(display,screenNumber);
    maxY = XDisplayHeight(display,screenNumber);

    /* algorithm 
     * 1. attempt left or right placement with no overlap
     * 2. if fails, attempt up or down placement with no overlap
     * 3. if fails, places on the right in the middle
     */
    
    /* first try left right placement */
    bestY = pY + pHeight/2 - myHeight/2;
    centeredY = bestY;
    rhsX = pX + pWidth;
    lhsX = pX - myWidth - 8;     /* 8: account for border */
    if ( (rhsX + myWidth) < maxX ) bestX = rhsX;
    else if ( lhsX > 0 ) bestX = lhsX;
    else
    {
          /* then try up down placement */
        bestX = pX + pWidth/2 - myWidth/2;
        botY = pY + pHeight;
        topY = pY - myHeight - 44;     /* 44: account for menu border */
        if ( (botY + myWidth) < maxY ) bestY = botY;
        else if ( topY > 0 ) bestY = topY;
        else
        {
            /* otherwise, center vertically and on the right */
            bestX = maxX - myWidth;
            bestY = centeredY;
        }
    }

    XtSetArg(args[0], XmNx, bestX);
    XtSetArg(args[1], XmNy,  bestY);
    XtSetValues(w, args, 2);

    return((XtCallbackProc) NULL);

}


/****************************************************************************
 * Function:         CloseHelpCB(
 *                              Widget w,
 *                              XtPointer clientData, 
 *                      	XtPointer callData
 *
 * Parameters:      
 *
 * Return Value:    Void.
 *
 * Purpose: 	    Process close requests on all Help Dialog widgets 
 *                  created and managed by this application.
 *
 ***************************************************************************/
#ifdef _NO_PROTO
static void CloseHelpCB (w, clientData, callData) 
    Widget		w;		/*  widget id		*/
    XtPointer		clientData;	/*  data from applicaiton   */
    XtPointer     	callData;	/*  data from widget class  */
#else
static void CloseHelpCB (
    Widget w,
    XtPointer clientData,
    XtPointer callData)
#endif /* _NO_PROTO */
{

  Widget helpDialog = (Widget) clientData;

  CacheListStruct *pTemp; 
 
  pTemp = pCacheListHead;
  
  /* Search our Cache List for the closed help dialog */
  while ((pTemp->helpDialog != helpDialog) && (pTemp != NULL))
     pTemp = pTemp->pNext;
 

  if (pTemp == NULL)
    /* ERROR */
    printf("We did not find our help dialog widget in the cache list??? /n");

  /* Un Map and Clean up the help widget */
   XtUnmanageChild(helpDialog);
  pTemp->inUseFlag  = FALSE;
   

}



/****************************************************************************
 * Function:         CloseMainCB(
 *                              Widget w,
 *                              XtPointer clientData, 
 *                      	XtPointer callData
 *
 * Parameters:      
 *
 * Return Value:    Void.
 *
 * Purpose: 	    Process close requests on our main help dialog.
 *
 ***************************************************************************/
#ifdef _NO_PROTO
static void CloseMainCB (w, clientData, callData) 
    Widget		w;
    XtPointer		clientData;
    XtPointer     	callData;
#else
static void CloseMainCB (
    Widget w,
    XtPointer clientData,
    XtPointer callData)
#endif /* _NO_PROTO */
{
   Widget currentDialog = (Widget) clientData;
 


  /* Un Map and Clean up the help widget */
   XtUnmanageChild(currentDialog);

}




/****************************************************************************
 * Function:	    void ProcessLinkCB(
 *                              Widget w,
 *                              XtPointer  clientData, 
 *                      	XtPointer callData
 *
 * Parameters:      
 *
 * Return Value:    Void.
 *
 * Purpose: 	    Process JUMP-NEW and APP-LINK hypertext requests in a 
 *                  given Help Dialog Window.
 *                 
 *                  This is the callback used for the DtNhyperLinkCallback
 *                  on each of the help dialog widges created.
 *
 ****************************************************************************/
void ProcessLinkCB (
    Widget w,
    XtPointer clientData,
    XtPointer callData)

{
  Arg 		    args[20];
  int 		    n;
  Position          xPos, yPos;
  int               appLinkNum=0;
  int               count;
  static Dimension  width=0;
  static Dimension  height=0;
  static Boolean    goBigger=TRUE;

  DtHelpDialogCallbackStruct * hyperData = 
                         (DtHelpDialogCallbackStruct *) callData;
  

  switch (hyperData->hyperType)
    {
      case DtHELP_LINK_JUMP_NEW:

        DisplayTopic (XtParent(w), hyperData->helpVolume,
                      hyperData->locationId);
        
        break;

       case  DtHELP_LINK_MAN_PAGE:

         /* Create and display the requested man page */
        DisplayMan(XtParent(w), hyperData->specification);

        
         break;

      case  DtHELP_LINK_TEXT_FILE:

         /* Create a quick help dialog and display the text file in it */
        
         break;

       case  DtHELP_LINK_APP_DEFINE:

       appLinkNum = atoi(hyperData->specification);

      if (appLinkNum == 100)     /* Move the window */
        {

          /* First Place the window in the upper left */
          n = 0;
          XtSetArg(args[n], XmNx, 0); ++n; 
          XtSetArg(args[n], XmNy, 0); ++n;
          XtSetValues(topLevel, args, n);
        
          /* Now move it down to the center of the display */

          for (count = 1;count < 500; count= count+5)
            {
              n = 0;
              XtSetArg(args[n], XmNx, count); ++n; 
              XtSetArg(args[n], XmNy, count); ++n;
              XtSetValues(topLevel, args, n);
              XmUpdateDisplay(topLevel);
            }

        }


      if (appLinkNum == 101)      /* Resize the window */
        {

        if (width == 0)
          {
            /* Get the current dialog size */
            n =0;
            XtSetArg (args[n], XmNheight, &height);  n++;
            XtSetArg (args[n], XmNwidth, &width);   n++;
            XtGetValues(topLevel, args, n);
          }

        if (goBigger)
          {
            n =0;
            XtSetArg (args[n], XmNheight, height+100);  n++;
            XtSetArg (args[n], XmNwidth, width+50);   n++;
            XtSetValues(topLevel, args, n);
            goBigger = FALSE;
          }
        else
	  {
            /* Go smaller */
            n =0;
            XtSetArg (args[n], XmNheight, height);  n++;
            XtSetArg (args[n], XmNwidth, width);   n++;
            XtSetValues(topLevel, args, n);
            goBigger = TRUE;
          }

        }
 

      if (appLinkNum == 102)  
        {

	}

      if (appLinkNum == 103)  
        {

        }

      break;

      default:  /* Catches any other  applicaion definded link types */

         printf("We some how got a bogus hyptertext link type/n");


    }  /* End Switch Statement */
 
}




/****************************************************************************
 * Function:	    void DisplayMan()
 *
 * Parameters:      
 *
 * Return Value:    Void.
 *
 * Purpose: 	    Displays a UNIX man page in a quick help dialog.
 *
 ****************************************************************************/
void DisplayMan(
    Widget  parent,
    char    *man)

{

  Arg  args[20];
  int    n;
  Widget helpWidget;
  char *title;

  XmUpdateDisplay(topLevel);
 


  if (manWidget == NULL)
    {    
      /* Create the QuickHelpDialog widget for help on help */
      title = XtNewString(man);
 
      n =0;
      XtSetArg (args[n], XmNuseAsyncGeometry, True);         n++;
      XtSetArg (args[n], XmNtitle, title);                   n++;
      XtSetArg (args[n], DtNhelpType,DtHELP_TYPE_MAN_PAGE); n++; 
      XtSetArg (args[n], DtNmanPage, man);                   n++;
      manWidget = DtCreateHelpQuickDialog(topLevel,"manBox", args, n);
      XtFree((char*) title);


      XtAddCallback(manWidget, DtNcloseCallback,
                    CloseMainCB, (XtPointer) manWidget);
 
      /* Add the popup position callback to our man dialog */
      XtAddCallback (XtParent(manWidget), XmNpopupCallback,
                    (XtCallbackProc)HelpMapCB, 
                    (XtPointer)topLevel);
 



      /* We do not want a help button for now so we unmap it */     
      helpWidget = DtHelpQuickDialogGetChild (manWidget,
                              DtHELP_QUICK_HELP_BUTTON);
      XtUnmanageChild (helpWidget);

      XtManageChild(manWidget);  
    }
  else
    {
       TurnOnHourGlass(manWidget);
  
       /* We already have a quick help dialog so re-use it */
       n = 0;
       XtSetArg (args[n], DtNhelpType,DtHELP_TYPE_MAN_PAGE); n++; 
       XtSetArg (args[n], DtNmanPage, man);                   n++;
     
       XtSetValues(manWidget, args, n);
       

       title = XtNewString(man);
       n = 0;
       XtSetArg (args[n], XmNtitle, title);                   n++;
       XtSetValues(XtParent(manWidget), args, n);
       XtFree((char*) title);

       XtManageChild(manWidget); 
       XtMapWidget(XtParent(manWidget));    
       XRaiseWindow(XtDisplay(parent), XtWindow(XtParent(manWidget)));
       TurnOffHourGlass(manWidget);       

     }

}

  
/****************************************************************************
 * Function:	    void DisplayTopic(
 *                              Widget parent,
 *                              char *helpVolume,
 *                              char *locationId)
 *
 * Parameters:      
 *
 * Return Value:    Void.
 *
 * Purpose: 	    Creats and displays a new help dialog w/the requested help
 *                  volume and topic.
 *
 ****************************************************************************/
void DisplayTopic(
    Widget  parent,
    char    *helpVolume,
    char    *locationId)

{
  Arg	 	args[10];	
  int           n;

 CacheListStruct *pCurrentNode = NULL;
  Boolean       cachedNode = FALSE;


  /* Get a inuse node if we have one or a Cached one */
  cachedNode = GetFromCache(parent, &pCurrentNode);


  /* If we got a free one from the Cache, use it */
  /* Set Values on current free one, then map it */
  if (cachedNode)
    {
       n = 0;
       XtSetArg (args[n], XmNtitle, "HelpDemo Help");          n++;
       if (helpVolume != NULL)
         {
           XtSetArg (args[n],DtNhelpVolume,helpVolume);     n++; 
         }
       XtSetArg (args[n], DtNlocationId,locationId);        n++;
       XtSetValues(pCurrentNode->helpDialog, args, n);
  
       XtManageChild(pCurrentNode->helpDialog);    
       XtMapWidget(XtParent(pCurrentNode->helpDialog));
     }
   else
     {
       while (!XtIsSubclass(parent, applicationShellWidgetClass))
         parent = XtParent(parent);
     


        /* Build a new one in our cached list */
        n = 0;
        XtSetArg (args[n], XmNtitle, "Helpdemo Help");          n++;
       if (helpVolume != NULL)
         {
           XtSetArg (args[n],DtNhelpVolume,helpVolume);     n++; 
         }
        XtSetArg (args[n], DtNlocationId,locationId);        n++;
        pCurrentNode->helpDialog =  
                   DtCreateHelpDialog(parent, "helpWidget", args, n);


        XtAddCallback(pCurrentNode->helpDialog, DtNhyperLinkCallback,
                       ProcessLinkCB, NULL);
             
        XtAddCallback(pCurrentNode->helpDialog, DtNcloseCallback,
                      CloseHelpCB, (XtPointer) pCurrentNode->helpDialog);

        XtManageChild(pCurrentNode->helpDialog);    
        XtMapWidget(XtParent(pCurrentNode->helpDialog));
     }
}





/****************************************************************************
 * Function:	    void DisplayMain(
 *                              Widget parent,
 *                              char *helpVolume,
 *                              char *locationId)
 *
 * Parameters:      
 *
 * Return Value:    Void.
 *
 * Purpose: 	    Displays help for helpdemo in the one helpDialog window 
 *                  created for the applicaiton.
 *
 ****************************************************************************/
void DisplayMain (
    Widget  parent,
    char    *helpVolume,
    char    *locationId)

{
  Arg	 	args[10];	
  int           n;
 
  if (helpMain != NULL)
    {
       n = 0;
       XtSetArg (args[n], XmNtitle, "hemodemo Help");          n++;
       if (helpVolume != NULL)
         {
           XtSetArg (args[n],DtNhelpVolume,helpVolume);     n++; 
         }
       XtSetArg (args[n], DtNlocationId,locationId);        n++;
       XtSetValues(helpMain, args, n);
  
       XtManageChild(helpMain);    
       
     }
   else
     {
       while (!XtIsSubclass(parent, applicationShellWidgetClass))
         parent = XtParent(parent);
     

        /* Build a new one in our cached list */
        n = 0;
        XtSetArg (args[n], XmNtitle, "Helpdemo Help");       n++;
       if (helpVolume != NULL)
         {
           XtSetArg (args[n],DtNhelpVolume,helpVolume);     n++; 
         }
        XtSetArg (args[n], DtNlocationId,locationId);        n++;
        helpMain = DtCreateHelpDialog(parent, "helpWidget", args, n);

        XtAddCallback(helpMain, DtNhyperLinkCallback,
                       ProcessLinkCB, NULL);
             
        XtAddCallback(helpMain, DtNcloseCallback,
                      CloseMainCB, (XtPointer) helpMain);

        /* Add the popup position callback to our main help dialog */
        XtAddCallback (XtParent(helpMain), XmNpopupCallback,
                        (XtCallbackProc)HelpMapCB, 
                        (XtPointer)parent);
 
        XtManageChild(helpMain);    
        
     }
}






/****************************************************************************
 * Function:	    void DisplayVersion(
 *                              Widget parent,
 *                              char *helpVolume,
 *                              char *locationId)
 *
 * Parameters:      
 *
 * Return Value:    Void.
 *
 * Purpose: 	    Displays the version dialog for the helpdemo program.
 *
 ****************************************************************************/
void DisplayVersion (
    Widget  parent,
    char    *helpVolume,
    char    *locationId)

{
  Arg	 	args[10];	
  int           n;
  Widget        printWidget;
  Widget        helpWidget;
  Widget        backWidget;

 
  if (versionMain != NULL)
    {
       n = 0;
       XtSetArg (args[n], XmNtitle, "Helpdemo Version Dialog");          n++;
       if (helpVolume != NULL)
         {
           XtSetArg (args[n],DtNhelpVolume,helpVolume);     n++; 
         }
       XtSetArg (args[n], DtNlocationId,locationId);        n++;
       XtSetValues(versionMain, args, n);
  
       XtManageChild(versionMain);    
       
     }
   else
     {
       while (!XtIsSubclass(parent, applicationShellWidgetClass))
         parent = XtParent(parent);
     

        /* Build a new one in our cached list */
        n = 0;
        XtSetArg (args[n], XmNtitle, "Helpdemo Version Dialog");       n++;
       if (helpVolume != NULL)
         {
           XtSetArg (args[n],DtNhelpVolume,helpVolume);     n++; 
         }
        XtSetArg (args[n], DtNlocationId,locationId);        n++;
	XtSetArg (args[n], DtNhelpType, DtHELP_TYPE_TOPIC);  n++;
        versionMain = DtCreateHelpQuickDialog(parent,"versionWidget",args,n);

        XtAddCallback(versionMain, DtNcloseCallback,
                      CloseMainCB, (XtPointer) versionMain);


        /* We do not want a print button for now so we unmap it */     
        printWidget = DtHelpQuickDialogGetChild (versionMain, 
                                         DtHELP_QUICK_PRINT_BUTTON);
        XtUnmanageChild (printWidget);
  

        /* We do not want a help button for now so we unmap it */     
        helpWidget = DtHelpQuickDialogGetChild (versionMain, 
                                        DtHELP_QUICK_HELP_BUTTON);
        XtUnmanageChild (helpWidget);
  
        backWidget = DtHelpQuickDialogGetChild (versionMain, 
                                        DtHELP_QUICK_BACK_BUTTON);
        XtUnmanageChild (backWidget);

        XtManageChild(versionMain);    
        
     }
}






/****************************************************************************
 * Function:	    static CacheListStruct GetFromCache(
 *                                  Widget parent);
 *
 * Parameters:      
 *
 * Return Value:    Void.
 *
 * Purpose: 	    Gets a free help node form our cache list.  If none are
 *                  free, it will return fallse and the calling routine will
 *                  create a new help dialog widget.
 *
 ****************************************************************************/
#ifdef _NO_PROTO
static Boolean GetFromCache(parent, pCurrentNode)
    Widget parent;
    CacheListStruct **pCurrentNode;
#else
static Boolean GetFromCache(
    Widget parent,
    CacheListStruct **pCurrentNode)
#endif /* _NO_PROTO */

{

  CacheListStruct *pTemp; 
 
  if (pCacheListHead == NULL)
    {
       /* We have a new list so lets create one and pass it back */
       pCacheListHead = 
                (CacheListStruct *) XtMalloc((sizeof(CacheListStruct)));

       /* Assign the default values to our node */
       pCacheListHead->helpDialog = NULL;
       pCacheListHead->inUseFlag  = TRUE;
       pCacheListHead->pNext      = NULL;
       pCacheListHead->pPrevious = NULL;

       /* Assign our tale pointer */
       pCacheListTale = pCacheListHead;

       /* Make sure or totalNodes counter is correct, e.g. force it to 1 */
       totalCacheNodes = 1;
    
       /* Return our head pointer because it's our first and only node */
       *pCurrentNode = pCacheListHead;
       return (FALSE);

    }
  else
    { 
       /* We have need for an in-use help dialog or a new one, so look */
       pTemp = pCacheListHead;
      
       while (pTemp != NULL)
         {
           if (pTemp->inUseFlag == FALSE)
             {
                pTemp->inUseFlag = TRUE;
                *pCurrentNode = pTemp;
                return (TRUE);
	      }
           else
             pTemp = pTemp->pNext;
	 }
      

       /* If we did not find a free nod then we must add a new one to the
        * top of the list, and return it.
        */

       pTemp =  (CacheListStruct *) XtMalloc((sizeof(CacheListStruct)));

       /* Assign the default values to our node */
       pTemp->helpDialog = NULL;
       pTemp->inUseFlag  = TRUE;
     
       pTemp->pNext      = pCacheListHead;
       pTemp->pPrevious  = NULL;

       pCacheListHead->pPrevious = pTemp;

       /* Re-Assign our head pointer to point to the new head of the list */
       pCacheListHead = pTemp;

       /* Make sure or totalNodes counter is correct, e.g. force it to 1 */
       totalCacheNodes = totalCacheNodes + 1;

       /* Return our head pointer because it's our new node */
       *pCurrentNode = pCacheListHead;
       return (FALSE);

     }
  

}



















