/* CLASS: StackListBox
   Provide the specific capabilities of a list box that holds stack frame items.
   It's a child control of the StackPresenter class.

   REQUIRE: STACKPRE.CLS
*/!!

inherit(ListBox, #StackListBox, #(viewOptions /* display flag: 
0x1 = stack addres, 0x2 = code address,
0x4 = function's params */
framesDict /* dictionary of stack frame */
varLoaded /* collection of flag to load var */
totalFrames /* total number of displayable frames */
framesCached /* total frame already cached in the StackListBox */
doneCached /* boolean flag of cached buffer */
stackLabel /* label of stack frame elements */
lineNum /* vertical scroll position in list */
addrSize /* Maximum number of digits in processor address */ ), 2, nil)!!

now(class(StackListBox))!!

/* 3/6/1992 10:35 - PUBLIC -- Create a new Stack list box */
Def new(self, id, par | theLB)
{ 
  theLB := new(self:Behavior);
  /* create object */
  setVars(theLB, id, par);
  create(theLB, nil, "ListBox", LBS_STANDARD bitOr WS_CHILD
    bitOr LBS_STANDARD_NOSORT bitOr WS_HSCROLL);
  /* Error if can not create */  
  if handle(theLB) = 0
  then alert(System, self, #wCreateError);
  endif;
  init(theLB);
  ^theLB;
}
!!

now(StackListBox)!!

/* 3/6/1992 10:45 - PUBLIC --- Clear StackListBox */
Def clear(self)
{ 
  framesCached := 0; 
  doneCached := nil;
  totalFrames := 0;
  stackLabel := " ";
  clear(framesDict);
  clear(varLoaded);
  clearList(self);
}
!!

/* 3/6/1992 14:13 - PRIVATE 
  Given a Collection of Stack Frame - extract each frame and put into
  the display list according to its id
*/
Def extractFrames(self, frameColl, locID | i)
{ 
  /* Go through the collection and extract out its element to insert */
  do(frameColl,
    {using(idx) 
      add(framesDict, locID, idx);
      add(varLoaded, locID, nil);
      locID := locID + 1;
    });
  /* Free the collection */
  frameColl := nil;    
}
!!

/* 3/6/1992 10:54 - PUBLIC */
Def getSelStkFrame(self | selIdx)
{ 
  if (selIdx := getSelIdx(self)) <> nil
  then
    /* Check to cache some more stack frames into the list */
    if not(doneCached) cand (selIdx >= (framesCached - 5))
    then
      loadStackFrames(self);
      invalidateStackFrames(self);
    endif;
    ^selIdx; /* current stack frame ID */ 
  endif;
  ^nil;
}
!!

/* 3/11/1992 22:50 - PUBLIC 
  Return View options of stack frames
*/
Def getViewOptions(self)
{ 
  ^viewOptions;
}
!!

/* 3/6/1992 10:40 - PRIVATE */
Def init(self)
{
  framesDict := new(Dictionary, 5);
  varLoaded := new(Dictionary, 5); 
  viewOptions := 0;
  lineNum := 0;
  clear(self);
  requireWithPath(AddressLib, "addr.dll"); 
  if not(addrSize := maxOutputAddrDigits(AddressLibClass$Inst,0)) then 
    addrSize := 8; /* Default max number of digits in address */
  endif; 
  sendMessage(self, WM_SETFONT, 
        Call GetStockObject(SYSTEM_FIXED_FONT), 1L);
}
!!

/* 3/11/1992 21:52 - PRIVATE 
  Parsing the str into code, stack address and symbol tokens to display.
  the addrSize depend upon the max output address value in PROC.DLL
  NOTES: str format = [stackAddr codeAddr Symbol]
     6/30/93 - all addresses will not have 0x prefix. - PIP for stack.  
     column position: [(0 - addrSize)] [(addrSize+1 - (addrSize*2)+1)]...
*/
Def insertFrame(self, str, idx | tmpStr, hasSymbol )
{ 
  if (hasSymbol := (size(str) > ((addrSize*2)+1) )) then /* 2 addresses + 1 space */
    str := str + "(...)";
  endif;  
  select
    case viewOptions = 2 /* Include Code Address */
      str := subString(str, addrSize+1, size(str));
      stackLabel := "Code";
    endCase
    case viewOptions = 1 /* Include Stack Address */
      if hasSymbol then /* frame has symbol info */
        tmpStr := subString(str, 0, addrSize+1); 
        str := tmpStr + subString(str, (addrSize*2)+1, size(str));
      endif; 
      stackLabel := "Stack";
    endCase
    case viewOptions = 0 /* only function name */
      if hasSymbol then /* frame has symbol info */  
        /* Display symbol */
        str := subString(str, (addrSize*2)+1, size(str));
      else
        /* Display frame address if no symbol available */
        str := subString(str, 0, (addrSize+1));  /* no symbol */
      endif; 
      stackLabel := "Function"; 
    endCase
    default /* display all */
      stackLabel := left("Stack", addrSize, " ") + " Code";
  endSelect;
  /* Other options 3 */
  insertString(self, str, idx);
}
!!

/* 3/11/1992 23:08 - PUBLIC 
  Invalidate the stack frames list to display according to its options
*/
Def invalidateStackFrames(self)
{ 
  if size(framesDict) > 0
  then
    clearList(self);
    redrawOff(self);
    do (framesDict,
      {using(id) 
        insertFrame(self, id, keyAt(framesDict,id));
      });
    redrawOn(self);
    invalidate(self);
  endif;  
}
!!

/* 3/15/1992 12:47 - PUBLIC ]
  Sender must ensure that frameID is valid.
*/
Def isVarLoaded?(self, frameID)
{ 
  ^varLoaded[frameID];
}
!!

/* 3/6/1992 10:50 - PUBLIC 
  Load stack frames into list box - Setup cache so that only the first
  20 frames are read in.
*/
Def loadStackFrames(self| startF, endF, fColl, openNum, anyFrame)
{ 
  showWaitCurs();
  if not(anyFrame := anyFrames(stackSession(parent))) cor 
      (anyFrame = BOOL_FALSE) then
    ^nil;
  endif;  
  /* For now use the openStack frame */
  totalFrames := openStack(stackSession(parent), 20);

  if not(totalFrames) cor (totalFrames = 0) cor doneCached
  then
     ^nil;
  endif;

  /* There are frames to cache, so do it */
  if (framesCached < totalFrames) then
     openNum := min(totalFrames - framesCached, 20);
     startF := framesCached; /* old frame value */
     framesCached := framesCached + openNum;
     endF := framesCached - 1; /* new frame value */ 
         
     loop
     while startF < framesCached 
     begin 
        /* Return nil if cannot get stack frames */
        if not(fColl := getFrame(stackSession(parent), startF, endF)) then
          ^nil;
        endif;  
        /* Extract frames from collection */
        extractFrames(self, fColl, startF);
        startF := startF + size(fColl);
     endLoop;
     /* Done caching - No more frame to read */
     if framesCached = totalFrames
     then
       doneCached := true;
     endif;
  endif;
  showOldCurs();
}
!!

/* 3/11/1992 16:58 - PUBLIC 
  Set the current selected stack frame to frame ID, and display its 
  variables. 
  Notes: Parent of self must have a valid VarBrowser and valid. Also,
  currentFrame of parent must be valid.
*/
Def setSelStkFrame(self, frameID )
{ 
  if frameID then
    setCurSel(self, frameID);
    showStackFrameVar(parent); /* Show the variables of frame via its parent */
  endif;
}
!!

/* 3/11/1992 21:29 - PUBLIC 
  Set view options of the stack frame
*/
Def setViewOptions(self, option)
{ 
  viewOptions := option; 
}
!!

/* 3/13/1992 9:10 - PUBLIC 
  Return the stack label of its view options setting.
*/
Def stackLabel(self)
{ 
  ^stackLabel;
}
!!

/* 3/13/1992 13:54 - PUBLIC */
Def totalFrames(self)
{ 
  if totalFrames then
    ^totalFrames
  endif;
  ^0; /* No frame */
}
!!

/* 3/15/1992 12:49 - PUBLIC */
Def varLoaded(self, frameID)
{ 
  varLoaded[frameID] := #true;
}
!!

/* 3/15/1992 20:41 - PRIVATE
 Return the number of visible text lines in window. - TextWindow Class
*/
Def visLines(self)
{ 
  ^(height(clientRect(self)) / tmHeight(parent));
}
!!

/* Allow function keys to work without using accelerators. */
Def WM_KEYDOWN(self, wp, lp)
{
 if between(wp, 0x70, 0x79)
    checkFuncKey(TheStackPresenter, wp, 0x10000);
 else
    execWindowProc(self, #WM_KEYDOWN, wp, lp);   
 endif;
}
!!

/* 3/12/1992 22:17 - WINDOWS
  Respond to MS-Window's vertical scrolling message.
  wP tells what kind of scrolling request has been made. 
*/
Def WM_VSCROLL(self, wp, lp | topIdx, endFrame, idx)
{ 
  /* Peek into message to handle caching */
  if (wp = SB_ENDSCROLL) cand not(doneCached) 
  then
    endFrame := framesCached - visLines(self) - 2;  
    if (topIdx := sendMessage(self, LB_GETTOPINDEX, 0, 0L)) > endFrame
    then
      loadStackFrames(self);
      invalidateStackFrames(self);
      if not(idx := currentFrame(parent))
      then
        idx := 0;
        /* Set the currentFrame of parent to frame 0 */
        setCurrentFrame(parent, idx);
      endif;  
      /* Select the current stack frame */
      setSelStkFrame(self, idx);
      ^0
    endif;   
  endif;
  ^execWindowProc(self, #WM_VSCROLL, wp, lp);
}
 
!!
