/* CLASS:  StackPresenter - STACKPRE.CLS
   Display the run-time call-stack frames in a comprehensible format and 
   allows the users manipulate the stack frame variables.  Its child controls
   including a StackListBox, a StackMeter and a VarBrowser.
   Use event notification library.

   REQUIRE: STKSERVE.DLL and STACKLIB.CLS, STKLIBIN.CLS, PSTACK.H
   STACKLIS.CLS and VARBROWS.CLS .
*/!!

inherit(Window, #StackPresenter, #(childActions /* control action dictionary */
stkFrameLB  /* stack frames list box */
stkMeter /* stack meter object */
stkFrameLBTitle /* stk frames LB title */
stkMeterTitle /* stack meter title */
stkVarBrowserTitle /* variable browser title */
mWidth /* stack meter width */
mHeight /* stack meter height */
stkHWMark /* hwmark arrow object */
stkPercentUsage /* precent stack usage */
stkErrState /* stack state: 1 = ok, 3 = overflow, 2 = underflow */
warnedUser? /* true or nil - warning already displayed */
stkVarBrowser /* object handle */
currentFrame /* current selected frame */
eventDescSet /* Events of The Presenter */
eventRespSet /* Events Responders */
stackSession /* Stack server session ID */
baseAddr     /* address of stack base - U32 */
stackSize    /* size of stack in bytes - U32 */
alarmLimit   /* address of alarm limit - U32 */
alarmPercent /* Alarm Limit Percentage */
currentSP    /* current StackPointer - U32 */
hwMarkAddr   /* address of HWMark */
enableStkAlarm /* boolean flag */
enableStkHWMark /* boolean flag */
validStackBase /* valid base addr */ validStackRange /* base, size valid */              lowToHigh    /* boolean flag of grow direction */
fieldEnabled /* VarBrowser */
contextDirty /* flag indicates context is dirty */
tmWidth /* text metric */
tmHeight
addrWidth  /* #digits in address */
minWindowWidth  /* don't get too skinny */
minWindowHeight /* don't get too short */), 2, nil)!!

setClassVars(StackPresenter, #())!!

now(class(StackPresenter))!!

/* 6/2/1992 9:37 - PUBLIC
  Defined the default style of the Stack Presenter
*/
Def style(self)
{ 
  ^WS_OVERLAPPEDWINDOW;
}
!!

/* 3/2/1992 16:03 - PUBLIC 
  Open a new Stack Presenter. 
  Example: open(StackPresenter);
*/
Def open(self | newInstance)
{
  if (newInstance := new(StackPresenter, nil, nil, "Stack" , nil)) then
    show(newInstance, SW_SHOW);
  endif;
  ^newInstance;
} 
!!

/* start presenter with specific position */
Def openWithPosAndState(self, sizeRect, showVal | newInstance, x, y)
{
  /* Provide a reasonable default size for the Stack window */
  if not(sizeRect) then
    /* Set its location and size relative to the screen size */
    x := (x(screenSize())/10);
    y := (y(screenSize())/10);
    if (sizeRect := new(Rect))
      init(sizeRect, x, y, x+250, y+400);
    endif;  
  endif;      
  /* Open Stack window */
  if (newInstance := newStyle(StackPresenter, nil, nil, "Stack",
                   sizeRect, nil, WS_OVERLAPPEDWINDOW)) then
    show(newInstance, showVal);
  endif;
  ^newInstance;
}
!!


/* 3/2/1992 16:07 - PUBLIC*/
Def wndClass(self)
{ 
  ^"Stack Presenter";
}
!!

/* 3/2/1992 16:08 - PUBLIC 
  Return the name of the Stack Presenter Icon
*/
Def wndIcon(self)
{ 
  ^"STACK";
}
!!

now(StackPresenter)!!

/* 3/17/1994 14:02 - PUBLIC (to child object only)
  Return the addrWidth of self
*/
Def addrWidth(self)
{ 
  ^addrWidth;
}
!!

/* 3/11/1992 21:10 - PRIVATE 
  Display the source of the current selected stackframe.
*/
Def displayCaller(self | callerAddr, result, symName)
{ 
  /* Get the calling address = mapping (return address-2) to symbol address */
  if not(callerAddr := getCallerAddress(stkFrameLB, currentFrame)) then
    ^nil;
  endif;
  /* Get the symbol name of the inspected frame for hilighting */  
  if (result := getFrameInfo(stackSession, currentFrame)) then
    /* NOTES: result = #(symDesc, moduleDesc, addrDesc) */
    symName := getSymbolName(SymbolLibClass$Inst, result[0]);
    destroyAddress(AddressLibClass$Inst, result[2]);
  endif;
  
  showWaitCurs();
  if not(TheSourcePresenter) then
     TheSourcePresenter := open(SourcePresenter);
  endif; 
  /* 
  ** Show the calling location of the funtion in the Source Presenter.
  ** The Source Presenter will consume the callerAddr.
  */
  showDataObjFromAddress(TheSourcePresenter, callerAddr, symName);
  show(TheSourcePresenter, SW_NORMAL);
  bringToTop(TheSourcePresenter);
  showOldCurs();
}
!!

/* 3/17/1993 9:20 - MENU
  Show the caller source of the current selected stackframe.
*/
Def cmdStkInspectCaller(self, wp)
{ 
  /* display the source of the current stack frame */
  if (currentFrame) then
    displayCaller(self);
  endif;  
}
!!

/* 7/13/1993 12:08 - PRIVATE
  Retrieve all stack information from the server.
*/
Def getAllStackInfo(self | result, stackInfo, stackRange)
{ 
  if not(stackInfo := getStackInfo(stackSession))
    ^nil;
  endif;
  /* stackInfo = #(currentSP, baseAddr, stackSize, 
  **               alarmPercent, alarmLimit, enableStkAlarm, 
  **               hwMarkAddr, enableStkHWMark, validBaseAddr, lowToHigh); 
  */
  destroy(stackSession, currentSP);
  destroy(stackSession, baseAddr);
  destroy(stackSession, alarmLimit);
  destroy(stackSession, hwMarkAddr);

  currentSP       := stackInfo[0];
  baseAddr        := stackInfo[1];
  stackSize       := stackInfo[2];
  alarmPercent    := stackInfo[3];
  alarmLimit      := stackInfo[4];
  enableStkAlarm  := stackInfo[5]; /* BOOL_TRUE | BOOL_FALSE */
  hwMarkAddr      := stackInfo[6];
  enableStkHWMark := stackInfo[7]; /* BOOL_TRUE | BOOL_FALSE */
  validStackBase  := stackInfo[8]; /* BOOL_TRUE | BOOL_FALSE */
  lowToHigh       := stackInfo[9];
  contextDirty    := BOOL_FALSE;
  
  if (not(stackSize) cor (stackSize = 0)
        cor (validStackBase = BOOL_FALSE))
     validStackRange := BOOL_FALSE;
     enableStkAlarm  := BOOL_FALSE;
     enableStkHWMark := BOOL_FALSE;
  else
     if not(stackRange := duplicateAddress(AddressLibClass$Inst, baseAddr))
        ^nil; 
     endif;
     if not(adjustRange(AddressLibClass$Inst, stackRange, stackSize,
           stackSize, MOVE_RANGE_TO_LOW_ADDR)) then 
        destroyAddress(AddressLibClass$Inst, stackRange);
       ^nil;
     endif;
     if (result := isPhysicalAddrInRange(AddressLibClass$Inst, currentSP,
            stackRange)) then
        validStackRange := BOOL_TRUE;
     else
        validStackRange := BOOL_FALSE;
     endif;
     destroyAddress(AddressLibClass$Inst, stackRange);

  endif;
  
  ^GOOD;     
}
!!

/* 3/4/1992 11:45  - PRIVATE
  Get an input value of the specified prompt from user */
Def getNewAlarmValue(self, oldValue | value, str, theDlg, radix temp)
{ 
  /* Create a temp dialog object to get value */
  value := oldValue;
  theDlg := new(InputNumDialog, "Alarm Limit",
    "&Percent of Size (1 - 100%): ", asString(oldValue), HE_DLGD_STACKPRE);
  loop
  while true
  begin 
    if (runModal(theDlg, DLG_INPUTNUM, self) = IDCANCEL) then 
      ^nil;/* User pressed cancel */   
    endif;
    /* Check if it's a valid string and convert to number of radix */
    if (str := inputVal(theDlg)) cand 
       (value := string2int(str)) then
      ^value;
    endif;
    /* Error */
    displayFormattedError(ErrorTextLibClass$Inst, 
       ER_INVALID_INPUT, FORCE_POPUP, nil, nil, nil);
  endLoop;
  ^nil;
}
!!

/* 7/14/1993 14:09 - PRIVATE
  Editing stack base and size
*/
Def cmdStkArea(self, msg | theDlg, result, baseDesc)
{ 
  /* Create the dialog */
  if not(baseDesc := duplicateAddress(AddressLibClass$Inst, baseAddr)) then
    ^nil;
  endif;
  if not(theDlg := new(StkAreaDialog, baseDesc, stackSize)) cor
     (runModal(theDlg, DLG_STACK_AREA, self) = IDCANCEL) 
    destroyAddress(AddressLibClass$Inst, baseDesc); 
    ^nil;
  endif;
  /* Get the new values, set it to the baseDesc and set context values */
  if not(result := getValues(theDlg)) then 
     /* NOTES: result = #(baseAddr, size) */ 
    destroyAddress(AddressLibClass$Inst, baseDesc); 
    ^nil;
  endif;
  /* Set the new base and size to stackSession - server consumes baseDesc */
  setStackBaseAndSize(stackSession, result[0], result[1]);
  /* update all instance variables */
  getAllStackInfo(self);
  updateStack(self);
  invalidate(self);
  ^GOOD;               
}
!!

/* 7/20/1992 14:27 - PRIVATE 
  Retrieve stack information from server as events notify.
*/
Def eventStackInfo(self, msg)
{ 
  /* Save the stack base address descriptor and Size */
  getAllStackInfo(self);
  updateStack(self);
  ^GOOD;
}

!!

/* 4/27/1992 12:04 - PRIVATE
  Respond to enable/disable for Stack Alarm limit
*/
Def changeStkAlarmStates(self | result)
{ 
  /* Check menu item according to its state  */
  if (enableStkAlarm = BOOL_TRUE) then
    if (disableAlarmLimit(stackSession)) then
      unCheckMenuItem(self, STK_ENABLE_ALARM);
      enableStkAlarm := BOOL_FALSE;
    endif;     
  else
    if enableAlarmLimit(stackSession) then
      destroy(stackSession, alarmLimit); 
      alarmLimit := nil;  
      if (result := getAlarmLimit(stackSession)) then
        alarmLimit := result[1];
        checkMenuItem(self, STK_ENABLE_ALARM);
        enableStkAlarm := BOOL_TRUE;
      endif;  
    endif;      
  endif;
  ^GOOD;
}
!!

/* 3/4/1992 11:16 - PRIVATE
  Responds to Editing alarm percent. 
*/
Def cmdStkAlarm(self, msg | newValue, result)
{
  if (newValue := getNewAlarmValue(self, alarmPercent)) then
    if not(between(newValue, 1, 100)) then
      displayFormattedError(ErrorTextLibClass$Inst, 
          ER_STACK_ALARM_RANGE, FORCE_POPUP, nil, nil, nil);
      ^nil;
    endif;
    if (setAlarmLimitPercent(stackSession, newValue) = GOOD) then
      /* Get the new alarm limit */
      destroy(stackSession, alarmLimit);
      alarmLimit := nil;
      if (result := getAlarmLimit(stackSession)) then
        alarmLimit := result[1];
        alarmPercent := newValue;
        if (enableStkAlarm = BOOL_FALSE) cand
            (validStackRange = BOOL_TRUE) cand
            (enableAlarmLimit(stackSession) = GOOD) then
          checkMenuItem(menu, STK_ENABLE_ALARM);
          enableStkAlarm := BOOL_TRUE;
        endif;  
        invalidate(self); /* repaint itself */  
      endif;            
    else
       unCheckMenuItem(menu, STK_ENABLE_ALARM);  
    endif;  
  endif;
  ^GOOD;
}
 
!!

/* 4/27/1992 12:04 - PRIVATE
  Respond to enable/disable for Stack HighWatermark limit
*/
Def changeStkHWMStates(self | result)
{ 
  /* Check menu item according to its state  */
  if (enableStkHWMark = BOOL_TRUE) then 
    if (disableHighWaterMark(stackSession)) then
      unCheckMenuItem(self, STK_ENABLE_HWMARK);
      enableStkHWMark := BOOL_FALSE;
    endif; 
  else
    if enableHighWaterMark(stackSession) then
      destroy(stackSession, hwMarkAddr);
      if (hwMarkAddr := getHighWaterMark(stackSession)) then
        checkMenuItem(self, STK_ENABLE_HWMARK);
        enableStkHWMark := BOOL_TRUE;
      endif;  
    endif;      
  endif;  
  ^GOOD;  
}
!!

/* 10/19/1992 16:32 - PRIVATE
  Save stack options to the ProfileControl object.
*/
Def saveSelf(self | appName)
{ 
  appName := "StackInfo";
  if not(stkFrameLB) then
    ^nil;
  endif;
  saveProfileEntry(TheProfileInfoObj, appName, "ViewStackAddr", 
            asString(asCBoolean(TheProfileInfoObj, (getViewOptions(stkFrameLB) bitAnd 1) = 1)) );
  saveProfileEntry(TheProfileInfoObj, appName, "ViewCodeAddr", 
            asString(asCBoolean(TheProfileInfoObj, (getViewOptions(stkFrameLB) bitAnd 2) = 2)) );
  ^GOOD;
}
!!

/* 3/4/1992 11:10 - PRIVATE
  Alarm user for stack overflow the alarm limit occured 
 */
Def alarmUser(self)
{ 
  beep(); 
  displayFormattedError(ErrorTextLibClass$Inst, 
     ER_STACK_ALARM_LIM, FORCE_POPUP, nil, nil, nil);
  ^GOOD;  
}
!!

/* 3/14/1992 15:26 - PRIVATE 
  Adjust the stack percent value if overflow/underflow 
  NOTES: Adjust overflowing/underflowing percent.

       *    OVERFLOW        
      --- 100%
       #
       #
      --- 0%
       *    UNDERFLOW
*/
Def checkStackOverLimit(self, stkPercent, spUsage)
{
  /* Check for underflow */  
  if negative(stkPercent) then 
    if spUsage then 
      stkErrState := 2; /* SP mark */
      ^100.0;
    endif;
    ^1; 
  endif;

  /* Check for Overflow */
  if stkPercent > 100 then
    if spUsage then stkErrState := 3; endif;
    ^100.0;
  endif;
  ^stkPercent; /* Normal */
}  
!!

/* 3/2/1992 16:10 */
Def close(self)
{ 
  saveSelf(self); /* Save options to PwrViews.ini file */  
  removeWindowMenu(self); /* Unhook with globals Windows Menu */
  closeAllVars(stkVarBrowser); 
  close(stkVarBrowser); /* Close the VarBrowser instance */
  closeEvents(EvNoteLibClass$Inst, eventDescSet); 
  /* Destroy the addrDesc */
  destroy(stackSession, currentSP);
  destroy(stackSession, baseAddr);
  destroy(stackSession, alarmLimit);
  destroy(stackSession, hwMarkAddr); 
  closeSession(stackSession); 
  stackSession := nil;
  TheStackPresenter := nil;  /* kill global reference */
  close(self:ancestor);
}


!!

/* 3/4/1992 13:24 - PRIVATE
  Provide Help dispatching for the Stack Presenter.
*/
Def cmdHelp(self, item)
{ 
  select
    case item = INDEX_HELP
      is contents(TheHelpObject, hWnd(self));
    endCase
    case item = USING_HELP
      is help(TheHelpObject, hWnd(self));
    endCase
    case item = ABOUT_HELP
      is contextNumber(TheHelpObject, hWnd(self), HELP_ENTRY_STACK);
    endCase    
  endSelect;
}


!!

/* 3/4/1992 11:16 - STK_EXIT Event */
Def cmdStkExit(self, msg)
{ 
 ^close(self);
}
 
!!

/* 3/4/1992 12:35 - STK_ENABLE_HWMARK, STK_ENABLE_ALARM commands */
Def cmdStkIndicator(self, indicator | state)
{ 
  if indicator = STK_ENABLE_HWMARK then
    /* Change the HWMark with the invert state */
    changeStkHWMStates(self);
  else
    if not(alarmPercent) then
      displayFormattedError(ErrorTextLibClass$Inst, 
          ER_NO_STACK_LIMIT, FORCE_POPUP, nil, nil, nil);
      ^nil;
    endif;
    /* Change the Alarm Limit with the invert state */
    changeStkAlarmStates(self);
  endif;
  setSelStkFrame(stkFrameLB, currentFrame);
  invalidate(self);
  ^GOOD; 
}
!!

/* 7/24/1992 9:20 - MENU
  Show the source of the current selected stackframe.
*/
Def cmdStkInspect(self, wp)
{ 
  /* display the source of the current stack frame */
  if (currentFrame := getSelStkFrame(stkFrameLB)) then
    showStackFrameVar(self);
    invalidate(stkVarBrowser);
    displaySource(self, currentFrame);
  endif;  
}
!!

/* 3/4/1992 11:16 - STK_PRINT Event */
Def cmdStkPrint(self, msg)
{ 
/* 
  displayFormattedError(ErrorTextLibClass$Inst, 
     ER_NO_PRINT_STACK, FORCE_POPUP, nil, nil, nil);
*/  
^GOOD;
}
 
!!

/* 3/10/1992 11:41 - PRIVATE test */
Def cmdStkRefresh(self, msg)
{ 
  raStatus := getRunAccess(HLBrkRootLibClass$Inst);
  if (raStatus = BOOL_TRUE) then
    emStatus := prim_getEmulationStatus(requireWithPath(HLBrkRootLib,"bkroot.dll"));
    if (emStatus = HLB_EMU_RUNNING) then
      showWaitCurs();
      halt(HLBreakLibClass$Inst);
      go(HLBreakLibClass$Inst);  
      showOldCurs();             
    endif;
  /* Eric 4/9/98 for ID#266 */
  else
    if (getEmulationStatus(requireWithPath(HLBrkRootLib,"bkroot.dll")) 
                         <> HLB_EMU_HALTED) then 
      displayFormattedError(ErrorTextLibClass$Inst, 
         ER_BK_EMULATOR_MUST_BE_HALTED, FORCE_POPUP, nil, nil, nil);
      ^nil;
    endif;
  /* Eof Eric 4/9/98 */
  endif;

  /* Update stack session and stack value */
  getAllStackInfo(self);
  updateStack(self);
  invalidate(self);
}
!!

/* 3/14/1992 18:42 - PRIVATE
  Variable Browser command dispatcher.
*/
Def cmdVarBrowser(self, action | state)
{ 
  select
    case action = VP_MENU_FIND_VAR
      findVar(stkVarBrowser, action);
    endCase
    case action = VP_MENU_VIEW_EXPAND
      if (state := expandGlobal?(stkVarBrowser))
      then
        setExpandGlobal?(stkVarBrowser, nil);
        unCheckMenuItem(menu, action);
      else
        setExpandGlobal?(stkVarBrowser, true);
        checkMenuItem(menu, action);
      endif;    
    endCase
    case action = VP_MENU_HISTORY_SORT
      setSortType(stkVarBrowser, #history);
      checkMenuItem(menu, action);
      unCheckMenuItem(menu, VP_MENU_VNAME_SORT);
    endCase
    case action = VP_MENU_VNAME_SORT
      setSortType(stkVarBrowser, #name);
      checkMenuItem(menu, action);
      unCheckMenuItem(menu, VP_MENU_HISTORY_SORT);
    endCase
    case action = VP_MENU_EDIT
      menuEdit(stkVarBrowser, action);
    endCase
    case action = VP_MENU_SHOW
      menuShow(stkVarBrowser, action);
    endCase
    case action = VP_MENU_DELETE
      menuDelete(stkVarBrowser, action);
    endCase
    case action = VP_MENU_LIST_DELETE
      menuDeleteList(stkVarBrowser, action);
    endCase
  endSelect;
  ^GOOD;
}
!!

/* 3/4/1992 13:22 - STK_VIEW_STKADDR, STK_VIEW_CODEADDR and
  STK_VIEW_PARAMS Events 
*/
Def cmdViewOptions(self, option | viewOptions)
{ 
  /* Get the options and check menu correctly */ 
  viewOptions := getViewOptions(stkFrameLB);
  select
    case option = STK_VIEW_STKADDR 
      if ((viewOptions bitAnd 1) = 1) then
        unCheckMenuItem(self, option);
        setViewOptions(stkFrameLB, viewOptions bitAnd 2); /* clear */
      else
        checkMenuItem(self, option); 
        setViewOptions(stkFrameLB, viewOptions bitOr 1);  /* set */
      endif;
    endCase
    case option = STK_VIEW_CODEADDR
      if ((viewOptions bitAnd 2) = 2) then
        unCheckMenuItem(self, option);
        setViewOptions(stkFrameLB, viewOptions bitAnd 1); /* clear */
      else
        checkMenuItem(self, option);
        setViewOptions(stkFrameLB, viewOptions bitOr 2);  /* set */
      endif;
    endCase
  endSelect;
  /* Update the Stack Frame list display to reflect new changes */
  invalidateStackFrames(stkFrameLB);
  invalidate(self);
  setSelStkFrame(stkFrameLB, currentFrame);
  ^GOOD;
}!!

/* Respond to the menu events.
  The wp argument gives the selected menu ID.
  Get a message symbol from the menu object. 
  NOTES: Nghia - 10/12/93
  Clear ESC key before processing command.
*/
Def command(self, wp, lp | msg)
{ 
  /* Perform the menu action */  
  if msg := action(menu, wp) then
    /* Clear ESC key */
    if (TaskLibClass$Inst)
      checkAbort(TaskLibClass$Inst);
    endif;
    ^perform(self, wp, msg)
  endif;
  
  /* Process child control actions */
  if msg := at(childActions, wp) then
    /* Clear ESC key */
    if (TaskLibClass$Inst)
      checkAbort(TaskLibClass$Inst);
    endif;
    ^perform(self, wp, lp, msg);
  endif;          
 
  /* Process Windows menu commands */
  if not(doAction(PreLauncher, wp)) then
    command(self:ancestor, lp, wp);
  endif; 
}!!

/* 3/18/1992 8:52 - PUBLIC
  return the current selected stack frame.
*/
Def currentFrame(self)
{ 
  ^currentFrame;
}
!!

/* 5/8/1992 10:47 - PRIVATE 
  Disable all specified menu items.
  EX: disableCommands(self, #(MENU_ID, ...));
*/
Def disableCommands(self, menuCmdSet)
{ 
  if menuCmdSet then
    do(menuCmdSet,
      {using(item)
        grayMenuItem(menu, item);
      });
  endif; 
}
!!

/* 3/11/1992 21:10 - PRIVATE 
  Display the source of the current selected stackframe.
*/
Def displaySource(self, frameNum | result, lineResult, moduleInfo, symName)
{ 
  /* NOTES: result = #(symDesc, moduleDesc, addrDesc) */
  if not(result := getFrameInfo(stackSession, frameNum)) then
    ^nil;
  endif;  
  symName := getSymbolName(SymbolLibClass$Inst, result[0]);
  showWaitCurs();
  if not(TheSourcePresenter) then
     TheSourcePresenter := open(SourcePresenter);
  endif; 
  /* 
  *** Show the text of funtion @ address - 
  *** Source Presenter consume the addrDesc - set symbol name to be hilighted.
  */
  showDataObjFromAddress(TheSourcePresenter, result[2], symName);
  show(TheSourcePresenter, SW_NORMAL);
  bringToTop(TheSourcePresenter);
  showOldCurs();
}
!!

/* 3/13/1992 8:56 - PRIVATE 
  Display the stack label and the percentage use of stack.
*/
Def displayStkLabel(self | x, y, theText, dpPos)
{ 
   /* clear the stack meter title */
  clearText(stkMeterTitle);
  theText := format("%#4.5lf",stkPercentUsage);
  if (size(theText) >= 5) cand 
    (dpPos := upTo(theText, ".",0)) then  
    theText := subString(theText, 0, dpPos)+"."+subString(theText,dpPos+1, dpPos+2);
  endif;
  if (validStackRange = BOOL_TRUE)
     drawTitleCenter(stkMeterTitle, theText+"%"); 
  endif;

  drawTitle(stkFrameLBTitle, stackLabel(stkFrameLB));
    
  drawTitle(stkVarBrowserTitle, "Parameters & Local Variables");
}
!!

/* 3/12/1992 1:12 - PRIVATE
  Draw the current alarm limit with respect to the
  stack meter.
 */
Def drawAlarmLimit(self, hDC | x, y,  x1, y1, hBrush)
{
  /* Calculate the position of stack pointer */
  if not(alarmLimit) cor
     /* find position within the Stack Meter */  
     not(y := findPosition(self, alarmLimit, nil)) then 
    ^nil; /* No alarm limit */
  endif;
  y  := y - 1;       
  x  := left(stkMeter);
  x1 := right(stkMeter) + mWidth;
  y1 := y + 2;
  if (hBrush := Call CreateSolidBrush(RGB_PURE_RED)) then
    drawColorBar(self,rect(x+1, y, x1-2, y1), hBrush, hDC);
  endif;  
}
!!

/* 3/15/1992 14:44 - PRIVATE
  Draw a color bar
*/
Def drawColorBar(self, theRect, newBrush, hDC | oldBrush)
{  
  oldBrush := Call SelectObject(hDC, newBrush);
  fill(theRect, newBrush, hDC);
  Call SelectObject(hDC, oldBrush);
  Call DeleteObject(newBrush);
 }
!!

/* 3/12/1992 1:12 - PRIVATE
  Draw the current stack pointer with respect to the
  stack meter.
 */
Def drawCurrStk(self, hDC | x, y,  x1, y1, spValue, hBrush)
{
  stkErrState := 1;   
  /* Calculate the position of the current stack pointer */
  if not(currentSP) cor
     /* find position within the Stack Meter - flag #true to save percent value */
     not(y := findPosition(self, currentSP, #true)) then
    ^nil;
  endif;   
  x  := left(stkMeter);
  x1 := right(stkMeter);
  y1 := bottom(stkMeter);
  /* Select color for stack bar */
  select
    case stkErrState = 3 /* Overflow - Magenta */
      is hBrush := Call CreateSolidBrush(RGB_PURE_RED bitOr RGB_PURE_BLUE);
    endCase
    case stkErrState = 2 /* Underflow - Yellow */
      is hBrush := Call CreateSolidBrush(RGB_PURE_RED bitOr RGB_PURE_GREEN);
    endCase
    case stkErrState = 1 /* Normal - Navy Blue */
      is hBrush := Call CreateSolidBrush(RGB_PURE_BLUE);
    endCase  
  endSelect;
  /* Draw the color bar and callee destroy the new brush */   
  drawColorBar(self, rect(x+1, y, x1-1, y1-1), hBrush, hDC);
}!!

/* 3/11/1992 9:46 - PRIVATE
  Draw the current highwater mark of the stack.
*/
Def drawHWMark(self, hDC | hwMark, hwPos, x, hBrush, hRgn)
{
  /* Calculate the position of HWMark */
  if not(hwMarkAddr) cor
     not(hwPos := findPosition(self, hwMarkAddr, nil)) then
    ^nil;
  endif;
  x := right(stkMeter)+mWidth-10;
  /* Make the mark */
  hwMark := new(WinPolygon, makePolygon(self, x, hwPos)); 
  /* Draw Arrow */      
  draw(hwMark, hDC);  
}
!!

/* 3/10/1992 12:36 - PRIVATE
  Draw meter mark for the stack meter.
*/
Def drawMeterMark(self, hDC | x, y,  x1, y1, stepSize, lastMark, hBrush)
{ 
  x := right(stkMeter)-1;
  y := top(stkMeter);
  x1 := x+mWidth;
  y1 := y+mHeight;
  hBrush := stock(LTGRAY_BRUSH);
  draw(rect(x, y , x1, y1), hDC);
  fill(rect(x+1, y+1, x1-1, y1-1), hBrush, hDC);
  stepSize := asInt(mHeight/10);
  lastMark := y1 - stepSize;
  /* Go over the 10 interval of y to y1 and draw the mark */
  do(overBy(y, lastMark, stepSize),
    {using(step)
      /* do not need to draw the last mark */
      line(point(x1 - 8, step), point(x1, step), hDC);
    });  
}


!!

/* 5/8/1992 10:47 - PRIVATE 
  Enable all specified menu items and the corresponding child objects.
  EX: enableCommands(self, #(MENU_ID, ...));
*/
Def enableCommands(self, menuCmdSet)
{ 
  if menuCmdSet then
    do(menuCmdSet,
      {using(item)
        enableMenuItem(menu, item);
      });
  endif; 
}
!!

/* PRIVATE - Used by VarBrowser */
Def enableField(self, fieldID)
{
  if fieldEnabled
  then grayMenuItem( menu, fieldEnabled ) ;
  endif ;

  if fieldID
  then enableMenuItem( menu, fieldID ) ;
  endif ;
  fieldEnabled := fieldID ;
  drawMenu( self ) ;
}
!!

/* 9/8/1992 11:00 - PRIVATE
  Respond to event stack alarm limit.  
*/
Def eventStkAlarmLimit(self, event | result)
{
  select
    case (event = EVENT_STK_ALARM_OVER_LIMIT)
      alarmUser(self);
    endCase
    case (event = EVENT_STK_ALARM_CHANGE)
      if (result := getAlarmLimit(stackSession)) then
        /* NOTES: result = #(alarmEnabled?, alarmLimit, alarmPercent) */
        enableStkAlarm := result[0];
        destroy(stackSession, alarmLimit);
        alarmLimit := result[1];
        alarmPercent := result[2];
      endif; 
    endCase  
    case (event = EVENT_STK_ALARM_ENABLED)
      enableStkAlarm := BOOL_TRUE;
      if (result := getAlarmLimit(stackSession)) then
        /* NOTES: result = #(alarmEnabled?, alarmLimit, alarmPercent) */
        enableStkAlarm := result[0];
        destroy(stackSession, alarmLimit);
        alarmLimit := result[1];
        alarmPercent := result[2];
      endif; 
      checkMenuItem(self, STK_ENABLE_ALARM);
    endCase     
    case (event = EVENT_STK_ALARM_DISABLED)
      enableStkAlarm := BOOL_FALSE;
      unCheckMenuItem(self, STK_ENABLE_ALARM);
    endCase 
  endSelect;  
}
!!

/* 9/8/1992 11:00 - PRIVATE
  Respond to event stack changed - from the CLI.  
*/
Def eventStkChanged(self, msg)
{ 
  getAllStackInfo(self);
  updateStack(self); 
}
!!

/* 9/8/1992 11:00 - PRIVATE
  Respond to event stack HighWaterMark state.  
*/
Def eventStkHWM(self, event)
{
  select 
    case (event = EVENT_STK_HWM_ENABLED)
      /* User enable HWM from the SHELL, if hwMarkAddr is valid then ok */ 
      destroy(stackSession, hwMarkAddr);
      if (hwMarkAddr := getHighWaterMark(stackSession)) then 
        enableStkHWMark := BOOL_TRUE;
        checkMenuItem(self, STK_ENABLE_HWMARK);
      endif;
    endCase
    case (event = EVENT_STK_HWM_DISABLED)
      enableStkHWMark := BOOL_FALSE;
      unCheckMenuItem(self, STK_ENABLE_HWMARK);     
    endCase
  endSelect;
}
!!

/* 3/11/1992 9:46 - PRIVATE
  Calculate the stack mark value with  respect to the
  stack meter height.
  NOTES: 
    findValue - address Descriptor
    spUsage - flag to save current stack usage.
*/
Def findPosition(self, findValue, spUsage | stkPercent, stkPercentAdj,
                                            stkPos, dir, tmpSize, tmpDesc,
                                            physDesc1, physDesc2, result)
{
  /* if not a valid stack range don't bother to calculate since we 
  **  are not going to display a stack usage
  */
  /* Calculate the position of findValue with respect to mheight */
  stkPercent := 0.0;
  if (validStackRange  <> BOOL_TRUE)
     ^nil;
  endif;
  if not(lowToHigh) then
     displayFormattedError(ErrorTextLibClass$Inst, 
        ER_CANT_GET_STACK_DIR, CHECK_MODE, nil, nil, nil);
    ^nil;
  endif;

  if (stackSize > 0) then
    tmpSize := asReal(stackSize);
    if (lowToHigh = 0) then /* high to low (i.e. base > findValue) */
      /* compare addresses  to check for different segments,
      ** if unable to compare then we must convert them to physical
      */
      tmpDesc := duplicateAddress(AddressLibClass$Inst, baseAddr);
      if (result := compareAddressesNoError(AddressLibClass$Inst,
             findValue,tmpDesc)) then
         subtractAddresses(AddressLibClass$Inst, tmpDesc, findValue);
      else
         /* convert address to physical and then subtract */
         physDesc2 := duplicateAddress(AddressLibClass$Inst, findValue);
         physDesc1 := convertAddressToType(AddressLibClass$Inst,
               tmpDesc,ADDR_PHYSICAL);
         physDesc2 := convertAddressToType(AddressLibClass$Inst,
               physDesc2,ADDR_PHYSICAL);
         subtractAddresses(AddressLibClass$Inst, physDesc1, physDesc2);
         destroyAddress(AddressLibClass$Inst, physDesc2);
      endif;
    else  /* low to high (i.e. findValue > base) */
      tmpDesc := duplicateAddress(AddressLibClass$Inst, findValue);
      if (result := compareAddressesNoError(AddressLibClass$Inst,
             findValue,tmpDesc)) then
         subtractAddresses(AddressLibClass$Inst,tmpDesc,baseAddr);
      else
         /* convert address to physical and then subtract */
         physDesc2 := duplicateAddress(AddressLibClass$Inst, baseAddr);
         physDesc1 := convertAddressToType(AddressLibClass$Inst,
               tmpDesc,ADDR_PHYSICAL);
         physDesc2 := convertAddressToType(AddressLibClass$Inst,
               physDesc2,ADDR_PHYSICAL);
         subtractAddresses(AddressLibClass$Inst, physDesc1, physDesc2);
         destroyAddress(AddressLibClass$Inst, physDesc2);
      endif;  
    endif;
    /* NOTES: getOffset() call is OK */
    stkPercent := asReal((asReal(getOffset(AddressLibClass$Inst, tmpDesc)) / tmpSize) * 100.0);
    /* Get rid of the unused descriptor */
    destroyAddress(AddressLibClass$Inst, tmpDesc);
  else
    displayFormattedError(ErrorTextLibClass$Inst, 
       ER_STACK_SIZE_0, CHECK_MODE, nil, nil, nil);
    ^nil;
  endif;
  /* Check for Overflow or Underflow condition to set color code */
  stkPercentAdj := asReal(checkStackOverLimit(self, stkPercent, spUsage));
  /* Convert the percent adjust to screen position - from Meter height */
  stkPos := asInt((stkPercentAdj * mHeight) / 100.0);    

  if spUsage then /* only save  the percentage of the SP */
    stkPercentUsage := stkPercent;
  endif;  
  ^abs(bottom(stkMeter) - stkPos); 
}
!!

/* 3/2/1992 16:19 - PUBLIC 
  Initialize the Stack Presenter by init its menu, child objects, window and
  register events 
*/
Def init(self | tmpSP)
{ 
  requireWithPath(StackLib, "stkservr.dll"); 
  requireWithPath(StackLib, "proc.dll"); 
  showWaitCurs();
  /*Eric 4/9/98 for ID#266*/
  raStatus := getRunAccess(HLBrkRootLibClass$Inst);
  if (raStatus = BOOL_TRUE) then
    emStatus := prim_getEmulationStatus(requireWithPath(HLBrkRootLib,"bkroot.dll"));
    if (emStatus = HLB_EMU_RUNNING) then
      showWaitCurs();
      halt(HLBreakLibClass$Inst);
      go(HLBreakLibClass$Inst);  
      showOldCurs();             
    endif;
  else
     if (getEmulationStatus(requireWithPath(HLBrkRootLib,"bkroot.dll")) 
                         <> HLB_EMU_HALTED) then 
      displayFormattedError(ErrorTextLibClass$Inst, 
         ER_BK_EMULATOR_MUST_BE_HALTED, FORCE_POPUP, nil, nil, nil);
      ^nil;
    endif;     
  endif;
  /*Eof Eric 4/9/98*/
  if not(openSession(self)) then 
    ^nil;
  endif;
  initTextMetrics(self);
  if not(tmpSP := getStackPointer(CpuLibClass$Inst)) cor
    not(addrWidth := maxOutputAddrDigits(AddressLibClass$Inst, tmpSP)) then 
    addrWidth := 15; /* Default max number of digits in address */
  endif;
  if (tmpSP) destroyAddress(AddressLibClass$Inst, tmpSP); endif;
  
  /* min width is 22 chars + 2*addrWidth + 2 * 10 for the borders */
  minWindowWidth := (((2*addrWidth) + 22) * tmWidth) + 20;
  minWindowHeight := 15 * tmHeight;
  initMainMenu(self);
  if not(initEvents(self)) then
     displayFormattedError(ErrorTextLibClass$Inst, 
       ER_STACK_CRE, FORCE_POPUP, nil, nil, nil);
     ^nil;
  endif;
  if not(initChildObj(self)) then
     displayFormattedError(ErrorTextLibClass$Inst, 
       ER_STACK_NO_CHILD, FORCE_POPUP, nil, nil, nil);
     ^nil;  /* Error create child objects */
  endif;  
  initActions(self);
  /* Init the stack session and return its handle */        
  if not(initStackSession(self)) then
     /* Failed to init so close it */
     close(self);
     displayFormattedError(ErrorTextLibClass$Inst, 
        ER_STACK_NO_SESSION, FORCE_POPUP, nil, nil, nil);
     ^nil; /* handle is nil */
  endif;  

  registerF1Help(CLIULibraryClass$Inst, HI_ENTRY_STACK,
    getHWnd(self), HELP_ENTRY_STACK);
  showOldCurs();
}
!!

/* 3/9/1992 13:33 - PRIVATE 
  Init all child controls actions for Stack List and Var Browser.
*/
Def initActions(self)
{ 
  childActions := new(Dictionary, 2);
  add(childActions, STK_FRAMELB, #selectStackFrame);
  add(childActions, STK_VARBROWSER, #varBrowserAction);
}
!!

/* 3/6/1992 10:18 - PRIVATE
  Create child control objects of the Stack Presenter
*/
Def initChildObj(self)
{ 
  if not(stkFrameLB := new(StackListBox, STK_FRAMELB, self)) then
    ^nil;
  endif;
  
  /* Create stkVarBrowser object */
  if not(stkVarBrowser := open(VarBrowser, rect(10, 10, 10, 10), self, 
    #true, WS_BORDER bitOr WS_HSCROLL)) then
    ^nil;
  endif;
  setContID(stkVarBrowser, STK_VARBROWSER); 
  /* Create StkMeter and HWM objects */
  stkMeter := new(Rect); 
  stkHWMark := new(Array, 7);
    
  stkFrameLBTitle :=
    newStyle(TitleWindow, self, nil, "child", nil, 0, WS_CHILD);
  stkMeterTitle :=
    newStyle(TitleWindow, self, nil, "child", nil, 0, WS_CHILD);
  stkVarBrowserTitle :=
    newStyle(TitleWindow, self, nil, "child", nil, 0, WS_CHILD);

  ^GOOD;
}
!!

/* 3/4/1992 11:01 - PRIVATE 
  Initialize Stack's Events 
*/
Def initEvents(self | events)
{

  eventRespSet := new(Dictionary, 6);

  /* dynamic to pick up current values */
  events := tuple(
                  EVENT_STK_BASE_SIZE_AVAIL,
                  EVENT_STK_HALTED,  
                  EVENT_STK_STACK_CHANGED,
                  EVENT_STK_ALARM_OVER_LIMIT,
                  EVENT_STK_ALARM_CHANGE,
                  EVENT_STK_ALARM_ENABLED,
                  EVENT_STK_ALARM_DISABLED,
                  EVENT_STK_HWM_ENABLED,
                  EVENT_STK_HWM_DISABLED
                 );

   add(eventRespSet, EVENT_STK_BASE_SIZE_AVAIL,   #eventStackInfo);
   add(eventRespSet, EVENT_STK_HALTED,            #eventStkChanged);                   
   add(eventRespSet, EVENT_STK_STACK_CHANGED,     #eventStkChanged);   
   add(eventRespSet, EVENT_STK_ALARM_OVER_LIMIT,  #eventStkAlarmLimit);
   add(eventRespSet, EVENT_STK_ALARM_CHANGE,      #eventStkAlarmLimit);
   add(eventRespSet, EVENT_STK_ALARM_ENABLED,     #eventStkAlarmLimit);
   add(eventRespSet, EVENT_STK_ALARM_DISABLED,    #eventStkAlarmLimit);
   add(eventRespSet, EVENT_STK_HWM_ENABLED,       #eventStkHWM);   
   add(eventRespSet, EVENT_STK_HWM_DISABLED,      #eventStkHWM);

  /* register with Event Notification library */
  requireWithPath( EvNoteLib, "enlib.dll");                
  ^(eventDescSet := registerEvents(EvNoteLibClass$Inst, hWnd(self), events));                
}
 
!!

/* 3/4/1992 8:39 - PRIVATE 
 Create and initialize main menu of the Stack Presenter
 Window 
 */
Def initMainMenu(self | subPopup, popup)
{ 
  /* Use menu instance variable to create the main menu */
  menu := create(new(Menu), self);
  
  /* File Menu */
  popup := newPopup(MenuItem, "&File");
  /* Notes: Future enhancement - Nghia
  addItem(popup, new(MenuItem, "&Print...", STK_PRINT, #cmdStkPrint));
  */
  addItem(popup, new(MenuItem, "Re&fresh Display", STK_REFRESH, #cmdStkRefresh));
  addItem(popup, nil);
  addItem(popup, new(MenuItem, "E&xit", STK_EXIT, #cmdStkExit));
  addItem(menu, popup);
  
  /* Edit Menu */
  /* popup := newPopup(MenuItem, "&Edit");
  addItem(menu, popup); */
  
  /* View and Option Menu */
  popup := newPopup(MenuItem, "&Options");
  addItem(popup, new(MenuItem, "&Stack Area...", STK_AREA, #cmdStkArea));
  addItem(popup, new(MenuItem, "Alar&m Limit...", STK_ALARM, #cmdStkAlarm));
  addItem(popup, nil);
  addItem(popup, new(MenuItem, "Include S&tack Address", STK_VIEW_STKADDR, 
    #cmdViewOptions));
  addItem(popup, new(MenuItem, "Include Return &Code Address", STK_VIEW_CODEADDR, 
    #cmdViewOptions));

  /* Notes:  Future Enhancement - Nghia    
  addItem(popup, new(MenuItem, "Include &Parameters", STK_VIEW_PARAMS, 
    #cmdViewOptions));
  */  

  addItem(popup, nil);
/*  addItem(popup, new(MenuItem, "Enable &High-Water Mark", 
    STK_ENABLE_HWMARK, #cmdStkIndicator)); */
  addItem(popup, new(MenuItem, "Enable &Alarm Limit", 
    STK_ENABLE_ALARM, #cmdStkIndicator));
  addItem(popup, nil);
  addItem(popup, new(MenuItem, "&Inspect Source",
    STK_INSPECT_FUNC, #cmdStkInspect));
/*
  addItem(popup, new(MenuItem, "Inspect Ca&ller",
    STK_INSPECT_CALL, #cmdStkInspectCaller));
*/
  addItem(menu, popup);

  /* Append Windows menu item */
  Call AppendMenu(handle(menu), MF_POPUP, windowsMenu(PreLauncher), asciiz("&Windows")); 
  
  /* Help */
  addItem(menu, initMenuHelp(self));
  drawMenu(self);  
}!!

/* 6/22/1992 14:10 - PUBLIC */
Def initMenuHelp(self | aMenu)
{ 
  /* HELP menu */
  aMenu := newPopup(MenuItem, "&Help");
  addItem(aMenu, new(MenuItem, "Help &Index",          
    INDEX_HELP, #cmdHelp));
  
  addItem(aMenu, new(MenuItem, "&Help With Help",          
    USING_HELP, #cmdHelp));
  addItem(aMenu, nil);
  addItem(aMenu, new(MenuItem, "Help With S&tack",
    ABOUT_HELP, #cmdHelp));
  ^aMenu;
}
 
!!

/* 10/20/92 - PRIVATE
   Init Stack options Alarm, HWM, View options.
*/
Def initStackOptions(self | iniValue, appName) {

  /* Set option for Alarm Limit */
  if (enableStkAlarm = BOOL_TRUE) 
    enableAlarmLimit(stackSession);
    checkMenuItem(self, STK_ENABLE_ALARM);
  else
    disableAlarmLimit(stackSession);
    unCheckMenuItem(self, STK_ENABLE_ALARM);      
  endif;  

  /* Set option for HWM */
  if (enableStkHWMark = BOOL_TRUE) then
    enableHighWaterMark(stackSession);
    checkMenuItem(self, STK_ENABLE_HWMARK);  
  else   
    disableHighWaterMark(stackSession);
    unCheckMenuItem(self, STK_ENABLE_HWMARK);    
  endif;  

  appName := "StackInfo";
  /* Set view option default to CODEADDR view */
  if (iniValue := getIniValueNum(TheProfileInfoObj, appName, "ViewStackAddr"))
    cand between(iniValue, 0,1) cand (iniValue = 1) then
    checkMenuItem(self, STK_VIEW_STKADDR);
    setViewOptions(stkFrameLB, (getViewOptions(stkFrameLB) bitOr 1));
  endif;  
  
  if (iniValue := getIniValueNum(TheProfileInfoObj, appName, "ViewCodeAddr"))
    cand between(iniValue,0,1) cand (iniValue = 1) then
    checkMenuItem(self, STK_VIEW_CODEADDR);
    setViewOptions(stkFrameLB, (getViewOptions(stkFrameLB) bitOr 2));
  endif;  
  ^GOOD;
}
!!

/* 3/5/1992 9:46 - PRIVATE 
  Initialize stack presenter with information from server.
*/
Def initStackSession(self | anyFrames)
{
  /* Initialize */
  currentFrame    := nil;  
  warnedUser?     := nil;  
  stkErrState     := 1;
  stkPercentUsage := 0.0; 
  baseAddr        := nil; 
  stackSize       := nil;
  alarmLimit      := nil;
  contextDirty    := BOOL_TRUE; 
  /* Get stackinfo from server */
  if not(getAllStackInfo(self))
    ^nil;
  endif;
  /* Initilize Stack options */
  initStackOptions(self);
  if (not(baseAddr) cor not(stackSize) cor 
         (baseAddr = 0) cor (stackSize = 0) cor
         (validStackRange = BOOL_FALSE))
     disableCommands(self, tuple(STK_ENABLE_ALARM, STK_ENABLE_HWMARK));
  endif;
/*
  disableCommands(self, tuple(STK_INSPECT_FUNC, STK_INSPECT_CALL));
*/
  disableCommands(self, tuple(STK_INSPECT_FUNC));
  showWaitCurs();
  /* Only update the stack list if stack frame is available */
  if (anyFrames := anyFrames(stackSession)) cand
    (anyFrames = BOOL_TRUE) then
    updateStack(self); /* Get Stack frames */
  endif;
  showOldCurs();
  invalidate(self);
  setFocus(stkFrameLB);
}
!!

/* 06/20/92 - PRIVATE
  Initialize text metrics data for this window.  Load the font data 
  into textMetrics, set the text width and height instance variables.
*/
Def initTextMetrics(self | hdc, tm)
{ 
  tm := new(Struct, 32);
  Call GetTextMetrics(hdc := getContext(self), tm);
  Call SelectObject(hdc, Call GetStockObject(SYSTEM_FIXED_FONT));
  tmWidth  := asInt(wordAt(tm, 10));
  tmHeight := asInt(wordAt(tm, 8)) + asInt(wordAt(tm, 0));
  Call SelectObject(hdc, Call GetStockObject(SYSTEM_FONT));
  releaseContext(self, hdc);
}!!

/* 3/11/1992 14:55 - PRIVATE 
  Fill in the HWMark array with points
*/
Def makePolygon(self, x, hwPos)
{
  /* Set position of the HighWaterMark */
  stkHWMark[0] := point(x, hwPos);
  stkHWMark[1] := point(x+6, hwPos-8);
  stkHWMark[2] := point(x+6, hwPos-4);
  stkHWMark[3] := point(x+12,hwPos-4);
  stkHWMark[4] := point(x+12,hwPos+4);
  stkHWMark[5] := point(x+6, hwPos+4);
  stkHWMark[6] := point(x+6, hwPos+8);
 
  ^stkHWMark;
}
!!

/* 3/4/1992 15:41 - PRIVATE
  Open a session of the Stack Server 
  */
Def openSession(self)
{ 
  /* make sure Variable Server dll is loaded */
  if not(requireWithPath(StackLib, "stkservr.dll") and
      requireWithPath(MallocLib, "malloc.dll")) then
    displayFormattedError(ErrorTextLibClass$Inst, 
       ER_NO_LIBS, FORCE_POPUP, "Stack", nil, nil);
    ^nil 
  endif ;
  /* Open Stack Session */
  if not(stackSession := openSession(StackLibClass$Inst, hWnd(self))) then
     displayFormattedError(ErrorTextLibClass$Inst, 
        ER_STACK_NO_SESSION, FORCE_POPUP, nil, nil, nil);
    ^nil 
  endif;
  ^stackSession;
}

!!

/* 3/10/1992 12:03  - PRIVATE
  Paint the stack meter then draw its text labels. 
  Only Paint stackMeter if its height is > a font char.
 */
Def paintStackMeter(self | hDC)
{ 
  hDC := Call BeginPaint(hWnd, paintStruct);
  /* Check if the stack meter value available and the presenter is not iconized */
  if (stkMeter cand (Call IsIconic(getHWnd(self)) = BOOL_FALSE) cand
    (height(stkMeter) > tmHeight)) then
    /* Draw the meter and marks */    
    draw(stkMeter, hDC);
    drawMeterMark(self, hDC);
    /* If there are stack frames then draw the meter */
    if totalFrames(stkFrameLB) > 0 then
      drawCurrStk(self, hDC);
    endif;  
    if ((enableStkAlarm = BOOL_TRUE) cand alarmLimit) then
      drawAlarmLimit(self, hDC);
    endif;
    /*Hera 12/19/96*/
    if ((enableStkHWMark = BOOL_TRUE) cand hwMarkAddr) then
      drawHWMark(self, hDC); 
    else
      if(enableStkHWMark = BOOL_TRUE) then
      changeStkHWMStates(self);
      perform(self, 712, #cmdStkIndicator);
      endif;
    endif;

    /* Always redraw the stack label */
    displayStkLabel(self);
    /* Check for overflow and underflow */
    if not(warnedUser?) then
      select
        case stkErrState = 2  
          beep(); 
          displayFormattedError(ErrorTextLibClass$Inst, 
             ER_STACK_UNDERFLOW, CHECK_MODE, nil, nil, nil);
        endCase
        case stkErrState = 3
          beep(); 
          displayFormattedError(ErrorTextLibClass$Inst, 
             ER_STACK_OVERFLOW, CHECK_MODE, nil, nil, nil);
        endCase
      endSelect;
      warnedUser? := #true; /* Set flag so msgBox only show up once */
    endif;  
  endif;  
  Call EndPaint(hWnd, paintStruct);
  ^GOOD;
}!!

/* Responds to reSize message - Calculate Size of child objects using 
   the parent window size then move them */
Def reSize(self, wp, lp | wUnit hUnit width height
      rectLB rectVar rectSTitle fixup)
{
  /* bail out if too early */
  if not(stkFrameLB)
    ^nil;
  endif;
  
  /* don't resize subwindows if being iconized */
  if (wp = SIZEICONIC)
    ^0;
  endif;
  
  /* maintain minimum size */
  cRect := windowRect(self);
  if cRect
    if (width(cRect) < minWindowWidth)
      fixup := 0;
      setRight(cRect, left(cRect) + minWindowWidth);
    endif;
    if (height(cRect) < minWindowHeight)
      fixup := 0;
      setBottom(cRect, top(cRect) + minWindowHeight);
    endif;
    if fixup
      moveWindow(self);
    endif;
  endif;
  
  /* calculate the width and height of the main window */
  width  := width(clientRect(self));
  height := height(clientRect(self));
  wUnit  := asInt(width/10);
  hUnit  := asInt(height/20);
     
  /* Stack Frame List Box Window */ 
  setCRect(stkFrameLB, rect(10, tmHeight+4, wUnit*8, hUnit*12));
  rectLB := cRect(stkFrameLB);
  
  stkMeter :=
    rect(asInt(wUnit*8.75), top(rectLB), asInt(wUnit*9.25), bottom(rectLB));
  mWidth := width(stkMeter);
  mHeight := height(stkMeter);

  /* add mWidth to right to match right side of meter hash marks */
  setCRect(stkVarBrowser, rect(left(rectLB),
    bottom(rectLB)+tmHeight+4, right(stkMeter) + mWidth, height-8));
  rectVar := cRect(stkVarBrowser);
  
  setCRect(stkFrameLBTitle, rect(left(rectLB), top(rectLB)-tmHeight-2,
    right(rectLB), top(rectLB)-1));

  rectSTitle := cRect(stkFrameLBTitle);

  /* 6/2/94 From Marilyn:
  Center the stkMeterTitle over the stkMeter.  At minimum size the
  entire stkMeter is mWidth*2 which includes the hash mark area.  The
  minimum size for the title is 5 chars (xx.x%).  To make it easier
  lets say 6 chars minumum size for title.   Right(stkMeter) is
  dead center, so center the stkMeterTitle relative to this.
  For some reason there is a difference between the tmWidth of the
  stackPresenter and the tmWidth of the stkMeterTitle. To be correct we
  will use the stkMeterTitle tmWidth.
  */
  setCRect(stkMeterTitle, rect(right(stkMeter)-3*tmWidth(stkMeterTitle),
    top(rectSTitle), right(stkMeter)+3*tmWidth(stkMeterTitle), 
    bottom(rectSTitle)));

  setCRect(stkVarBrowserTitle, rect(left(rectVar), top(rectVar)-tmHeight-2,
    right(rectVar), top(rectVar)-1));

  /* Get new stack info since contextDirty is TRUE */ 
  if (contextDirty = BOOL_TRUE)
    getAllStackInfo(self);
    updateStack(self); 
  endif;
  
  /* Now move them accordingly */
  moveWindow(stkFrameLB);
  moveWindow(stkVarBrowser);
  moveWindow(stkFrameLBTitle);
  moveWindow(stkMeterTitle);
  moveWindow(stkVarBrowserTitle);
  
  invalidate(self);
}!!

/* WINDOWS - Called by VarBrowser */
Def selected(self, fieldType)
{
   ^enableField(self, fieldType) ;
}
!!

/* 3/9/1992 13:25 - PRIVATE
  Responds to the user selecting by select the current stack frame 
 */
Def selectStackFrame(self, wp, lp | newFrame)
{ 
  /* Set the current stack frame if it's selected */
  if high(lp) = LBN_SELCHANGE then
    if (stkFrameLB cand (newFrame := getSelStkFrame(stkFrameLB)) 
          cand (newFrame <> currentFrame)) then
      currentFrame := newFrame;
      showStackFrameVar(self);
      setFocus(stkFrameLB);
    endif;
  endif;
}
!!

/* 3/18/1992 8:44 - PUBLIC 
  Set the current selected stack frame.
*/
Def setCurrentFrame(self, id)
{ 
  ^(currentFrame := id);
}
!!

/* 3/6/1992 11:14 - Actor's Message
 Show the Stack Presenter and its child window */
Def show(self, scrnDisplayMode)
{
  show(self:ancestor, scrnDisplayMode);
  if ((scrnDisplayMode <> SW_MINIMIZE) cand stkFrameLB cand stkVarBrowser) then
    show(stkFrameLB, SW_NORMAL);
    show(stkVarBrowser, SW_NORMAL);
    show(stkFrameLBTitle, SW_NORMAL);
    show(stkMeterTitle, SW_NORMAL);
    show(stkVarBrowserTitle, SW_NORMAL);
  endif;  
}
!!

/* 3/15/1992 18:50 - PRIVATE */
Def showCurrentFrameVars(self)
{
  /*
  **  Notes: VarBrowser does not store old info for now
  **  so the Stack Presenter need to destroy the old var to
  **  display the new ones
  */
 
  if not(isVarLoaded?(stkFrameLB, currentFrame))  cand
     showStackFrameVar(self) then
     /* Variables just loaded, so set its varLoaded flag */
     varLoaded(stkFrameLB, currentFrame);  
  endif;
  
}
!!

/* 3/14/1992 19:13 - PRIVATE
  Display all locals and parameters of the current selected
  stack frame.
*/
Def showStackFrameVar(self | aVarDesc, nextVarDesc, numParam)
{ 
  /* Clear the VarBrowser first */
  closeAllVars(stkVarBrowser);
  
  if not(SymbolLibClass$Inst cand 
    (aVarDesc := getFrameLocals(stackSession, currentFrame))) then
    ^nil;
  endif;
  /* Get the number of  parameters in the selected frame */  
  numParam := aVarDesc[1];
  
  loop while (aVarDesc[0] <> 0) /* NULL_SYMBOL == 0L */
  begin
    if not(addVarSymAndFrameNoScroll(stkVarBrowser, aVarDesc[0], nil, currentFrame)) then
       displayFormattedError(ErrorTextLibClass$Inst, 
          ER_STACK_NO_VAR, CHECK_MODE, nil, nil, nil);
      ^nil;
    endif;   
 
    if (nextVarDesc := getSymbolSibling(SymbolLibClass$Inst, 
      aVarDesc[0]))
    then
      aVarDesc[0] := nextVarDesc[0];
    endif;
  endLoop;
  ^GOOD;
}
!!

/* Display the current title of the Stack session or " " 
   if no name has been specified. */
Def showTitle(self)
{ 
  setText(self,"Stack");
}!!

/* 3/6/1992 12:45 - PUBLIC 
  Should only be used by its child objects to obtain the server session
 */
Def stackSession(self)
{ 
  ^stackSession;
}
!!

/* 3/15/1992 20:42 - PUBLIC */
Def tmHeight(self)
{ 
  ^tmHeight;
}
!!

/* 3/4/1992 11:09 - PRIVATE 
  Update the stack display with new information from the server.
 */
Def updateStack(self)
{ 
  clear(stkFrameLB);
  /* Clear the VarBrowser first */
  closeAllVars(stkVarBrowser);

  /* Load stack frames */
  currentFrame := nil;
  warnedUser? := nil;  /* Reset warning flag to warn user Stack overflow/underflow */
  stkErrState := 1;
  stkPercentUsage := 0.0;
/*
  disableCommands(self, tuple(STK_INSPECT_FUNC, STK_INSPECT_CALL));
*/
  disableCommands(self, tuple(STK_INSPECT_FUNC));
  /* 10/05/94 Marilyn: if the stack range is not valid then disable
  **  all commands that require base and size for the calculation.
  **  This includes hwm, alarm, and percent usage calculation.
  */
  if (validStackRange = BOOL_FALSE)
     disableCommands(self, tuple(STK_ENABLE_ALARM, STK_ENABLE_HWMARK));
  else
     enableCommands(self, tuple(STK_ENABLE_ALARM, STK_ENABLE_HWMARK));
  endif;
  if loadStackFrames(stkFrameLB) then
    invalidateStackFrames(stkFrameLB);
    currentFrame := 0;
    setSelStkFrame(stkFrameLB, currentFrame); /* top frame selected */
/*
    enableCommands(self, tuple(STK_INSPECT_FUNC, STK_INSPECT_CALL));  
*/
    enableCommands(self, tuple(STK_INSPECT_FUNC));  
  endif;
  ^GOOD;
}
!!

/* 3/15/1992 16:16 - PRIVATE
  Respond to actions associated with the variables browser.
*/
Def varBrowserActions(self, action, lp)
{ 
  select
   case action = EDIT_SRCH
    is searchText(stkVarBrowser, nil)
    endCase
   case high(lp) <> 1
    is ^0
    endCase
    case action = EDIT_PRIOR
    is pageUp(stkVarBrowser);
    endCase
    case action = EDIT_NEXT
    is pageDown(stkVarBrowser);
    endCase
    case action = VK_LEFT
    is ^leftArrow(stkVarBrowser);
    endCase
    case action = VK_UP
    is ^upArrow(stkVarBrowser);
    endCase
    case action = VK_RIGHT
    is ^rightArrow(stkVarBrowser);
    endCase
    case action = VK_DOWN
    is ^downArrow(stkVarBrowser);
    endCase
    case action = EDIT_TAB
    is ^rightArrow(stkVarBrowser); /* alias for rightArrow */
    endCase
  endSelect;
  ^0
}
!!

/* 3/4/1992 11:06 - WINDOWS -- sent by event manager (evNoteLib) */
Def WM_EVENT(self, ignored, event)
{
  /* Set flag to update context later if presenter is iconized */
  if ((contextDirty := Call IsIconic(getHWnd(self))) = BOOL_TRUE)
     ^GOOD;
  endif;    
  if event cand at(eventRespSet, event) then    
    perform(self, event, at(eventRespSet, event)); 
  else  
    /* Happened only if Windows went nuts */
    displayFormattedError(ErrorTextLibClass$Inst, 
       ER_UNKNOWN_EVENT, CHECK_MODE, asciiz(asHex(event)), nil, nil);
  endif;
  invalidate(self);  
  ^GOOD;
}
 
!!

/* 7/23/1992 23:24 - WINDOWS
  Initialize Windows dynamic menu.
*/
Def WM_INITMENU(self, wp, lp)
{ 
  ^WM_INITMENU(PreLauncher, wp, lp);
}

!!

/* 3/10/1992 12:03  - MS-Window's message to paint self -- sends a
  paint(self) message then draw its text labels. 
 */
Def WM_PAINT(self, wP, lP)
{ 
  if (contextDirty = BOOL_TRUE) 
    /* getStackInfo() will reset the contextDirty flag */
    getAllStackInfo(self);
    updateStack(self);
  endif; 
  paintStackMeter(self);
  ^0;
}!!
