/* Class: SourcePresenter - SOURCEPR.CLS
   Display source and disassembly text of the current loadfile
   and handle execution controls.
   Support generic data object (file, dasm, and mixed.)
   Handle high level menu and action controls.
   If splitPos is 0, then there is only 1 browser, else 2.

   REQUIRE: PSOURCE.H, SOURCECB.CLS, HISTORYS.CLS, VIRTSMAR.CLS,
   HLBREAKL.CLS, POPUPMENU, LOADMDIA.CLS, DASMSRCB.CLS
*/!!

inherit(Window, #SourcePresenter, #(backwardFile
bkptType /* Permanent or Temporary */
browser /* a CachingBrowser */
browser2 /* second window or nil */
childActions /* actions associate with child controls */
childDict /* child controls dictionary */
eventDescSet /* contains event descriptors */
eventRespSet /* events respond dictionary */
execGran  /* Line, Statement, Asm */
goType /* CallReturn or Return only */
maxDepth /* historyDepth */
tabWidth /* tab expansion width */
popWindow /* a  popupMenu or nil */
prevCRect
searchText /* search text string or nil */
selectedWin /* browser or browser2 */
splitArea /* "split area" window */
splitPos /* yPos between windows */
tmHeight
tmWidth
viewMode   /* Source or Mixed */
stepCount  /* Number of Step */
displayLineNum /* true or nil - turn ON/OFF displaying line number */
animating /* true or nil */
sourceDelimiter /* CR_LF or LF only */
minButtonWidth /* # of pixel for button text */), 2, nil)!!

setClassVars(SourcePresenter, #($keysDict))!!

now(class(SourcePresenter))!!

/* start presenter with specific position or a default position */
Def openWithPosAndState(self, sizeRect, showVal | x, y)
{
  /* Provide a reasonable default size for the Source window */
  if not(sizeRect) then
    /* Set its location and size */
    x := x(screenSize()); y := y(screenSize());
    if (sizeRect := new(Rect)) then
      if x > 640 then /* Screen resolution is greater than 640x480 */
        x := asInt((x - 650) / 2);  /* Width of window */
        y := asInt((y - 500) / 2);  /* Height of window */
        init(sizeRect, x, y, x+650, y+500);
      else
        x := asInt((x - 600) / 2);  /* Width of window */
        y := asInt((y - 400) / 2);  /* Height of window */
        init(sizeRect, x, y, x+600, y+400);
      endif;
    endif;    
  endif;

  /* Open the source window */
  if TheSourcePresenter := newStyle(SourcePresenter, nil, nil, "Source Debugger ",
                   sizeRect, nil, WS_OVERLAPPEDWINDOW)   then
    show(TheSourcePresenter, showVal);
  else
    ^(TheSourcePresenter := nil);
  endif;
  ^TheSourcePresenter;

}
!!

/* 6/2/1992 9:37 - PUBLIC
   Open a new source debugging window.
   Example: open(SourcePresenter).
*/
Def open(self)
{
  if TheSourcePresenter := new(SourcePresenter, nil, nil, "Source Debugger ", nil) then
    show(TheSourcePresenter, SW_SHOW);
  else
    ^(TheSourcePresenter := nil);
  endif;
  ^TheSourcePresenter;
}
!!

/* 6/2/1992 9:37 - PUBLIC
  Defined te default style og the Source Presenter
*/
Def style(self)
{
  ^WS_OVERLAPPEDWINDOW;
}
!!
/* 6/2/1992 9:37 - PUBLIC */
Def wndClass(self)
{ ^"SourcePresenter"
}!!

/* 6/2/1992 9:37 - PUBLIC */
Def wndIcon(self)
{ ^"SOURCE"
}!!

now(SourcePresenter)!!

/* 12/1/1995 15:42 */
/* Get the address space text using reg. SR*/
Def getAddrSpaceText(self | addrSpace addrDesc)
{
  if (class(dataObject(selectedWin))=VirtSmrtDasmObject)
    ^(if getSpaceMode(CpuLibClass$Inst) = 0
      then "(SP)  "
      else "(UP)  "
      endif);
  else
    ^(if ldrSpaceMode(LoaderLibClass$Inst) = 0
      then "(SP)  "
      else "(UP)  "
      endif);      
  endif;
}
!!

/* 05/09/94 - PRIVATE (PUBLIC only to popWindow) 
  Called by the PopupMenu object to destroy itself in parent.
*/
Def nilifyPopWindow(self)
{
  popWindow := nil;
}
!!

/* 11/7/1992 11:11 - PRIVATE
  Check if the addressDescriptor has source.
  Return nil or moduleDesc
  NOTES:
    - This method does not destroy the input <addrDesc>.  
*/
Def addrHasSource?(self, addrDesc | sourceInfo)
{
  if (sourceInfo := addr2lineAndModule(SymbolLibClass$Inst, addrDesc)) then
     /* NOTES: sourceInfo = #(moduleDesc, moduleAddrDesc, lineNum, colNum, lineAddrDesc, lineNumDesc) */
     /* destroy the unused address descriptors */
     destroyAddress(AddressLibClass$Inst, sourceInfo[1]); 
     destroyAddress(AddressLibClass$Inst, sourceInfo[4]);
     /* RETURN: sourceInfo = #(moduleDesc, lineNum, colNum) */
     ^sourceInfo[0];
  endif;
  ^nil;
}
!!

/* 06/20/92 - Child Action */
Def cmdStepToCall(self, wp | execGranularity)
{
  /* Only Source Data Object can used execGran() - LINE/STMT */
  execGranularity := execGran(self);
  if (class(dataObject(selectedWin)) <> VirtSmartFileObject) then
    execGranularity := HLB_ASM_GRANULAR;
  endif;
  showWaitCurs();
  if wp = SRC_CMD_UNTILCALL then
    goUntilCallReturn( HLBreakLibClass$Inst, execGranularity,
                      HLB_GO_CALL_RETURN);
  else
    goIntoCallReturn( HLBreakLibClass$Inst, execGranularity,
                      HLB_GO_CALL_RETURN);
  endif;
  showOldCurs();
}
!!

/* 06/20/92 - Child Action */
Def cmdStepToReturn(self, wp | execGranularity)
{
  /* Only Source Data Object can used execGran() - LINE/STMT */
  execGranularity := execGran(self);
  if (class(dataObject(selectedWin)) <> VirtSmartFileObject) then
    execGranularity := HLB_ASM_GRANULAR;
  endif;
  showWaitCurs();
  if wp = SRC_CMD_TILRETURN then
    goUntilCallReturn(HLBreakLibClass$Inst, execGranularity,
                       HLB_GO_RETURN_ONLY);
  else
    goIntoCallReturn(HLBreakLibClass$Inst, execGranularity,
                       HLB_GO_RETURN_ONLY);
  endif;
  showOldCurs();
}
!!

/* 7/22/1993 10:25 */
Def reportFailedToOpenSource(self)
{
  displayFormattedError(ErrorTextLibClass$Inst,
    ER_SRC_CANT_OPEN_DATA, FORCE_POPUP, nil, nil, nil);
  ^nil;
}
!!

/* 6/3/1992 10:53 - PRIVATE
  Run the Source Path dialog to allow user edit/define source path.
*/
Def cmdOptnSourcePath(self, msg | pathDlg )
{
  /* Check to make sure that the pathsDict is not empty */
  if TheProjectInfoObj then
    pathDlg := new(SrcPathDialog, TheProjectInfoObj);
    runModal(pathDlg, DLG_SOURCE_PATHS, self);
  endif;
  ^GOOD;
}
!!

/* select compiler */
Def cmdOptnCompiler(self, item)
{
  selectCompiler(LoaderLibClass$Inst);
  ^0;
}
!!

/* 5/21/1993 13:18 - PUBLIC
  Rebuild the last loadfiles list appending to the File menu
*/
Def rebuildSourceLastLoadFileList(self, loadFilesColl |
  hMenuFile, lastID, numItems, currNumLoadFiles, i)
{
  if (size(loadFilesColl) = 0) cor not(hMenu) cor
    not(hMenuFile := Call GetSubMenu(hMenu, 0 /* FILE at index 0 */)) then
    ^nil;
  endif;
  /* Get menu ID of the last item in the File menu */
  if ((numItems := Call GetMenuItemCount(hMenuFile)) = -1) cor
      ((lastID := Call GetMenuItemID(hMenuFile, numItems-1)) = -1) then
    ^nil;
  endif;
  /* Remove the old appended menu and build a new menu */
  currNumLoadFiles := lastID - SRC_FILE_EXIT;

  i := 1;
  loop
  while (i <= currNumLoadFiles)
    begin
      if (Call RemoveMenu(hMenuFile, SRC_FILE_EXIT+i, MF_BYCOMMAND) = 0) then
         /* Failed to remove menu item */
         ^nil;
      endif;
      i := i + 1;
    endLoop;
  /* If there is no appended loadfile, need a separator */
  if (i = 1) cand (Call AppendMenu(hMenuFile, MF_SEPARATOR, 0, 0) = 0) then
     ^nil; /* report warning to user */
  endif;

  /* Append the last loadfiles as menu items */
  i := 1;
  do (loadFilesColl,
    {using(element | menuStr, strLen)
      if ((strLen := size(element)) > 25)
         menuStr := " ..."+subString(element, strLen-25, strLen);
         menuStr := "&"+asString(i)+menuStr;
      else
        menuStr := "&"+asString(i)+" "+asString(element);
      endif;
      /* Modify current menu item */
      if (Call AppendMenu(hMenuFile, MF_BYCOMMAND bitOr MF_STRING,
        SRC_FILE_EXIT+i, asciiz(menuStr)) = 0) then
         ^nil; /* report warning to user */
      endif;
      i := i + 1;
    });
  ^GOOD;
}
!!

/* 5/21/1993 11:23
  Load the last loadfile.
*/
Def cmdFileLoadLastFile(self, loadFileNum | loadfile, indx)
{
  indx := loadFileNum - SRC_FILE_EXIT - 1;
  if between(indx, 0, 4) cand
      (loadfile := lastLoadFiles(TheProjectInfoObj)[indx]) then
     /* Call to loader to do the load */
    load695(LoaderLibClass$Inst, loadfile,
      loadOnDemand(TheProjectInfoObj),
      loadFlags(TheProjectInfoObj),
      hWnd(self));
  else
    /* WARNING - REMOVE menu entry */
  endif;
  ^GOOD;
}
!!

/* state of dasmSym changed...update disassembly */
Def eventDasmSymChanged(self | otherBrowser)
{
  /* If emulation is running then do not refresh screen yet */
  if (not(prim_processorHalted?(HLBrkRootLibClass$Inst))) then
     ^GOOD;
  endif;
  /* If the current data object is not a dasm related object then do nothing */
  if (class(dataObject(selectedWin)) <> VirtSmartFileObject) then
    /* Recompute all dasm text */
    invalidateCache(selectedWin);
  endif;
  if ((otherBrowser := getOtherBrowser(self)) cand
     (class(dataObject(otherBrowser)) <> VirtSmartFileObject)) then
    invalidateCache(otherBrowser); 
  endif;
  ^GOOD;
}
!!

/* 4/20/1993 16:42 - PRIVATE
  Destroy the specified descriptors. - 300 bytes limit, reduce code size
*/
Def freeDesc(self, desc1, desc2)
{
  destroyAddress(AddressLibClass$Inst, desc1);
  destroyAddress(AddressLibClass$Inst, desc2);
}
!!

/* 2/23/1993 14:23 */ /* PRIVATE */
Def setPCAndDasmRange(self| newPC pcOffset, temp, tmpDesc)
{
  if not(newPC := getProgramCounter(CpuLibClass$Inst))
    ^nil;
  endif;
  if not(pcOffset := getOffset(AddressLibClass$Inst, newPC))
    destroyAddress(AddressLibClass$Inst, newPC);
    ^nil;
  endif;

  if not(tmpDesc := createAndSetAddress(AddressLibClass$Inst,
                    maxOutputOffset(AddressLibClass$Inst,newPC)))
    destroyAddress(AddressLibClass$Inst, newPC);
    ^nil;
  endif;

  if not(temp := subtractFromAddress(AddressLibClass$Inst, tmpDesc, pcOffset))
    destroyAddress(AddressLibClass$Inst, newPC);
    destroyAddress(AddressLibClass$Inst, tmpDesc);
    ^nil;
  endif;

  if not(temp := compareOffsets(AddressLibClass$Inst, 128,
                 getOffset(AddressLibClass$Inst, tmpDesc)))
    destroyAddress(AddressLibClass$Inst, newPC);
    destroyAddress(AddressLibClass$Inst, tmpDesc);
    ^nil;
  endif;

  /* determine the smallest address range to use */
  if (temp = ADRLIB_ADDR_GREATER_THAN) then /* 128 > maxOffset - pcOffset */
    if not(temp := getOffset(AddressLibClass$Inst, tmpDesc))
      destroyAddress(AddressLibClass$Inst, newPC);
      destroyAddress(AddressLibClass$Inst, tmpDesc);
      ^nil;
    endif;
  else
    temp := 128;
  endif;

  if not(newPC := setAddrRangeLength(AddressLibClass$Inst, newPC, temp))
    destroyAddress(AddressLibClass$Inst, newPC);
    destroyAddress(AddressLibClass$Inst, tmpDesc);
    ^nil;
  endif;
  destroyAddress(AddressLibClass$Inst, tmpDesc);
  ^newPC;
}
!!

/* 1/5/1992 14:49 - PRIVATE
  Given a symbol descriptor - return its #(address, name).
*/
Def getSymInfo(self | symDesc, symAddr, symName )
{
  /* tokenInfo[4] = symbol descriptor */
  if (symDesc :=  tokenInfo(selectedWin)[4]) cand
    (symAddr := getSymbolAddress(SymbolLibClass$Inst, symDesc)) cand
    (symName := getSymbolName(SymbolLibClass$Inst, symDesc)) then
    /* RETURN #(name, addressDesc-range) or nil */
    ^tuple(symName, symAddr);
  endif;
  ^nil;
}
!!

/* 12/9/1992 17:04 - PUBLIC (Only to its virtual Data Object)
  Return the current setting of Source File delimiter.
     sourceDelimiter = #true - return CR_LF
     sourceDelimtier = nil   - return LF
*/
Def sourceDelimiter(self)
{
  /* Return CRLF */
  if sourceDelimiter then
    ^CR_LF;
  endif;
  ^asString(asChar(10));
}
!!

/* 12/9/1992 17:03 - PRIVATE
  Select Source Line delimiter type.
*/
Def cmdOptnSourceLineDelimiter(self, cmd)
{
  if (cmd = SRC_OPTN_CRLF_DELIMITER) then
    unCheckMenuItem(self, SRC_OPTN_LF_DELIMITER);
    sourceDelimiter := #true;  /* CR_LF */
  else
    unCheckMenuItem(self, SRC_OPTN_CRLF_DELIMITER);
    sourceDelimiter := nil;  /* LF */
  endif;
  checkMenuItem(self, cmd);
  /* NOT SUPPORT
  if (class(dataObject(selectedWin)) <> VirtSmrtDasmObject)
    displayFormattedError(ErrorTextLibClass$Inst,
       ER_SRC_REOPEN_DELIM, FORCE_POPUP, nil, nil, nil);
  endif;
  */
}
!!

/* 12/9/1992 12:22 - PRIVATE
  Check if any breakpoint available in the data Object.
    NOTES: checkServer = #true/nil.
*/
Def noBkptsAvail(self, checkServer | condTrue )
{

  if (checkServer) then
    condTrue := not(anyBkpts?(HLBreakLibClass$Inst));
  else
    condTrue := (size(bkptDict(dataObject(selectedWin))) = 0);
  endif;
  /* Either condition is true then put up error message */
  if condTrue then
    displayFormattedError(ErrorTextLibClass$Inst,
       ER_BKPT_NONE_AVAILABLE, FORCE_POPUP, nil, nil, nil);
    ^#true;
  endif;
  ^nil;
}
!!

/* 12/8/1992 14:24 - PRIVATE
  Reset PC Mark when execution start - Apply to both Source browsers.
*/
Def eventExecutionStart(self | otherBrowser)
{
  if selectedWin then
    clearCurrentExecPoint(dataObject(selectedWin));
    clearPC(selectedWin);
  endif;
  /* Clear the PC of other browser if one is present */
  if (otherBrowser := getOtherBrowser(self)) then
    clearCurrentExecPoint(dataObject(otherBrowser));
    clearPC(otherBrowser);
  endif;
}
!!

/*  12/3/1992 16:33 - Child Action */
Def actionResetAndGo(self, wp)
{
  if (errMessageBox(ErrorTextLibClass$Inst, "Warning",
       "Reset target processor and go?",
       MB_YESNO bitOr MB_ICONQUESTION, HE_DLGM_SOURCEPR_1) = IDYES) then
    animating := nil; /* Exit Animating */
    showWaitCurs();
    resetAndGo(HLBreakLibClass$Inst);
    showOldCurs();
  endif;
  ^GOOD;
}
!!

/*  12/3/1992 16:33 - Child Action */
Def actionReset(self, wp)
{
  /* 04/25/94 - Nghia
  ** Move the warning message to CpuLib
  */
  if (CpuLibClass$Inst) cand reset(CpuLibClass$Inst) then
    animating := nil; /* Exit Animating */
  endif;
  ^GOOD;
}
!!

/* 11/18/1992 17:16 - PRIVATE
  Remap one data type cursor linked value to another data type.
*/
Def reMapLinkedCursor(self, newDataType, oldObj, oldValue | symInfo, addrDesc, newValue)
{
  if not(moduleDescriptor(oldObj)) then
    ^nil;
  endif;
  if (newDataType = VirtSmartFileObject) cand (class(oldObj) = VirtSMixedObject) then
    if not(addrDesc := createAddress(AddressLibClass$Inst)) cor
      not(setOffset(AddressLibClass$Inst, addrDesc, oldValue)) then
      ^nil;
    endif;
    /* Map address to lineNumber - symInfo = #(lineNum, colNum, addrDesc, lineNumDesc) */
    if not(symInfo := addr2Linenum(SymbolLibClass$Inst, moduleDescriptor(oldObj), addrDesc)) then
      ^nil;
    endif;
    /* Destroy the unused address range descriptor */
    destroyAddress(AddressLibClass$Inst, symInfo[2]);
    ^symInfo[0];
  else
    /* Map line # to address offset - symInfo = #(addrDesc, lineNum, colNum, lineNumDesc) */
    if not(symInfo := getLineNumInfo(SymbolLibClass$Inst, moduleDescriptor(oldObj), oldValue)) cor
      not(newValue := getOffset(AddressLibClass$Inst, symInfo[0])) then
      ^nil;
    endif;
    destroyAddress(AddressLibClass$Inst, symInfo[0]);
    ^newValue;
  endif;
}
!!

/* 11/7/1992 11:11 - PRIVATE
  Check if the addressDescriptor is within module symbol range.
    NOTES: return nil or module Descriptor of the input addrDesc.
*/
Def addrHasSymbol?(self, addrDesc | symInfo)
{
  if (symInfo := getAddrInfo(self, addrDesc)) then
    /* NOTES: symInfo  = #(moduleDesc, lineNum, colNum) */
    ^symInfo[0];
  endif;
  ^nil;
}
!!

/* 10/15/1992 10:12 - PUBLIC (to child)
  Return tab width to caller.
*/
Def tabWidth(self)
{
  ^tabWidth;
}
!!

/* 6/12/1992 14:09 - PRIVATE
  Input number of space per tab character.
*/
Def cmdOptnTabWidth(self, item | theDlg, numStr, saveValue, otherBrowser)
{
  saveValue := tabWidth;
  theDlg := open(InputDlgWithHelp);
  setUp(theDlg, "Tab Width", "Tab Width (1-32):",
     asString(tabWidth), HE_DLGD_SOURCEPR_1);
  if (runModal(theDlg, DLG_INPUT_WITH_HELP, ThePort) = IDCANCEL) then
    ^nil;
  endif;
  if not(numStr := getText(theDlg)) cor not(tabWidth := string2int(numStr)) then
    displayFormattedError(ErrorTextLibClass$Inst,
       ER_SRC_INVALID_INPUT_TAB, FORCE_POPUP, nil, nil, nil);
    tabWidth := saveValue;
  else
    if not(between(tabWidth, 1, 32)) then
      displayFormattedError(ErrorTextLibClass$Inst,
         ER_SRC_TAB_RANGE, FORCE_POPUP, nil, nil, nil);
      tabWidth := saveValue;
    else
      if tabWidth <> saveValue then
        /* Now set the new tabWidth to the Browsers */
        setTabWidth(selectedWin, tabWidth);
        if (otherBrowser := getOtherBrowser(self))
          setTabWidth(otherBrowser, tabWidth);
        endif;
        invalidate(self);
      endif;
    endif;
  endif;
  ^GOOD;
}
!!

/* 10/14/1992 10:40 - PRIVATE
  Get the line number portion of the selected string.
*/
Def getLineNumFromSelStr(self, selStr | lineParts )
{
  /* Parse the line num out - [xxxxxx]bttttttttt.... */
  if lineParts := findStrings(selStr, "[]") then
    ^asInt(lineParts[0], 10);
  endif;
  ^1;
}
!!

/* 06/20/92 - MENU
  Exit self.
*/
Def cmdFileExit(self,item)
{
  close(self);
}
!!

/* 06/20/92 - MENU */
Def cmdEditSearchAgain(self, item)
{
  /* search selected browser - #true : to search the same text again */
  searchText( self, #true, selectedWin);
}!!

/* 06/20/92 - MENU */
Def cmdEditTextSearch(self, item)
{
  /* search selected browser - nil : not a next search */
  searchText(self, nil, selectedWin);
}!!

/* 06/20/92 - MENU
  Show a source file from the current PC.
*/
Def cmdEditGotoPC(self, ignored | newPC)
{
  /* If emulation is running then do not perform the requested function */
  if not(processorHalted?(HLBrkRootLibClass$Inst)) then
    ^nil;
  endif;
  showWaitCurs();
  if (newPC := getProgramCounter(CpuLibClass$Inst)) then
     showFileFromAddress(self, newPC, selectedWin, nil, #true);
  endif;
  showOldCurs();
}
!!

/* 06/20/92 - MENU */
Def cmdEditGotoLine(self, msg | theDlg, lineStr, lineNum1, lineNum2, goFromTop)
{
  /* Convert mixed line to source line - for Mixed data object */
  lineNum1 := tokenLocation(selectedWin)[0];
  if class(dataObject(selectedWin)) = VirtSMixedObject cand
    (size(selectedLineText(selectedWin)) > 0) then
    lineNum1 := getLineNumFromSelStr(self, selectedLineText(selectedWin));
  endif;
  lineStr := asString(lineNum1);

  theDlg := open(GotoLineDialog);
  setValue(theDlg, lineStr);
  setHelpEntry(theDlg, HE_DLGR_GOTO_LINE);

  if (runModal(theDlg, DLG_GOTO_LINE, ThePort) = IDOK) then
    if not(lineStr := getValue(theDlg)) cor not(lineNum2 := string2int(lineStr)) cor
       (lineNum2 <= 0) then
       ^displayFormattedError(ErrorTextLibClass$Inst,
          ER_SRC_INVALID_LINE, FORCE_POPUP, nil, nil, nil);
    else
      /* NOTES: selectedWin handles "location beyond EOF" problem */
      showWaitCurs();
      if (class(dataObject(selectedWin)) = VirtSMixedObject) then
        goFromTop := (lineNum2 <=  lineNum1);   /* #true indicate search backward */
        if not(findTokenAndShow(self, format("[%6.6u]", lineNum2),
          selectedWin, goFromTop) ) then
          showTokenAt(selectedWin, tuple(virtualLineLimit(selectedWin)+1, 0));
        endif;
      else
        showTokenAt(selectedWin, tuple(lineNum2, 0));
      endif;
      showOldCurs();
    endif;
  endif;
  ^GOOD;
}
!!

/* 06/20/92 - MENU
  Get the input address and show the source of that address.
*/
Def cmdEditGotoAddress(self, msg | addrDesc, inputAddr, theDlg)
{
  /* If emulation is running then do not perform the requested function */
  if not(processorHalted?(HLBrkRootLibClass$Inst)) then
    ^nil;
  endif;
  /* Get the current PC to set dialog box */
  if not(addrDesc := getProgramCounter(CpuLibClass$Inst)) cor
     not(inputAddr := getAddressTextParam(AddressLibClass$Inst, addrDesc, 1, 0)) then
     inputAddr := "0x";
  endif;
  /* Get rid of the unused address descriptor */
  destroyAddress(AddressLibClass$Inst, addrDesc);
  addrDesc := nil;

  theDlg := open(GotoLineDialog);
  setValue(theDlg, inputAddr);
  setHelpEntry(theDlg, HE_DLGR_SRC_GOTO_ADDRESS);

  if (runModal(theDlg, DLG_GOTO, ThePort) <> IDOK) then
    ^nil;
  endif;
  /* Convert the input address to an address Descriptor */
  if not(inputAddr := getValue(theDlg)) cor (size(inputAddr) = 0) then
    ^displayFormattedError(ErrorTextLibClass$Inst,
       ER_SRC_INVALID_ADDRESS, FORCE_POPUP, nil, nil, nil);
  endif;
  showWaitCurs();
  if (addrDesc :=  convertTextToAddress(AddressLibClass$Inst, inputAddr)) then
    showFileFromAddress(self, addrDesc, selectedWin, #true, #true);
  endif;
  showOldCurs();
  ^GOOD;
}
!!

/* 10/13/1992 14:51 - PRIVATE
  Get all breakpoints of the  specified browser and check menu cmds.
*/
Def getBkptsAndCheck(self, theBrowser)
{
  if (size(bkptDict(dataObject(theBrowser))) > 0) then
    enableMenuItems(self, tuple(SRC_BKPT_CLR_BKPT,
                                SRC_BKPT_ENABLE_BKPT,
                                SRC_BKPT_DISABLE_BKPT,
                                SRC_BKPT_CLEAR_ALL,
                                SRC_BKPT_ENABLE_ALL,
                                SRC_BKPT_DISABLE_ALL));
    ^#true;
  else
    disableMenuItems(self, tuple(SRC_BKPT_CLR_BKPT,
                                SRC_BKPT_ENABLE_BKPT,
                                SRC_BKPT_DISABLE_BKPT));
    /* If there is no breakpoint then disable these commands */
    if not(anyBkpts?(HLBreakLibClass$Inst)) then
      disableMenuItems(self, tuple(SRC_BKPT_CLEAR_ALL,
                                SRC_BKPT_ENABLE_ALL,
                                SRC_BKPT_DISABLE_ALL));
    endif;
  endif;
  ^nil;
}
!!

/* 10/12/1992 14:32 - PRIVATE
  Create the execution control bar.
*/
Def initExecBar(self | theBut)
{
  /* Create child controls for execution buttons */
  childDict    := new(Dictionary, 8);
  childActions := new(Dictionary, 8);

  /* 12 = number of chars in a longest button title */
  minButtonWidth := (tmWidth * 10);
  /* Execution control buttons */
  theBut := newPush(Button, GO_BUTTON,         self, "Go");
  add(childDict, GO_BUTTON, theBut);
  theBut := newPush(Button, HALT_BUTTON,       self, "Halt");
  add(childDict, HALT_BUTTON, theBut);
  theBut := newPush(Button, STEP_BUTTON,       self, "Step Into");
  add(childDict, STEP_BUTTON, theBut);
  theBut := newPush(Button, STEPOVER_BUTTON,   self, "Step Over");
  add(childDict, STEPOVER_BUTTON, theBut);
  theBut := newPush(Button, UNTILCALL_BUTTON,  self, "Till Call");
  add(childDict, UNTILCALL_BUTTON, theBut);
  theBut := newPush(Button, TILRETURN_BUTTON,  self, "Till Return");
  add(childDict, TILRETURN_BUTTON, theBut);
  theBut := newPush(Button, GOTOCURSOR_BUTTON, self, "Goto Cursor");
  add(childDict, GOTOCURSOR_BUTTON, theBut);
  theBut := newPush(Button, 809,               self, "Reset"); /* reset button */
  add(childDict, 809, theBut);
  /* Responder methods  */
  add(childActions, GO_BUTTON,        #actionGo);
  add(childActions, HALT_BUTTON,      #actionHalt);
  add(childActions, STEP_BUTTON,      #actionStep);
  add(childActions, STEPOVER_BUTTON,  #actionStepOver);
  add(childActions, UNTILCALL_BUTTON, #actionStepToCall);
  add(childActions, TILRETURN_BUTTON, #actionStepToReturn);
  add(childActions, GOTOCURSOR_BUTTON,#actionGoToCursor);
  add(childActions, 809,              #actionReset);
}
!!

/* 8/23/1992 3:42 - PRIVATE
  Set commands for a File data object.
*/
Def setCmdsForSource(self)
{
  enableMenuItems(self, tuple(SRC_VIEW_MIX,
                              SRC_VIEW_SHOW_LINENUM,
                              SRC_EDIT_GOTO_LINE,
                              SRC_OPTN_STEP_LINE_GRAN,
                              SRC_OPTN_STEP_STMT_GRAN,
                              SRC_CMD_UNTILCALL,
                              SRC_CMD_TILRETURN,
                              SRC_CMD_INTOCALL,
                              SRC_CMD_INTORETURN));
  /* Enable the 'Go' buttons */
  enable(at(childDict, TILRETURN_BUTTON));
  enable(at(childDict, UNTILCALL_BUTTON));
}
!!

/* 8/23/1992 3:44 - PRIVATE
  Set commands for Asm data object.
*/
Def setCmdsForAsm(self)
{
  disableMenuItems(self, tuple(SRC_VIEW_MIX,
                               SRC_VIEW_SHOW_LINENUM,
                               SRC_EDIT_GOTO_LINE,
                               SRC_OPTN_STEP_LINE_GRAN,
                               SRC_OPTN_STEP_STMT_GRAN,
                               SRC_CMD_UNTILCALL,
                               SRC_CMD_TILRETURN,
                               SRC_CMD_INTOCALL,
                               SRC_CMD_INTORETURN));
  /* Disable the 'Go' buttons */
  disable(at(childDict, TILRETURN_BUTTON));
  disable(at(childDict, UNTILCALL_BUTTON));
}
!!

/* 8/26/1992 19:37 - PRIVATE
  Continuously Steps the program.
*/
Def actionAnimating(self, wp | err )
{
  if (animating) then
    ^animating := nil; /* Exit animating */
  endif;
  /* Check menu and set semaphore to be true */
  grayMenuItem(menu, SRC_CMD_ANIMATE_OVER);
  checkMenuItem(menu, SRC_CMD_ANIMATE_INTO);
  animating := #true;

  err := GOOD;
  loop
  while (animating) cand (err = GOOD) cand
    (TaskLibClass$Inst cand not(queryAbort(TaskLibClass$Inst)) )
    begin
      /* Wait for eventPCChanged to set the flag again - if error exit */
      showWaitCurs();
      if class(dataObject(selectedWin)) <> VirtSmartFileObject cor
          not(currentExecPoint(dataObject(selectedWin))) then
        err := asmStepType(HLBreakLibClass$Inst, HLB_STEP_INTO, 1);
      else
        err := sourceStep(HLBreakLibClass$Inst, HLB_STEP_INTO, execGran(self), 1);
      endif;
      showOldCurs();
      /* Loop to flush the messages queue if command is sucessful */
      loop
      while (animating) cand (err = GOOD) cand not(checkMessage())
        begin
        ; /* Let the system run to process message */
      endLoop;
  endLoop;
  animating := nil;
  beep();
  enableMenuItem(menu, SRC_CMD_ANIMATE_OVER);
  unCheckMenuItem(menu, SRC_CMD_ANIMATE_INTO);
}
!!

/* 8/26/1992 19:37 - PRIVATE
  Continuously Steps the program.
*/
Def actionAnimatingOver(self, wp | err)
{
  if animating then
    animating := nil; /* Exit animating */
    ^beep();
  endif;
  animating := #true;
  grayMenuItem(menu, SRC_CMD_ANIMATE_INTO);
  checkMenuItem(menu, SRC_CMD_ANIMATE_OVER);
  err := GOOD;
  loop
  while (animating) cand (err = GOOD) cand
    (TaskLibClass$Inst cand not(queryAbort(TaskLibClass$Inst)) )
    begin
      /* Wait for eventPCChanged to set the flag again - if error exit */
      showWaitCurs();
      if class(dataObject(selectedWin)) <> VirtSmartFileObject cor
         not(currentExecPoint(dataObject(selectedWin))) then
        err := asmStepType(HLBreakLibClass$Inst, HLB_STEP_OVER, 1);
      else
        err := sourceStep(HLBreakLibClass$Inst, HLB_STEP_OVER, execGran(self), 1);
      endif;
      showOldCurs();
      /* Flush the message queue before issue another command */
      loop
      while (animating) cand (err = GOOD) cand not(checkMessage())
        begin
        ; /* Let the system run */
      endLoop;
  endLoop;
  beep();
  animating := nil;
  enableMenuItem(menu, SRC_CMD_ANIMATE_INTO);
  unCheckMenuItem(menu, SRC_CMD_ANIMATE_OVER);
}
!!

/* 06/18/92 - Child Action */
Def actionGo(self, wp)
{
  showWaitCurs();
  go(HLBreakLibClass$Inst);
  showOldCurs();
}
!!

/* 06/20/92 - MENU
  Set the current PC to the cursor position then do a GO
  NOTES: Warning user about the potential system screw up.
*/
Def actionGoFromCursor(self, item | virtDataObj, tokenInfo)
{
  /* Warn user about the potential */
  if (errMessageBox(ErrorTextLibClass$Inst, "Warning",
      "This operation might destroy the current execution state.  "
      + "Do you really want to do this?",
       MB_YESNO bitOr MB_ICONASTERISK, HE_DLGM_SOURCEPR_3) = IDYES) then

    /* Get the current cursor position of the selectedWin */
    virtDataObj := dataObject(selectedWin);
    tokenInfo := tokenLocation(selectedWin);

    /* NOTES: tokenInfo := #(lineNum, startChar, endChar) */
    tokenInfo[1] = 0;
    /* Set the current PC at the address of line */
    if setPC(virtDataObj, tokenInfo[0], tokenInfo[1], selectedLineText(selectedWin)) then
      /* Issue a GO */
      actionGo(self, #ignore);
    endif;
  endif;
}
!!

/* 7/17/1992 16:21 - MENU
  Let emulation go to the current address of cursor.
    a. Set a temporary breakpoint at current cursor position.
    b. Execute a Go.
*/
Def actionGoToCursor(self, wp | virtDataObj, tokenInfo)
{
  /* Get the current cursor position of the selectedWin */
  virtDataObj := dataObject(selectedWin);
  tokenInfo := tokenLocation(selectedWin);
  /* NOTES: tokenInfo := #(lineNum, startChar, endChar) */
  tokenInfo[1] := getLogicalCharPos(selectedWin, tokenInfo[1]);
  /* DEBUG printLine("col :"+asString(tokenInfo[1])); */
  showWaitCurs();
  /*
  ** Set a temporary internal breakpoint at address and Go - when halt
  ** breakpoint will be removed automatically by the server.
  */
  if setBkpt(virtDataObj, HLB_BKPT_LIFE_TEMP_INT, tokenInfo[0], tokenInfo[1],
    selectedLineText(selectedWin)) then
    go(HLBreakLibClass$Inst);     /* do a GO */
  endif;
  showOldCurs();
}
!!

/*  3/23/1992 16:33 - Child Action */
Def actionHalt(self, wp)
{
  animating := nil; /* Exit Animating */
  showWaitCurs();
  halt(HLBreakLibClass$Inst);
  showOldCurs();
}
!!

/* 06/20/92 - Child Action */
Def actionStep(self, wp)
{
  showWaitCurs();
  /* Do the AsmStep if dataObject is not a File or a file object without PC */
  if (class(dataObject(selectedWin)) <> VirtSmartFileObject) cor
      not(currentExecPoint(dataObject(selectedWin))) then
    asmStepType(HLBreakLibClass$Inst, HLB_STEP_INTO, stepCount);
  else
    sourceStep(HLBreakLibClass$Inst, HLB_STEP_INTO, execGran(self), stepCount);
  endif;
  showOldCurs();
}
!!

/* 06/20/92 - Child Action */
Def actionStepOver(self, wp)
{
  showWaitCurs();
  /* Asm step if dataObject is not a file object, or a file object without PC */
  if (class(dataObject(selectedWin)) <> VirtSmartFileObject) cor
      not(currentExecPoint(dataObject(selectedWin)))  then
    asmStepType(HLBreakLibClass$Inst, HLB_STEP_OVER, stepCount);
  else
    sourceStep(HLBreakLibClass$Inst, HLB_STEP_OVER, execGran(self), stepCount);
  endif;
  showOldCurs();
}
!!

/* 06/20/92 - Child Action */
Def actionStepToCall(self, wp | execGranularity)
{
  /* Only Source Data Object can used execGran() - LINE/STMT */
  execGranularity := execGran(self);
  if (class(dataObject(selectedWin)) <> VirtSmartFileObject) then
    execGranularity := HLB_ASM_GRANULAR;
  endif;
  showWaitCurs();
  if goType = SRC_OPTN_EXEC_GO_UNTIL then
    goUntilCallReturn( HLBreakLibClass$Inst, execGranularity,
                      HLB_GO_CALL_RETURN);
  else
    goIntoCallReturn( HLBreakLibClass$Inst, execGranularity,
                      HLB_GO_CALL_RETURN);
  endif;
  showOldCurs();
}
!!

/* 06/20/92 - Child Action */
Def actionStepToReturn(self, wp | execGranularity)
{
  /* Only Source Data Object can used execGran() - LINE/STMT */
  execGranularity := execGran(self);
  if (class(dataObject(selectedWin)) <> VirtSmartFileObject) then
    execGranularity := HLB_ASM_GRANULAR;
  endif;
  showWaitCurs();
  if goType = SRC_OPTN_EXEC_GO_UNTIL then
    goUntilCallReturn(HLBreakLibClass$Inst, execGranularity,
                       HLB_GO_RETURN_ONLY);
  else
    goIntoCallReturn(HLBreakLibClass$Inst, execGranularity,
                       HLB_GO_RETURN_ONLY);
  endif;
  showOldCurs();
}
!!

/* 7/29/1992 9:10 - Documentation */
Def aREAD_ME(self)
{
/*
  This documentation is the general introduction of the functionalities and operation
  of this class.

  The SourcePresenter uses the SourceCB object for source text browsing.  All menu and
  operational commands are directly intercepted and propagate to its browser child object.
  One exception that the child browsers receive and process the scrolling and
  single-click to set/clear breakpoint without any knowledge of its parent object (which is
  the SourcePresenter.)

  There are three type of data objects that a SourceCB can process:
    - VirtSmrtDasmObject:  object to handle disassembly text.
    - VirtSmartFileObject: object to handle text file.
    - VirtSMixedObject:    object to handle the mixed source and assembly text.

  Notice that the SourceCB and the SourcePresenter never need to know what type
  of data object that it's processing at any instance.  To achieve this goal, there
  are overloading methods being implemented for these three data objects, which
  provide a general interface for the SourceCB.  However, in some certain circumstances,
  the SourcePresenter and the SourceCB have to filter out their current data object for
  its specific operations.
*/
}
!!

/* 5/8/1992 10:47 - PRIVATE
  Enable all specified menu items.
  EX: enableMenuItems(SourcePresenter, #(SRC_FILE_MODULES, ...));
*/
Def checkMenuItems(self, menuCmdSet)
{
  if menuCmdSet then
    do(menuCmdSet,
      {using(item)
        checkMenuItem(menu, item);
      });
  endif;
}
!!

/* 06/20/92 - Actor
  closing self.
*/
Def close(self)
{
  /* Unregister LinkedCursor Server */
  if TheCursorLinkServer then
    unRegister(TheCursorLinkServer, TheSourcePresenter);
  endif;
  /* Unhook with globals Windows Menu */
  removeWindowMenu(self);
  maybeClosePopup(self);
  saveSelf(self); /* Save options to ini file */
  closeEvents(EvNoteLibClass$Inst, eventDescSet);
  eventRespSet := nil;
  animating    := nil; /* make sure animating is terminated when exit */
  /* Destroy the browser history stack data objects */
  do (backwardFile,
    {using(savedDataObj)
      if (savedDataObj cand (generality(savedDataObj) < 0)) then
        destroyDataObj(savedDataObj);
      endif;
     });
  backwardFile := nil;
  close(self:ancestor);
  TheSourcePresenter := nil;
}
!!

/* 3/29/1992 16:40 - PRIVATE
  Clear a breakpoint on selected line.
*/
Def cmdBkptClear(self, item | tokenInfo)
{
  /* NOTES: tokenInfo = #(lineNum, StartChar, EndChar) */
  tokenInfo := tokenLocation(selectedWin);
  tokenInfo[1] := getLogicalCharPos(selectedWin, tokenInfo[1]);
  /* DEBUG - printLine("col :"+asString(tokenInfo[1])); */
  clearStmtBkpt(dataObject(selectedWin), tokenInfo[0], tokenInfo[1],
     selectedLineText(selectedWin));
  /* Let Event  Notification does the update */
  ^GOOD;
}
!!

/* 06/20/92 - MENU
  Clear all breakpoint settings.
*/
Def cmdBkptClearAll(self, item)
{
  /* #true to check server */
  if noBkptsAvail(self, #true) then
    ^nil;
  endif;
  /* Let warn the user about his/her intented actions */
  if (errMessageBox(ErrorTextLibClass$Inst, "Warning",
       "Do you really want to clear all breakpoints?",
       MB_YESNO bitOr MB_ICONQUESTION, HE_DLGM_SOURCEPR_4) = IDYES) then
    showWaitCurs();
    removeAllBkpt(HLBreakLibClass$Inst);
    showOldCurs();
  endif;
  ^GOOD;
}
!!

/* 5/1/1992 11:38 - PRIVATE
  Disable a breakpoint.
*/
Def cmdBkptDisable(self, msg | tokenInfo)
{
  tokenInfo := tokenLocation(selectedWin);
  /* NOTES: tokenInfo = #(lineNum, colStart, colEnd) */
  tokenInfo[1] := getLogicalCharPos(selectedWin, tokenInfo[1]);
  /* DEBUG - printLine("col :"+asString(tokenInfo[1])); */
  disableStmtBkpt(dataObject(selectedWin), tokenInfo[0], tokenInfo[1],
    selectedLineText(selectedWin));
  ^GOOD;
  /* Let Event Notification does the update */
}
!!

/* 5/1/1992 11:38 - PRIVATE
  Enable a breakpoint.
*/
Def cmdBkptEnable(self, msg | virtDataObj, tokenInfo)
{
  /* Get the token location of a line */
  tokenInfo := tokenLocation(selectedWin);
  /* NOTES: tokenInfo = #(lineNum, colStart, colEnd) */
  tokenInfo[1] := getLogicalCharPos(selectedWin, tokenInfo[1]);
  /* DEBUG - printLine("col :"+asString(tokenInfo[1])); */
  enableStmtBkpt(dataObject(selectedWin), tokenInfo[0], tokenInfo[1],
      selectedLineText(selectedWin));
  ^GOOD;
  /* Let Event  Notification does the update */
}
!!

/* 6/12/1992 13:16 - PRIVATE
  set a source breakpoint
*/
Def cmdBkptSet(self, cmdType | bkptType, tokenInfo )
{
  tokenInfo := tokenLocation(selectedWin);
  /* NOTES: tokenInfo = #(lineNum, startChar, endChar) */
  bkptType := HLB_BKPT_LIFE_TEMPORARY;
  if (cmdType = SRC_BKPT_PERM_SET_BREAK) cor (cmdType = SRC_POPUP_BKPT_LINE_PERM) then
    /* Set Permanent breakpoint */
    bkptType := HLB_BKPT_LIFE_PERMANENT;
  endif;      /* Call data object to set breakpoint */
  tokenInfo[1] := getLogicalCharPos(selectedWin, tokenInfo[1]);
  /* DEBUG  printLine("col :"+asString(tokenInfo[1])); */
  setBkpt( dataObject(selectedWin), bkptType, tokenInfo[0], tokenInfo[1],
      selectedLineText(selectedWin));
  /* Let Event  Notification does the update */
  ^GOOD;
}
!!

/* 3/29/1992 16:40 - PRIVATE
  Set a breakpoint by address or symbol.
  Handles Intel breakpoint dialog box and retrieve modules on demand as well.
*/
Def cmdBkptSetAt(self, item
    | theDlg, tmpDict, resourceId, addrSpace, msgDlg, loadFile)
{
  /* If emulation is running then do not perform the requested function */
  if not(processorHalted?(HLBrkRootLibClass$Inst)) then
    ^nil;
  endif;
  msgDlg := nil;
  if not(loadFile := currentLoadFile(TheProjectInfoObj)) then
    ^nil;
  endif;
  if (not(modulesDictUpdated(loadFile)) cand
     (numLoadedModules(loadFile) > 10)) then
    msgDlg := new(Dialog);
    runModeless(msgDlg, DLG_WAIT_MSG, ThePort);
  endif;

  /* Utilizes the BkptDialog of the Breakpoint Presenter */
  tmpDict := getModuleNamesDict(currentLoadFile(TheProjectInfoObj));

  /* Clean up : message dialog */
  if (msgDlg <> nil) then  end(msgDlg, 1); endif;

  /* Create a BkptDialog object*/
  resourceId := INTEL_BKPT_DLG;
  theDlg := new(BkptIntelDialog, tmpDict, ADDR_SPACE_DEFAULT);

  /* Run the dialog box */
  if not(theDlg) cor (runModal(theDlg, resourceId, self) <> IDOK) then
    /*Fixed bug ID#211 Eric 3/2/98*/
    if (anyBkpts?(HLBreakLibClass$Inst)) then
      enableMenuItems(self, tuple(SRC_BKPT_CLR_BKPT,
                                SRC_BKPT_ENABLE_BKPT,
                                SRC_BKPT_DISABLE_BKPT,
                                SRC_BKPT_CLEAR_ALL,
                                SRC_BKPT_ENABLE_ALL,
                                SRC_BKPT_DISABLE_ALL));
    endif;
    /*eof Eric 3/2/98*/
    ^nil;
  endif;

  /* Let Event Notification updates the new breskpoint setting */
  ^GOOD;
}
!!

/* 3/25/1992 16:08 - MENU */
Def cmdBkptSetStateAll(self, item)
{
  /* #true to check server */
  if noBkptsAvail(self, #true) then
    ^nil;
  endif;
  showWaitCurs();
  select
    case item = SRC_BKPT_ENABLE_ALL
      enableAllBkpt(HLBreakLibClass$Inst);
    endCase
    case item = SRC_BKPT_DISABLE_ALL
      disableAllBkpt(HLBreakLibClass$Inst);
    endCase
  endSelect;
  showOldCurs();
}
!!

/* 6/12/1992 13:16 - PRIVATE
  show all breakpoints.
*/
Def cmdBkptShowAll(self, item)
{
  if TheBreakpointPresenter then
    Call BringWindowToTop(getHWnd(TheBreakpointPresenter));
    show(TheBreakpointPresenter, SW_SHOWNORMAL);
  else
    TheBreakpointPresenter := open(BkptPresenter);
  endif;
}
!!

/* 08/25/93 - MENU
  Display the Load info from the last interactive load operation
*/
Def cmdFileLoadInfo(self, item )
{
  displayLoadInfo(CLIULibraryClass$Inst);
}
!!

/* 06/20/92 - MENU
  Get the next browser's DataObject position in the Hyperbrowse history.
*/
Def cmdFileNext(self, item | nextVirtDataObj, virtDataObj)
{
  if (size(backwardFile) == 0) cor
    ((nextVirtDataObj := next(backwardFile)) = nil) then
    ^displayFormattedError(ErrorTextLibClass$Inst,
       ER_SRC_END_OF_HISTORY, FORCE_POPUP, nil, nil, nil);
  else
    virtDataObj := dataObject(selectedWin) ;
    close(virtDataObj);
    /* Reset the browser with the nextVirtDataObj - reset the old one if failed */
    if not(resetVirtDataObj(self, nextVirtDataObj)) then
      resetVirtDataObj(self, virtDataObj);
    endif;
  endif;
  showTitle( self ) ;
}
!!

/* 06/20/92 - MENU
  Open a loadfile to load. Save the loadfile name and path to currentLoadFile.
*/
Def cmdFileOpen(self, item)
{
  requireWithPath(LoaderLib, "ld695.dll");
  ^invokeLoader(LoaderLibClass$Inst, self);
}!!

/* 06/20/92 - MENU
  Get the previous browser's DataObject position from the hyperbrowse.
*/
Def cmdFilePrevious(self, item | inMiddle, prevVirtDataObj, virtDataObj)
{
  /* Get inMiddle flag first, before extract prev. object */
  inMiddle := inMiddle?(backwardFile);
  if (size(backwardFile) == 0)  cor
     ((prevVirtDataObj := previous(backwardFile)) = nil) then
    displayFormattedError(ErrorTextLibClass$Inst,
       ER_SRC_START_OF_HISTORY, FORCE_POPUP, nil, nil, nil);
  else
    /* Close the current dataObject of selectedWin */
    virtDataObj := dataObject(selectedWin);
    close(virtDataObj);
    if not(inMiddle) cand virtDataObj then
      maybeAdd(backwardFile, virtDataObj);
    endif ;

    /* Same data object */
    if (prevVirtDataObj == virtDataObj) then
      prevVirtDataObj := previous(backwardFile);
    endif;

    if not(prevVirtDataObj) then
      ^displayFormattedError(ErrorTextLibClass$Inst,
          ER_SRC_START_OF_HISTORY, FORCE_POPUP, nil, nil, nil);
    endif;
    /* Reset the browser with the prevVirtDataObj */
    if not(resetVirtDataObj(self, prevVirtDataObj)) then
      resetVirtDataObj(self, virtDataObj);
    endif;
  endif;
  showTitle( self ) ;
}
!!

/* 06/20/92 - FILE MENU
  Show the text of the selected module of current loadfile
  Handles retrieving all modules ondemand.  Put up a warning box while
  process modules.
*/
Def cmdFileViewModules(self,item | theDlg, theModule, ret, loadFile, msgDlg)
{
  /* Check to make sure that currentLoadFile is valid and loaded */
  if isLoaded(loadFile := currentLoadFile(TheProjectInfoObj)) then
    msgDlg := nil;
    if (not(modulesDictUpdated(loadFile)) cand
       (numLoadedModules(loadFile) > 10)) then
      msgDlg := new(Dialog); 
      runModeless(msgDlg, DLG_WAIT_MSG, ThePort);  
    endif;

    /* Process the modulesDict ondemand */
    modulesDict(loadFile);   
     
    /* Clean up : message dialog */
    if (msgDlg <> nil) then  end(msgDlg, 1); endif;
     
    theDlg := new(LoadMDialog, loadFile);
    if (runModal(theDlg, DLG_SOURCE_BROWSE, ThePort) <> IDOK) then /* user press CANCEL */
      ^nil;
    endif;

    /* Open SourcePresenter with the module */
    if (theModule := getSelModule(theDlg)) <> nil then
      /* show the source of module */
      showWaitCurs();                     /* row, col, moduleDesc */
      ret := showFileFromLineAndModule(self, 1, 0, theModule);
      showOldCurs();
      ^ret;
    endif;
  else
    /* Somehow the currentLoadFile symbol being removed */
    ^displayFormattedError(ErrorTextLibClass$Inst,
        ER_SRC_NO_LOADFILE, FORCE_POPUP, nil, nil, nil);
  endif;
}
!!

/* 8/1/1992 15:32 - PRIVATE
  Provide Help dispatching for the Source 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_SOURCE);
    endCase
  endSelect;
}
!!

/* 3/26/1992 16:50 - PRIVATE */
Def cmdOptnExecGran(self, item)
{
  /*
  *** Use the execGran as flag to select menu items only.
  *** to get the actual ExecGran value - call execGran()
   */
  if item <> execGran then
    unCheckMenuItem(menu, execGran);
    checkMenuItem(menu, item);
    execGran := item;
  endif;
}
!!

/* 3/26/1992 16:05 - PRIVATE */
Def cmdOptnGoType(self, item)
{
  if item <> goType then
    unCheckMenuItem(menu, goType);
    checkMenuItem(menu, item);
    goType := item;
    setGoButtonCmds(self);
  endif;
}
!!

/* OPTION MENU - 6/12/92
  Input number of maximum files history.
*/
Def cmdOptnHistoryDepth(self, msg | theDlg, depthStr, newDepth)
{

  theDlg := open(InputDlgWithHelp);
  setUp(theDlg, "Browser History Depth", "Max Depth (5-100):",
     asString(maxDepth), HE_DLGD_SOURCEPR_2);
  if (runModal(theDlg, DLG_INPUT_WITH_HELP, ThePort) <> IDOK) then
    ^nil;
  endif;

  if (depthStr := getText(theDlg)) cand (newDepth := string2int(depthStr)) cand
    between(newDepth, 5, 100) then
    maxDepth := min(newDepth, 100);
    setMaxDepth(backwardFile, maxDepth);
  else
    ^displayFormattedError(ErrorTextLibClass$Inst,
       ER_SRC_HISTORY_DEPTH, FORCE_POPUP, nil, nil, nil);
  endif;
}
!!

/* 6/12/1992 14:09 - PRIVATE
  Input number of execution step per Step commands.
*/
Def cmdOptnStepCount(self, item | theDlg, stepStr, saveValue )
{

  saveValue := stepCount;
  theDlg := open(InputDlgWithHelp);
  setUp(theDlg, "Step Count", "Step Count:",
     asString(stepCount), HE_DLGD_SOURCEPR_3);
  if (runModal(theDlg, DLG_INPUT_WITH_HELP, ThePort) = IDCANCEL) then
    ^nil;
  endif;

  if not(stepStr := getText(theDlg)) cor not(stepCount := string2int(stepStr)) cor
    (stepCount <= 0) then
    displayFormattedError(ErrorTextLibClass$Inst,
       ER_SRC_STEP_COUNT, FORCE_POPUP, nil, nil, nil);
    stepCount := saveValue;
  endif;
  ^GOOD;
}
!!

/* POPUPMENU -- show address for selected function */
Def cmdPopFunAddressShow(self, ignored | symInfo, addrString)
{
  /* Get information about the symbol */
  if not(symInfo := getSymInfo(self)) then
    ^nil;
  endif;

  /* NOTES: symInfo is #(name, addressDescriptor) */
  addrString := getAddressTextParam(AddressLibClass$Inst, symInfo[1], 1, 1);
  destroyAddress(AddressLibClass$Inst, symInfo[1]);
  if not(addrString) then
    ^nil
  endif ;
  /* Show user the information */
  displayFormattedError(ErrorTextLibClass$Inst,
     ER_SRC_FUNC_START_INFO, FORCE_POPUP,
     asciiz(symInfo[0]), asciiz(addrString), nil);
  ^GOOD;
}
!!

/* 06/20/92 - POPUPMENU
  Show source/dasm text of the selected function.
*/
Def cmdPopFunSource(self, msg | symInfo)
{
  /* NOTES: symInfo = #(name, addressRangeDesc) */
  if not(symInfo := getSymInfo(self)) then
    ^nil;
  endif;
  /* Force Windows to repaint Source first */
  update(self);
  showWaitCurs();
  /* NOTES: function showFileFromAddress() will consume the addrDesc */
  if showFileFromAddress(self, symInfo[1], selectedWin, #true, #true) then
    /* Find funcInfo name then hilight it. - Only Source need it. */
    if (class(dataObject(selectedWin)) = VirtSmartFileObject) then
      findAndShowStringReverse(selectedWin, symInfo[0]);
    endif;
  endif;
  showOldCurs();
  ^nil;
}
!!

/* POPUPMENU
  Open the Variable Browser and put the selected variable into it to allow editing.
*/
Def cmdPopVarValueEdit(self, msg | symbolDesc, errMsg)
{
  if not(symbolDesc := getTokenSymbol(selectedWin)) then
    displayFormattedError(ErrorTextLibClass$Inst,
       ER_SRC_NO_VAR_SEL, FORCE_POPUP, nil, nil, nil);
  else

    if not(prim_processorHalted?(HLBrkRootLibClass$Inst)) then
      errMsg := getErrorText(ErrorTextLibClass$Inst, clearError(HLBrkRootLibClass$Inst));
      displayFormattedError(ErrorTextLibClass$Inst,
         ER_SRC_MEM_NEEDED, FORCE_POPUP, "Inspect variable", asciiz(errMsg), nil);
      ^0;
    endif;
    /* Force Windows to update Source first */
    update(self);
    showWaitCurs();
    /* entire or composite ? */
    if (size(getLeadingToken(selectedWin)) = size(getToken(selectedWin))) then
      /* No syntactic component structure */
      if TheVariablePresenter then
        addVarSymbol(TheVariablePresenter, symbolDesc, getToken(selectedWin));
        show(TheVariablePresenter, SW_NORMAL);
      else
        TheVariablePresenter := open(VarPresenter, symbolDesc, getToken(selectedWin));
      endif ; /* composite */
    else
      /* Has component syntactic structure, e.g. "x.y->z" */
      if TheVariablePresenter then
        addVarComposite( TheVariablePresenter,
                         symbolDesc,
                         tokenInfo(selectedWin)[0],
                         getTokenOffset(selectedWin)
                       ) ;
        show(TheVariablePresenter, SW_SHOWNORMAL);
      else
        TheVariablePresenter :=
                openWithComposite( VarPresenter,
                                   symbolDesc,
                                   tokenInfo(selectedWin)[0],
                                   getTokenOffset(selectedWin)   ) ;
      endif ;
    endif ; /* composite */
  endif;
  showOldCurs();
}
!!

/* 6/24/1992 16:48 - PRIVATE
  Turn ON/OFF linkedCursor
*/
Def cmdViewLinkedCursor(self, cursorLinked)
{
  /* true/false - register/unregister with CursorLinkedServer respectively */
  if (cursorLinked) then
     register(TheCursorLinkServer, self);
  else
     setCursorLinked(selectedWin, nil);
     unRegister(TheCursorLinkServer, self);
     invalidate(selectedWin);
  endif;
  ^GOOD;
}
!!

/* 6/12/1992 13:47 - PRIVATE
  Select View mode of the Source Browsers.
*/
Def cmdViewMode(self, item | errMsg)
{
  /* If emulation is running then do not perform the requested function */
  if (class(dataObject(selectedWin)) == VirtSmartFileObject) cand
     (item == SRC_VIEW_MIX) cand not(prim_processorHalted?(HLBrkRootLibClass$Inst)) then
    errMsg := getErrorText(ErrorTextLibClass$Inst, lastError(HLBrkRootLibClass$Inst));
    displayFormattedError(ErrorTextLibClass$Inst,
       ER_SRC_MEM_NEEDED, FORCE_POPUP, "Switch to new view", asciiz(errMsg), nil);
    ^GOOD;
  endif;
  /* Only if new mode is selected */
  if (item <> viewMode) then
    unCheckMenuItem(menu, viewMode);
    viewMode := item;
    checkMenuItem(menu, item);
    /* Open a new data objects according to the new view mode */
    showWaitCurs();
    openNewDataView(self, browser);
    if split?(self) and browser2 then
      openNewDataView(self, browser2);
    endif;
    showOldCurs();
  endif;
  ^GOOD;
}
!!

/* 7/12/1992 14:13 - MENU
  Turn on/off display line number in the browsers.
*/
Def cmdViewShowLineNum(self, item)
{
  if displayLineNum := not(displayLineNum) then
    checkMenuItem(self, item);
  else
    unCheckMenuItem(self, item);
  endif;

  /* Switch the option for browsers - invalidate when done */
  setBrowserShowLineNum(self, browser, class(dataObject(browser)), #true); /* #true to shift the highlight */
  invalidate(browser);
  if split?(self) cand browser2 then
    setBrowserShowLineNum(self, browser2, class(dataObject(browser2)), #true); /* #true to shift the highlight */
    invalidate(browser2);
  endif;

  ^GOOD;
}
!!

/* 06/20/92 - Actor
  Commands dispatcher
  NOTES: Nghia - 10/12/93
    Clear ESC key before processing command.
*/
Def command(self, wp, lp | msg)
{

  /* KeyBoard keys */
  select
    case wp == EDIT_PRIOR is
      ^WM_VSCROLL(selectedWin, SB_PAGEUP, 0);
    endCase
    case wp == EDIT_NEXT is
      ^WM_VSCROLL(selectedWin, SB_PAGEDOWN, 0);
    endCase
  endSelect;

  /* 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, msg);
    /* Set focus to Source Presenter again to let Function keys work */
    /* JOHN - make the Function keys as Accelerator - then remove this */
    ^setFocus(self);
  endif;

  /* Process Windows menu commands */
  if not(doAction(PreLauncher, wp)) then
    command(self:ancestor, lp, wp);
  endif;
}
!!

/* PRIVATE
   If location for popup is too close to bottom of screen, show it
   up higher.  aPoint is in Screen coordinates.
*/
Def decidePopLocation(self, aPoint | screenSizePt)
{
  screenSizePt := screenSize() ;
  if (y(aPoint) > ((y(screenSizePt) * 2) / 3) ) then
    ^point( x(aPoint), min(y(aPoint), (y(aPoint)-(tmHeight * 4)))); /* Above the selection */
  else
    /* 20 shoule be tmHeight * 2 */
    ^point( x(aPoint), y(aPoint)+(tmHeight * 2));  /* Below the selection */
  endif ;
}
!!

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

/* 5/8/1992 10:47 - PRIVATE
  Enable all specified menu items.
  EX: enableMenuItems(SourcePresenter, #(SRC_FILE_MODULES, ...));
*/
Def enableMenuItems(self, menuCmdSet)
{
  if menuCmdSet then
    do(menuCmdSet,
      {using(item)
        enableMenuItem(menu, item);
      });
  endif;
}
!!

/* 06/20/92 - PRIVATE
  Reponds to a temporary breakpoint hit. Get the last break cause and id to
  clear the temporary break both the server and presenter.
 */
Def eventBreakpointChanged(self | otherBrowser)
{
  /* Rebuild the breakpoint dictionary for visible browser */
  if selectedWin then
    rebuildBkptDict(dataObject(selectedWin));
    /* Check for breakpoint commands against the selectedWin */
    getBkptsAndCheck(self, selectedWin);
    invalidate(selectedWin); /* Wipe out the old or paint the new ones */
  endif;

  /* Update the other browser if one is present */
  if (otherBrowser := getOtherBrowser(self)) then
    rebuildBkptDict(dataObject(otherBrowser));
    invalidate(otherBrowser);
  endif;
  ^GOOD;
}
!!

/* 06/20/92 - EVENT RESPOND
  if CPU's PC register has changed, show corresponding source at
  PC location in selectedWin.
*/
Def eventCPUChanged(self | newPC, otherBrowser)
{
  /* Display data text at the current PC position */
  showWaitCurs();
  requireWithPath( CpuLib, "cpu.dll");
  if (newPC := getProgramCounter(CpuLibClass$Inst)) then
    /* Update the browser, but do not save the history */
    showFileFromAddress(self, newPC, selectedWin, nil, #true);
  endif;
  /* Update the other browser if one is present */
  if (otherBrowser := getOtherBrowser(self)) then      
    if getCurrentExecPoint(dataObject(otherBrowser)) then
      updatePC(otherBrowser);
    endif;  
  endif;
  showOldCurs();
}
!!

/* 8/18/1992 10:57 - PRIVATE
  Responding to event memory changed in the code region of the
  browser1.
  NOTES: selectedWin can be browser or browser2.
*/
Def eventMemSource1Changed(self | virtDataObj)
{
  /* If the current data object is not a dasm related object then do nothing */
  if not(browser) cor class(virtDataObj := dataObject(browser)) = VirtSmartFileObject then
    ^GOOD;
  endif;
  /* Recompute all dasm text */
  invalidateCache(browser);
}
!!

/* 8/18/1992 10:57 - PRIVATE
  Responding to event memory changed in the code region of browser2.
    NOTES: selectedWin can be browser or browser2.
*/
Def eventMemSource2Changed(self | virtDataObj)
{
  /* If the current data object is not a dasm related object then do nothing */
  if not(browser2) cor class(virtDataObj := dataObject(browser2)) = VirtSmartFileObject then
    ^GOOD;
  endif;
  /* Recompute all dasm text */
  invalidateCache(browser2);
}
!!

/* 3/26/1992 16:53 - PRIVATE
  Return the server's execution granularity attribute.
*/
Def execGran(self)
{
  select
    case execGran = SRC_OPTN_STEP_LINE_GRAN
      ^HLB_LINE_GRANULAR;
    endCase
    case execGran = SRC_OPTN_STEP_STMT_GRAN
      ^HLB_STATEMENT_GRANULAR;
    endCase
  endSelect;
}
!!

/* 7/22/1992 11:11 - PRIVATE
  Search for the input token in the specified browser and show it.
*/
Def findTokenAndShow(self, tokenSearch, theBrowser, goFromTop | newLoc)
{
  newLoc := 0; /* Start from line 0 from top */
  /* NOTES: findAndShowString() returns nil if found, else return the last line searched */
  if not(goFromTop) cand
    not(newLoc := findAndShowString(theBrowser, tokenSearch)) then
    ^GOOD;
  endif;
  /* Search for the token in the data object begin at newLoc */
  if (newLoc := searchText(dataObject(theBrowser),
       tokenSearch, newLoc+1, getTxtLineRoutine(theBrowser))) then
    /* Found it - show the location */
    showTokenAt(theBrowser, newLoc);
    ^GOOD;
  endif;
  ^nil;
}
!!

/* 3/29/1992 11:47 - PRIVATE
  Get the symbolic information of the input address descriptor -
  Return tuple: #(moduleDesc, lineNum, colNum) or nil.
  NOTES:
    - This method does not destroy the input <addrDesc>.  
*/
Def getAddrInfo(self, addrDesc | addrInfo)
{
  /* Get information about the address */
  addrInfo := addr2lineAndModule(SymbolLibClass$Inst, addrDesc);
  if (addrInfo) then
     /* NOTES: addrInfo = #(moduleDesc, moduleAddrDesc, lineNum, colNum, lineAddrDesc, lineNumDesc) */
     /* destroy the unused address descriptors */
     destroyAddress(AddressLibClass$Inst, addrInfo[1]); 
     destroyAddress(AddressLibClass$Inst, addrInfo[4]);
     /* RETURN: addrInfo = #(moduleDesc, lineNum, colNum) */
     ^tuple(addrInfo[0], addrInfo[2], addrInfo[3]);
  else
    /* NOTES: addrInfo = moduleDesc */
    if (addrInfo := addr2Module(SymbolLibClass$Inst, addrDesc)) then
      ^tuple(addrInfo, 1, 0)
    endif;    
  endif;
  ^nil;
}
!!

/* 8/4/1992 13:51 - PRIVATE
  Check to see if the requested module has any line number information for the
  mixed mode display.
  Notes: Check no mem leaking.
*/
Def getDataTypeOfModule(self, moduleDesc | lineInfo)
{
  /* Heavy modification, to take into account assembly modules. */
  /* now can return VirtSmrtDasmObject if an assembly module */
  /* First, check symbolic info; then, check view mode */
  /* NOTES: lineInfo = #(addrDesc, lineNum, colNum, lineNumDesc) */
  lineInfo := getLineNumInfo(SymbolLibClass$Inst, moduleDesc, 0);
  if (lineInfo) then
     destroyAddress(AddressLibClass$Inst, lineInfo[0]);
     if (viewMode = SRC_VIEW_SOURCE) then
        checkMenuItem(menu, SRC_VIEW_SOURCE);
        unCheckMenuItem(menu, SRC_VIEW_MIX);         
        ^VirtSmartFileObject;
     endif;
     if (viewMode = SRC_VIEW_MIX) then
        /* Get rid of the unused address descriptor */
        checkMenuItem(menu, SRC_VIEW_MIX);
        unCheckMenuItem(menu, SRC_VIEW_SOURCE);
        ^VirtSMixedObject;
     endif;
  endif;
  /* @@@Defer data module handling for now@@@ */
  /* Data modules should ultimately be viewed in source mode,
  ** they don't have line numbers but the address range is 0.  Nghia
  ** feels they should be a new type of object, due to the differences
  ** (no line numbers is a big change).
  */
  if (viewMode = SRC_VIEW_MIX) then
    viewMode = SRC_VIEW_SOURCE;
    unCheckMenuItem(menu, SRC_VIEW_MIX);
    checkMenuItem(menu, SRC_VIEW_SOURCE);
  endif;
  ^VirtSmrtDasmObject;
}
!!

/* 4/6/1992 10:38 - PRIVATE
  get an address offset of the line# in a module
*/
Def getLineOffset(self, lineNum, moduleDesc | lineInfo, addrOffset)
{
  addrOffset := nil;
  if (lineInfo := getLineNumInfo(SymbolLibClass$Inst, moduleDesc, lineNum)) then
    /* NOTES: lineInfo = #(addrDesc, lineNum, colNum, NextIndex) */
    addrOffset := getOffset(AddressLibClass$Inst, lineInfo[0]);
    /* destroy the unused address descriptor */
    destroyAddress(AddressLibClass$Inst, lineInfo[0]);
  endif;
  ^addrOffset;
}
!!

/* 7/23/1992 10:35 - PRIVATE
  Return the unselected Browser, or nil.
*/
Def getOtherBrowser(self)
{
  if split?(self) then
    if selectedWin <> browser then
      ^browser;
    else
      ^browser2;
    endif;
  endif;
  ^nil;
}
!!

/* 06/20/92 - PRIVATE
   Draw thick border around selected browser.
*/
Def hilightSelectedWindow(self | cRect, hWnd, hDC, hPen, origPt, offsPt, newBrush, oldBrush)
{
  hWnd := hWnd(self);
  cRect  := windowRect(selectedWin) ; /* screen coords */
  origPt := screenToClient(self, point(left(cRect)-1,  top(cRect)-1));
  offsPt := screenToClient(self, point(right(cRect), bottom(cRect)+1));
  hDC := Call GetDC(hWnd);
  newBrush := Call CreateSolidBrush(RGB_PURE_RED bitOr RGB_PURE_GREEN);
  oldBrush := Call SelectObject(hDC, newBrush);
  hPen := Call CreatePen(PS_SOLID,3,0);
  Call SelectObject(hDC, hPen);
  /* Draw 3-sided rectangle */                       /* 17 Offset the split button */
  line(origPt, point(x(offsPt)-17, y(origPt)), hDC); /* +---------- */
  line(origPt, point(x(origPt),y(offsPt)), hDC);     /* |           */
  line(point(x(origPt),y(offsPt)), offsPt, hDC);     /* +---------- */

  Call SelectObject(hDC, oldBrush);
  Call DeleteObject(newBrush);
  Call ReleaseDC(hWnd, hDC);
  Call DeleteObject(hPen);
}
!!

/* 6/29/1992 13:53 - PRIVATE
  Initialize a Source Presenter instance.
*/
Def init(self)
{
  showWaitCurs();
  init(self:ancestor);
  initTextMetrics(self);
  initMainMenu(self);
  initSelf(self);    /* Initialize all self instance variables and Menu commands */
  initExecBar(self);
  initCmds(self);
  if not(initChildObj(self)) then    /* includes splitter area and child controls */
    ^nil; /* failed to open browser, so bail out */
  endif;
  if not(initEvents(self)) then
    displayFormattedError(ErrorTextLibClass$Inst,
       ER_SRC_EVENT_REG, FORCE_POPUP, nil, nil, nil);
    ^nil;
  endif;
  setFocus(self);
  registerF1Help(CLIULibraryClass$Inst, HI_ENTRY_SOURCE,
     getHWnd(self), HELP_ENTRY_SOURCE);
  showOldCurs();
}
!!

/* 06/20/92 - PRIVATE
   Execution Buttons & window splitter area
*/
Def initChildObj(self | cr, virtDataObj, newPC, showAddr, addrRange )
{
  cr := clientRect( self ) ;
  /*
  ** Open a new data object and browser. If failed return nil, do not open the
  ** Source Presenter. (DO NOT OPEN A BLANK WINDOW. GUI laws.)
  */
  /* If the Trace is opened and LC is ON and LC is good, open the source at LC */
  if not(TheTracePresenter cand TheCursorIsLinked?) cor
    not(newPC := getCurrentLCAddress(TheCursorLinkServer)) then             
    /* Open at current PC */
    if not(newPC := getProgramCounter(CpuLibClass$Inst)) cor
        not(showAddr := duplicateAddress(AddressLibClass$Inst, newPC)) cor
        not(maskAddressMSB(AddressLibClass$Inst, newPC)) then
      if newPC then destroyAddress(AddressLibClass$Inst, newPC); endif;
      if showAddr then destroyAddress(AddressLibClass$Inst, showAddr); endif;
      ^reportFailedToOpenSource(self); 
    else
      if not(virtDataObj := openDataObject(self, newPC)) cor
        not(browser := openBrowser(self, virtDataObj,            /* virtDataObj */
        rect( left(cr), top(cr)+18, right(cr), bottom(cr) ))) cor  /* browserRect */
        not(showFileFromAddress(self, showAddr, selectedWin, #true, #true)) then
        ^reportFailedToOpenSource(self); 
      endif;
    endif;
  else
    /* Open Source at LC address */
    if not(newPC) cor 
      not(showAddr := duplicateAddress(AddressLibClass$Inst, newPC)) cor
      not(maskAddressMSB(AddressLibClass$Inst, newPC)) then
      if newPC then destroyAddress(AddressLibClass$Inst, newPC); endif;
      if showAddr then destroyAddress(AddressLibClass$Inst, showAddr); endif;
      ^reportFailedToOpenSource(self);
    else
      if not(virtDataObj := openDataObject(self, newPC)) cor
         not(browser := openBrowser(self, virtDataObj,            /* virtDataObj */
           rect(left(cr), top(cr)+18, right(cr), bottom(cr) ))) cor  /* browserRect */
         not(showLinkedFromAddress(self, showAddr, browser)) then
        ^reportFailedToOpenSource(self); 
      endif;
    endif;    
  endif;           

  /* Register Memory Changed event notification - Server destroy addressRange */
  if not(addrRange := addressRange(virtDataObj))
    ^reportFailedToOpenSource(self); 
  endif;   
  setSourceMemoryRange(MemServLibClass$Inst, addrRange, MEM_SOURCE1);

  /* Split Button - 17 = width */
  splitArea := new( SplitRegion, self, nil, "split area",
                       rect(right(cr)-17, 12, right(cr), 18));
  setCRect(splitArea, rect(right(cr)-17, 12, right(cr), 18));
  setYadjust(splitArea, 12);  /* 12 is top of splitArea */
  /* Now resize and position them in the main window */
  reSizeChilds(self);
}
!!

/* 8/7/1992 16:39 - PRIVATE
  Init menu commands according to the profile controlk or default value.
*/
Def initCmds(self)
{
  /* set default menu items - initSelf() initialized these values */
  checkMenuItems(self, tuple(
      goType,
      execGran,
      viewMode));
  /* Change the Button text and menu enable */
  setGoButtonCmds(self);
  /* Set display LineNumber */
  if displayLineNum then
    checkMenuItem(self, SRC_VIEW_SHOW_LINENUM);
  endif;
  /* Set Source Line Delimiter */
  if sourceDelimiter then
    checkMenuItem(self, SRC_OPTN_CRLF_DELIMITER);
  else
    checkMenuItem(self, SRC_OPTN_LF_DELIMITER);
  endif;
  drawMenu(self); /* Update Menu */
}
!!

/* 06/20/92 - PRIVATE */
Def initEvents(self | events)
{
  /* REGISTER with Event Notification for
   *       Breakpoint & Execution controls,
   *       Cursor Link update,
   *       New PC register
   *       Whatever...
   */

  eventRespSet := new(Dictionary, 6);

  /* dynamic to pick up current values */
  events := tuple(
                  EVENT_HL_BKPTEXEC_EMUL_STARTED,
                  EVENT_HL_BKPTEXEC_EMUL_STEPPING,
                  EVENT_MEM_MEMORY_CHANGED_SRC0_RANGE,
                  EVENT_MEM_MEMORY_CHANGED_SRC1_RANGE,
                  EVENT_BKPT_EDIT,
                  EVENT_CPU_PC_EDIT,
                  EVENT_DASM_HALTED,
                  EVENT_DASM_SYM_CHANGED
                 );
  /* Setup events respond messages */
  add(eventRespSet, EVENT_HL_BKPTEXEC_EMUL_STARTED,      #eventExecutionStart);
  add(eventRespSet, EVENT_HL_BKPTEXEC_EMUL_STEPPING,     #eventExecutionStart);
  add(eventRespSet, EVENT_BKPT_EDIT,                     #eventBreakpointChanged);
  add(eventRespSet, EVENT_CPU_PC_EDIT,                   #eventCPUChanged);
  add(eventRespSet, EVENT_DASM_HALTED,                   #eventCPUChanged);
  add(eventRespSet, EVENT_DASM_SYM_CHANGED,              #eventDasmSymChanged);
  add(eventRespSet, EVENT_MEM_MEMORY_CHANGED_SRC0_RANGE, #eventMemSource1Changed);
  add(eventRespSet, EVENT_MEM_MEMORY_CHANGED_SRC1_RANGE, #eventMemSource2Changed);

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

/* 06/20/92 - PRIVATE
  Initialize the main menu of the Source Presenter.
*/
Def initMainMenu(self)
{
  menu := create(new(Menu), self);
  addItem(menu, initMenuFile(self));
  addItem(menu, initMenuEdit(self));
  addItem(menu, initMenuView(self));
  addItem(menu, initMenuRun(self));
  addItem(menu, initMenuBkpt(self));
  addItem(menu, initMenuOption(self));
  /* Append Windows menu item */
  Call AppendMenu(handle(menu), MF_POPUP, windowsMenu(PreLauncher), asciiz("&Windows"));

  addItem(menu, initMenuHelp(self));

  /* Gray item not operate-able */
  disableMenuItems(self, tuple(SRC_FILE_BACK,
                              SRC_FILE_FORWARD));

  /* Disable commands operate on symbols if there is no loadfile loaded */
  if not(isLoaded(currentLoadFile(TheProjectInfoObj))) cor
      (numLoadedModules(currentLoadFile(TheProjectInfoObj)) = 0) then
    invalidateSourceCommands(self);
  endif;
  drawMenu(self);
}
!!

/* 06/20/92 - PRIVATE
   Initialize the main window of the Presenter within the screen
*/
Def initMainWindow(self | x, y)
{
  /* Set its location and size */
  x := x(screenSize()); y := y(screenSize());
  if x > 640 then /* Screen resolution is greater than 640x480 */
    x := asInt((x - 650) / 2);  /* Width of window */
    y := asInt((y - 500) / 2);  /* Height of window */
    setCRect(self, rect(x, y, x+650, y+500));
  else
    x := asInt((x - 600) / 2);  /* Width of window */
    y := asInt((y - 400) / 2);  /* Height of window */
    setCRect(self, rect(x, y, x+600, y+400));
  endif;
  moveWindow(self);
}!!

/* 3/26/1992 15:47 - PRIVATE */
Def initMenuBkpt(self | aMenu, aSubMenu)
{
  /* BREAKPOINTS menu */
  aMenu := newPopup(MenuItem, "&Breakpoints");
  addItem(aMenu, new(MenuItem, "Set &Permanent Breakpoint",
    SRC_BKPT_PERM_SET_BREAK, #cmdBkptSet));
  addItem(aMenu, new(MenuItem, "Set &Temporary Breakpoint",
    SRC_BKPT_TEMP_SET_BREAK, #cmdBkptSet));
  addItem(aMenu, new(MenuItem, "Set &Breakpoint...",
    SRC_BKPT_SET_BREAK_AT, #cmdBkptSetAt));
  addItem(aMenu, nil);
  addItem(aMenu, new(MenuItem, "&Clear",
    SRC_BKPT_CLR_BKPT, #cmdBkptClear));
  addItem(aMenu, new(MenuItem, "&Enable",
    SRC_BKPT_ENABLE_BKPT, #cmdBkptEnable));
  addItem(aMenu, new(MenuItem, "&Disable",
    SRC_BKPT_DISABLE_BKPT, #cmdBkptDisable));
  addItem(aMenu, nil);
  addItem(aMenu, new(MenuItem, "Clear &All",
    SRC_BKPT_CLEAR_ALL, #cmdBkptClearAll));
  addItem(aMenu, new(MenuItem, "E&nable All",
    SRC_BKPT_ENABLE_ALL, #cmdBkptSetStateAll));
  addItem(aMenu, new(MenuItem, "D&isable All",
    SRC_BKPT_DISABLE_ALL, #cmdBkptSetStateAll));
  addItem(aMenu, nil);
  addItem(aMenu, new(MenuItem, "&Show All...",
    SRC_BKPT_SHOW_BKPTS, #cmdBkptShowAll));

  ^aMenu;
}
!!

/* 3/26/1992 15:45 - PRIVATE */
Def initMenuEdit(self | aMenu)
{
  /* EDIT menu */
  aMenu := newPopup(MenuItem, "&Edit");
  addItem(aMenu, new(MenuItem, "&Search...",
    SRC_EDIT_SEARCH, #cmdEditTextSearch));
  addItem(aMenu, new(MenuItem, "Search &Next",
    SRC_EDIT_SEARCH_AGAIN, #cmdEditSearchAgain));
  addItem(aMenu, nil);
  addItem(aMenu, new(MenuItem, "Go To &Line...",
    SRC_EDIT_GOTO_LINE, #cmdEditGotoLine));
  addItem(aMenu, new(MenuItem, "Go To &Address...",
    SRC_EDIT_GOTO_ADDR, #cmdEditGotoAddress));
  addItem(aMenu, new(MenuItem, "Go To &PC",
    SRC_EDIT_GOTO_PC, #cmdEditGotoPC));
  ^aMenu;
}
!!

/* 3/26/1992 15:41 - PRIVATE */
Def initMenuFile(self| aMenu, i, menuStr, strLen, lastLoadFiles)
{
  /* FILE menu */
  aMenu := newPopup(MenuItem, "&File");
  addItem(aMenu, new(MenuItem, "&Load Code...",
    SRC_FILE_OPEN, #cmdFileOpen));
  addItem(aMenu, new(MenuItem, "Load &Information...",
    SRC_FILE_LOAD_INFO, #cmdFileLoadInfo));
  addItem(aMenu, nil);
  addItem(aMenu, new(MenuItem, "&Browse Modules...",
    SRC_FILE_MODULES, #cmdFileViewModules));
  addItem(aMenu, new(MenuItem, "&Previous Browsed Module",
    SRC_FILE_BACK, #cmdFilePrevious));
  addItem(aMenu, new(MenuItem, "&Next Browsed Module",
    SRC_FILE_FORWARD, #cmdFileNext));
  addItem(aMenu, nil);
  addItem(aMenu, new(MenuItem, "E&xit",
    SRC_FILE_EXIT, #cmdFileExit));
  /* Append the last loadfiles to the File menu */
  i := 1;
  if (lastLoadFiles := lastLoadFiles(TheProjectInfoObj))
        cand size(lastLoadFiles) > 0 then
    addItem(aMenu, nil); /* Separator */
    do (lastLoadFiles,
      {using(element | menuStr, strLen)
        if ((strLen := size(element)) > 25)
           menuStr := " ..."+subString(element, strLen-25, strLen);
           menuStr := "&"+asString(i)+menuStr;
        else
          menuStr := "&"+asString(i)+" "+asString(element);
        endif;
        addItem(aMenu, new(MenuItem, menuStr,
            SRC_FILE_EXIT+i, #cmdFileLoadLastFile));
        i := i + 1;
      });
  endif;
  /* 
  ** NOTES: Initialize action for the last loadfile command, although
  ** they might not present (no menu item to display). 
  ** Start from whatever i was left with - stop at j = 5.
  */
  do (overBy(i, 5, 1),
    {using(j) 
      setAction(menu, SRC_FILE_EXIT+j, #cmdFileLoadLastFile);     
    });
  ^aMenu;
}
!!

/* 6/3/1992 15:25 - 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 &Source",
    ABOUT_HELP, #cmdHelp));
  ^aMenu;
}
!!

/* 3/26/1992 15:51 - PRIVATE */
Def initMenuOption(self | aMenu, aSubMenu )
{
  /* OPTIONS menu */
  aMenu := newPopup(MenuItem, "&Options");
  addItem(aMenu, new(MenuItem, "Source &Path...",
    SRC_OPTN_PATH_CONV,  #cmdOptnSourcePath));
  addItem(aMenu, new(MenuItem, "&Tab Width...",
    SRC_OPTN_TAB_WIDTH, #cmdOptnTabWidth));
  addItem(aMenu, nil);
  aSubMenu := newPopup(MenuItem, "Source Step &Granularity");
    addItem(aSubMenu, new(MenuItem, "Source &Line",
      SRC_OPTN_STEP_LINE_GRAN, #cmdOptnExecGran));
    addItem(aSubMenu, new(MenuItem, "Source &Statement",
      SRC_OPTN_STEP_STMT_GRAN, #cmdOptnExecGran));
  addItem(aMenu, aSubMenu);
  addItem(aMenu, new(MenuItem, "Step &Count...",
      SRC_OPTN_STEP_COUNT, #cmdOptnStepCount));
  addItem(aMenu, nil);
  addItem(aMenu, new(MenuItem, "&Browser History Depth...",
    SRC_OPTN_HIST_DEP, #cmdOptnHistoryDepth));
  addItem(aMenu, nil);
  aSubMenu := newPopup(MenuItem, "Source Line &Delimiter");
    addItem(aSubMenu, new(MenuItem, "&Carriage Return/Linefeed",
      SRC_OPTN_CRLF_DELIMITER, #cmdOptnSourceLineDelimiter));
    addItem(aSubMenu, new(MenuItem, "&Linefeed Only",
      SRC_OPTN_LF_DELIMITER, #cmdOptnSourceLineDelimiter));
  addItem(aMenu, aSubMenu);
  addItem(aMenu, nil);
  aSubMenu := newPopup(MenuItem, "&Set Go Buttons");
    addItem(aSubMenu, new(MenuItem, "&Until Call/Return",
      SRC_OPTN_EXEC_GO_UNTIL, #cmdOptnGoType));
    addItem(aSubMenu, new(MenuItem, "&Into Call/Return",
      SRC_OPTN_EXEC_GO_INTO, #cmdOptnGoType));
  addItem(aMenu, aSubMenu);
  addItem(aMenu, nil);
  addItem(aMenu, new(MenuItem, "Compiler &Used...",
    SRC_OPTN_COMPILER, #cmdOptnCompiler));
  ^aMenu;
}
!!

/* 3/26/1992 15:43 - PRIVATE */
Def initMenuRun(self | aMenu)
{
  /* RUN menu */
  aMenu := newPopup(MenuItem, "&Run");
  addItem(aMenu, new(MenuItem, "&Go"+asString(Tab)+"F9",
    SRC_CMD_GO, #actionGo));
  addItem(aMenu, new(MenuItem, "&Halt"+asString(Tab)+"F2",
    SRC_CMD_HALT, #actionHalt));
  addItem(aMenu, new(MenuItem, "&Step Into"+asString(Tab)+"F7",
    SRC_CMD_STEP_INTO, #actionStep));
  addItem(aMenu, new(MenuItem, "Step &Over"+asString(Tab)+"F8",
    SRC_CMD_STEP_OVER, #actionStepOver));
  addItem(aMenu, nil);
  addItem(aMenu, new(MenuItem, "Go Until &Call",
    SRC_CMD_UNTILCALL, #cmdStepToCall));
  addItem(aMenu, new(MenuItem, "Go Until Ret&urn",
    SRC_CMD_TILRETURN, #cmdStepToReturn));
  addItem(aMenu, new(MenuItem, "Go &Into Call",
    SRC_CMD_INTOCALL, #cmdStepToCall));
  addItem(aMenu, new(MenuItem, "Go Into &Return",
    SRC_CMD_INTORETURN, #cmdStepToReturn));
  addItem(aMenu, nil);
  addItem(aMenu, new(MenuItem, "Go&to Cursor",
    SRC_CMD_GO_TOCURSOR, #actionGoToCursor));
  addItem(aMenu, new(MenuItem, "Go &From Cursor",
    SRC_CMD_GO_FROMCURSOR, #actionGoFromCursor));
  addItem(aMenu, nil);
  addItem(aMenu, new(MenuItem, "Ste&p Into Continuously",
    SRC_CMD_ANIMATE_INTO, #actionAnimating));
  addItem(aMenu, new(MenuItem, "Step O&ver Continuously",
    SRC_CMD_ANIMATE_OVER, #actionAnimatingOver));
  addItem(aMenu, nil);
  addItem(aMenu, new(MenuItem, "R&eset",
    SRC_CMD_RESET, #actionReset));
  if not(micepack?(ProcLibClass$Inst)) then
      addItem(aMenu, new(MenuItem, "Reset A&nd Go",
        SRC_CMD_RESET_AND_GO, #actionResetAndGo));
  endif;
  ^aMenu;

}
!!

/* 3/26/1992 15:43 - PRIVATE */
Def initMenuView(self | aMenu)
{
  /* VIEW menu */
  aMenu := newPopup(MenuItem, "&View");
  addItem(aMenu, new(MenuItem, "&Source Only",
      SRC_VIEW_SOURCE, #cmdViewMode));
  addItem(aMenu, new(MenuItem, "&Mixed Source and Asm",
      SRC_VIEW_MIX, #cmdViewMode));
  addItem(aMenu, nil);
  addItem(aMenu, new(MenuItem, "Line &Numbers",
      SRC_VIEW_SHOW_LINENUM, #cmdViewShowLineNum));
  ^aMenu;
}
!!

/* 6/3/1992 15:25 */
Def initMenuWindow(self | aMenu)
{
  /* HELP menu */
  aMenu := newPopup(MenuItem, "&Windows");
  ^aMenu;
}
!!

/* 6/2/1992 9:53 - PRIVATE *
  Initialize all self instance variables with the
  saved ProfileControl from POWERVIEW.INI file.
*/
Def initSelf(self | iniVal, appName)
{
  TheSourcePresenter := self;
  appName := "SourceInfo";

  /* Call getIniValue to do the dirty work */
  if (iniVal := getIniValueNum(TheProfileInfoObj, appName, "TabWidth")) cand between(iniVal, 1, 32) then
    tabWidth := iniVal;
  else
    tabWidth := 8;    /* tab width by default */
  endif;

  if (iniVal := getIniValueNum(TheProfileInfoObj, appName, "DisplayLineNum")) cand
    between(iniVal, 0, 1) then
    displayLineNum := (iniVal <> 0);
  else
    /* default do not display Line number */
    displayLineNum := nil;
  endif;

  if (iniVal := getIniValueNum(TheProfileInfoObj, appName, "StepCount")) then
    stepCount := iniVal;
  else
    stepCount := 1;    /* 1 single step by default */
  endif;

  if (iniVal := getIniValueNum(TheProfileInfoObj, appName, "ViewSource")) cand
    between(iniVal, 0, 1) then
    if (iniVal <> 0) then
      viewMode := SRC_VIEW_SOURCE;
    else
      viewMode := SRC_VIEW_MIX;
    endif;
  else
    viewMode := SRC_VIEW_SOURCE; /* default view Source */
  endif;

  if (iniVal := getIniValueNum(TheProfileInfoObj, appName, "UseGoInto")) cand
    between(iniVal, 0, 1) then
    if (iniVal <> 0)
      goType := SRC_OPTN_EXEC_GO_INTO;
    else
      goType := SRC_OPTN_EXEC_GO_UNTIL;
    endif;
  else
    goType := SRC_OPTN_EXEC_GO_INTO;
  endif;

  if (iniVal := getIniValueNum(TheProfileInfoObj, appName, "UseLineExecGranularity")) cand
    between(iniVal, 0, 1) then
    if (iniVal <> 0) then
      execGran := SRC_OPTN_STEP_LINE_GRAN;
    else
      execGran := SRC_OPTN_STEP_STMT_GRAN;
    endif;
  else
    execGran := SRC_OPTN_STEP_LINE_GRAN;
  endif;

  if (iniVal := getIniValueNum(TheProfileInfoObj, appName, "HistoryDepth")) cand
    between(iniVal, 5, 100) then
    maxDepth := iniVal;
  else
    maxDepth := 30;
  endif;

  if (iniVal := getIniValueNum(TheProfileInfoObj, appName, "SourceDelimiterUseCRLF")) cand
    between(iniVal, 0, 1) then
    sourceDelimiter := (iniVal <> 0);
  else
    sourceDelimiter := #true; /* default is CR_LF */
  endif;

  /* Set History Stack */
  backwardFile    := new(HistoryStack, maxDepth);
  setMaxDepth(backwardFile, maxDepth);

  /* There is no profile control values for these */
  splitPos        := 0 ;  /* start with only 1 browser */
  prevCRect       := clientRect(self);
  animating       := nil;
  /* Register with CursorLinkServer */
  register(TheCursorLinkServer, self);
}
!!

/* 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);
}!!

/* 7/20/1992 15:31 - PUBLIC
  Disable all symbolic commands of the Source Presenter.
*/
Def invalidateSourceCommands(self)
{
  disableMenuItems(self, tuple(SRC_FILE_LOAD_INFO,
                              SRC_FILE_MODULES,
                              SRC_VIEW_SHOW_LINENUM,
                              SRC_EDIT_GOTO_LINE));
}
!!

/* 06/20/92 - PRIVATE
   close popup window if opened.
*/
Def maybeClosePopup(self)
{
  if (popWindow <> nil) then
    close(popWindow);
    popWindow := nil ;
  endif;
}!!

/* 7/21/1992 10:44 - PUBLIC
  Respond to the Cursor Linked event, only the selectedWin is effected.
  If addrDesc then the current LC will be blanked out.
  NOTES: consume the addrDesc when done.
*/
Def newPC(self, addrDesc)
{
  showWaitCurs();
  /* Set the linked cursor at the center of the selectedWin */
  if selectedWin then
    if (addrDesc) then
      showLinkedFromAddress(self, addrDesc, selectedWin);
    else 
      setCursorLinked(selectedWin, nil);
      invalidate(selectedWin);
    endif  
  endif;
  showOldCurs();
  ^GOOD;
}
!!

/* 06/20/92 - PRIVATE (PUBLIC to child)
  Open 2 browser if split position is > its top value;
  else close an unused browser
*/
Def newSplit(self, newYpos | cr, tmpBr)
{
  /* only 1 window shown if < 4 lines remain */
  maybeClosePopup(self);  
  cr := clientRect( self ) ;
  if (newYpos < (4 * tmHeight)) cand split?(self) cand browser2 then
    /* Bottom window only is shown -- swap with Top */
    newYpos := 0 ;
    tmpBr := browser2 ;
    browser2 := browser ;
    browser := tmpBr ;
    show(browser2, SW_HIDE);
    setSelectedWin(self, browser);
  else if (max((bottom(cr) - newYpos), 1) < (4 * tmHeight))
    then /* Top window only is shown */
      newYpos := 0 ;
      if browser2 then
        show(browser2, SW_HIDE);
      endif ;
      setSelectedWin(self, browser);
    endif ;
  endif ;
  splitPos := newYpos ;
  reSizeChilds(self);
  showTitle(self);
  invalidate(self);
}
!!

/* 6/2/1992 9:49 - PRIVATE
  Open a browser to display data of a dataObj (generic to browser).
  and Set the selectedWin of the SourcePresenter to it.
  EX:
    To display dasm information
      openBrowser(self, VirtSmrtDasmObject, rect);
    To display source information
      openBrowser(self, VirtSmartFileObject, rect);
    To display mixed source and dasm.
      openBrowser(self, VirtSMixedObject, rect);
*/
Def openBrowser(self, dataObj, browserRect | aBrowser, showLine)
{
  /* Open a new Source Browser */
  aBrowser := new(SourceCB,
                 self,
                 browserRect,           /* bottom(cr)-15 if horizScrollBar */
                 (WS_BORDER bitOr WS_CHILD bitOr WS_VSCROLL bitOr WS_HSCROLL), /* style */
                 dataObj,               /* Virtual data object */
                 numLinesIn(dataObj),   /* totalLines */
                 1);                    /* firstLine - show line #1 */
  /* Reset language name for token parsing */
  setLanguageName(aBrowser, languageName(dataObj));

  /* Set the getTxtLineRoutine for the open browser */
  setBrowserShowLineNum(self, aBrowser, class(dataObj), nil);

  /* Check for the CurrentExecPoint to show */
  if (class(dataObj) <> VirtSmrtDasmObject) cand
    (showLine := getCurrentExecPoint(dataObj)) then
    /* Remap data object line number to a virtual line number */
    showLine := getVirtLineNum(aBrowser, showLine);
    showTokenAt(aBrowser, tuple(showLine, 0)); /* startLine, startCol */
  else
    showTokenAt(aBrowser, tuple(1,0));  /* start at beginning */
  endif;

  /* Enable/Disable commands specific to data object type */
  if (class(dataObject(aBrowser)) = VirtSmrtDasmObject) then
    setCmdsForAsm(self);
  else
    setCmdsForSource(self);
  endif;
  /* Check breakpoints */
  rebuildBkptDict(dataObj);
  /* Check breakpoints to set menu commands */
  getBkptsAndCheck(self, aBrowser);
  /* Set selectedWin to be the new open browser */
  ^(selectedWin := aBrowser);
}
!!

/* 7/1/1992 13:57 - PRIVATE
  This method will intelligently open a data object for a browser.
  (NOTES: specific data object type is opened depend upond the global
  display mode and the availability of symbolic information of the address.)
  OPERATION:
    Mapping the input address descriptor to its equivalent symbolic information,
    if Symbolic Information is available then
      if display mode is SOURCE & ASM then.
        open a VirtSmartFileObject with the mapped module,
      else
        open a VirtSMixedObject, using the address range of the input address.
    else
      open a VirtSmrtDasmObject with the input address.

  return a Virtual Data Object.
  NOTES: This method will consume the input addressDesc, do not destroy the
    addrDesc after calling this routine with it.
*/
Def openDataObject(self, addrDesc | symInfo)
{
  /*
  ** If there's symbolic information available, and the addr space mode
  ** set by Loader is the same as the one getting from reg SR then the
  ** viewMode is considered,
  ** else only disassembly data, so the viewMode does not apply.
  */
  getSpaceMode(CpuLibClass$Inst);
  /* Winky 3/5/98 add isBoth flag */
  if (symInfo := addrHasSymbol?(self, addrDesc)) cand
     ((getSpaceMode(CpuLibClass$Inst) = ldrSpaceMode(LoaderLibClass$Inst)) cor isBoth = 1) then
    ^selectSymbolicDataObject(self, addrDesc, symInfo);
  else
    /* No Symbolic information available. Open DASM OBJECT  */
    ^selectDasmObject(self, addrDesc);
  endif;
}
!!

/* 7/21/1992 9:35 - PRIVATE
  Reset the input browser with an appropriate data object type (according to the
  Source Presenter View Mode).
    NOTES: openNewDataView does not save the oldData object into the history stack.
*/
Def openNewDataView(self, theBrowser | oldDataObj, dataRef, dataType, newDataObj,
  oldTokenLoc, oldSelect, oldLinked, lineInfo, showLine, ret)
{

  /* Only VirtSmartFileObject and VirtSMixed Object have effect from the view mode */
  if class(oldDataObj := dataObject(theBrowser)) = VirtSmrtDasmObject then
    ^nil;
  endif;

  /* Get the information of the oldDataObj and open a new data object type */
  dataRef     := moduleInfo(oldDataObj);
  oldTokenLoc := tokenLocation(theBrowser);
  oldSelect   := selectedLineText(theBrowser);
  oldLinked   := cursorLinked(theBrowser);
  /* Use the moduleInfo from the currentLoadFile of the ProjectInfo
     Check if the requested module has any linenumber information */
  if (dataType := getDataTypeOfModule(self, moduleDescriptor(dataRef)))
    = class(oldDataObj) then
    ^nil;  /* No new data type open, so return */
  endif;

  /* Open a new data type to view */
  if not(newDataObj := open(dataType, dataRef)) then
    displayFormattedError(ErrorTextLibClass$Inst,
       ER_SRC_OPEN_FAIL, FORCE_POPUP, nil, nil, nil);
    ^nil;
  endif;

  /* Reset the browser data object - NOTES: showLineNum does not need to be set again */
  setBrowserWithDataObj(self, theBrowser, newDataObj, nil, nil); /* nil - do not reregister mem server */
  /* Remap old linked cursor to new data type */
  if oldLinked then
    setCursorLinked(theBrowser, reMapLinkedCursor(self, dataType, oldDataObj, oldLinked));
  endif;
  /*
  ** When switch view, select line is consider first before the current ExecPoint
  ** Remap the old selectedLine to it new data object
  */
  if (oldTokenLoc cand (oldTokenLoc[0] > 0) cand (size(oldSelect) > 0)) then
    if dataType = VirtSMixedObject then
      /* Map the old source line number to its equivalent address */
      ret := reMapLineNumToDasmOffset(self, oldTokenLoc[0], oldDataObj, theBrowser);
    else
      /* Map the old mixed data line to its equivalent source Line # */
      ret := reMapMixedLineToSourceLine(self, oldSelect, theBrowser);
    endif;
    if not(ret) then
      displayFormattedError(ErrorTextLibClass$Inst,
         ER_SRC_CURSOR_MAP, FORCE_POPUP, nil, nil, nil);
      showTokenAt(theBrowser, tuple(0, 0)); /* startLine, startCol */
    endif;
  else
    /* Check for the CurrentExecPoint to show */
    if (showLine := getCurrentExecPoint(newDataObj)) then
      /* Remap data object line number to a virtual line number */
      showLine := getVirtLineNum(theBrowser, showLine);
    else
      showLine := 0;
    endif;
    showTokenAt(theBrowser, tuple(showLine, 0)); /* startLine, startCol */
  endif;
  oldSelect := nil; oldTokenLoc := nil;

  /* Replace the oldDataObj with its newDataObject type, then get rid of the oldDataObject */
  keysDo(backwardFile,
    {using(indx)
      if (backwardFile[indx] == oldDataObj) then
        backwardFile[indx] := newDataObj;
      endif;
    });
  /* Notes: 09/07/93 - Nghia
  ** Check for object type before destroying it
  */
  if generality(oldDataObj) < 0 then
    destroyDataObj(oldDataObj);
  endif;
}
!!

/* 06/20/92 - PRIVATE (PUBLIC to contained browsers)
   Invoke popup menu with possible operations on this symbol or line.
*/
Def popFunctionMenu(self, funName, aPoint, disableCmds | displayPt, title, len)
{
  maybeClosePopup( self ) ;  /* don't open 2 popup at once! */
  displayPt := decidePopLocation(self, aPoint);
  title := funName;
  if (len := size(funName)) > 25 then
    title := ".."+subString(funName, len-25, len);
  endif;
  popWindow := new( PopupMenu,
             "Function: " + title            , /* title */
             tuple(                            /* data */
               tuple( "&Go To Source        ",    SRC_POPUP_INSPECT,        #cmdPopFunSource      ),
               tuple( "&Show Load Address   ",    SRC_POPUP_SHOW_ADDR,      #cmdPopFunAddressShow ),
               tuple( "Set &Perm. Breakpoint",    SRC_POPUP_BKPT_LINE_PERM, #cmdBkptSet           ),
               tuple( "Set &Temp. Breakpoint",    SRC_POPUP_BKPT_LINE_TEMP, #cmdBkptSet           ),
               tuple( "&Clear Breakpoint    ",    SRC_POPUP_BKPT_CLEAR,     #cmdBkptClear         )
             ),
             self,                  /* parent */
             displayPt              /* where to put upper-left corner */
             );
  if (popWindow) then           
    /* Disable Command of the popup */
    if disableCmds then
      do (disableCmds,
        {using(menuId)
          grayMenuItem(popWindow, menuId);
        });
    endif;
    show(popWindow,1);
  endif;  
}
!!

/* PRIVATE (PUBLIC to contained browsers)
   Invoke popup menu with possible operations on this variable or line.
*/
Def popVariableMenu(self, varName, aPoint, disableCmds | displayPt, title, len)
{
  maybeClosePopup( self ) ;  /* don't open 2 popup at once */
  displayPt := decidePopLocation(self, aPoint);
  title := varName;
  if (len := size(varName)) > 25 then
    title := ".."+subString(varName, len-25, len);
  endif;
  popWindow := new( PopupMenu,
             "Variable: " + title,   /* title */
             tuple(                  /* data */
                   tuple( "&Inspect Variable    ", SRC_POPUP_INSPECT,        #cmdPopVarValueEdit ),
                   tuple( "Set &Perm. Breakpoint", SRC_POPUP_BKPT_LINE_PERM, #cmdBkptSet         ),
                   tuple( "Set &Temp. Breakpoint", SRC_POPUP_BKPT_LINE_TEMP, #cmdBkptSet         )
                  ),
             self,                  /* parent */
             displayPt              /* where to put upper-left corner */
           ) ;
  if(popWindow) then
    /* Disable Command of the popup */
    if disableCmds then
      do (disableCmds,
        {using(menuId)
          grayMenuItem(popWindow, menuId);
        });
    endif;
    show(popWindow,1);
  endif;  
}
!!

/* 6/11/92 - PRIVATE
   Resize: recalculate splitPos to try to keep %split ~ the same.
*/
Def recalcSplitPos(self, newCRect | oldHeight, newHeight, perCent, newYpos)
{
  if (splitPos = 0) then
    ^0  /* not split */
  endif ;

  oldHeight := height( prevCRect ) ;
  newHeight := height( newCRect  ) ;
  if (newHeight = oldHeight) then
    ^splitPos
  endif ;
  prevCRect := newCRect ; /* for next time */
  perCent   := asInt( (100 * splitPos) / oldHeight ) ;
  newYpos := asInt( (perCent * newHeight) / 100 ) ;
  /* invariant: need 3 lined to display in each window */

  if (newHeight < (8 * tmHeight)) then
    /* hide bottom window */
    if split?(self) then
       show( browser2, SW_HIDE );
       selectedWin := browser;
    endif ;
    ^(splitPos := newYpos := 0)
  endif ;

  if (newYpos < (3 * tmHeight)) then
    newYpos := (3 * tmHeight) ;  /* enlarge top window to 3 lines */
  else
    if (newHeight - newYpos) < (3 * tmHeight) then
      newYpos := (newHeight - (3 * tmHeight)) ; /* enlarge bottom window */
    endif ;
  endif ;
  showTitle( self ) ;

  ^(splitPos := newYpos)
}
!!

/* 7/22/1992 10:44 - PRIVATE
  Map the selected source line number to its equivalent address offset, then
  show the new location.
  Algorithm:
    - Map the old selected line number and module descriptor for its corresponded address.
    - Convert the address offset to a HEX text string.
    - Search the offset text string in the new data object.
    - Show the new location from searching.

  NOTES: theBrowser must have a VirtSMixedObject data object.
*/
Def reMapLineNumToDasmOffset(self, lineNum, oldDataObj, theBrowser |
  lineInfo, newOffset, lNum)
{
  /* Map line number to Address descriptor */
  if not(lineInfo := getLineNumInfo(SymbolLibClass$Inst, moduleDescriptor(oldDataObj),
      lineNum)) then
    ^nil;
  endif;

  /* NOTES: lineInfo = #(addrDesc, lineNum, colNum, lineNumDesc) */
  newOffset :=  getOffset(AddressLibClass$Inst, lineInfo[0]);
  /* Get rid of the unused address descriptor */
  destroyAddress(AddressLibClass$Inst, lineInfo[0]);
  if not(newOffset) then
    ^nil;
  endif;
  /* Get the text presentation of offset - it's must be HEX format */
  lNum := getVirtLineNum(theBrowser, newOffset);
  showLineAt(theBrowser, tuple(lNum, 0)); /* startLine, startCol */
  ^GOOD;  
}
!!

/* 7/22/1992 10:33 - PRIVATE
  Map a mixed data line to its equivalent source line number, then show
  the current selected line at its new position.
    NOTE: theBrowser must have a VirtSmartFileObject.
*/
Def reMapMixedLineToSourceLine(self, mixedSel, theBrowser |
  lineInfo, lineNum)
{
  lineNum := 1;
  /* Use the source line number of the mixeSel */
  if mixedSel cand (size(mixedSel) > 0) then
    /* Parse the line num out */
    lineNum := getLineNumFromSelStr(self, mixedSel);
  endif;
  /* Show the equivalent source line */
  showTokenAt(theBrowser, tuple(lineNum, 0));
  ^GOOD;
}!!

/* 4/28/1992 16:43 - PUBLIC (to its launcher) */
Def removeSourceSymbols(self | newPC, newDataObj, oldDataObj, 
otherBrowser, pcOffset)
{
  /* Disable all symbolic commands */
  invalidateSourceCommands(self);
  /* Gray item not operate-able */
  disableMenuItems(self, tuple(SRC_FILE_BACK,
                              SRC_FILE_FORWARD));
  /* Force the browser to dipslay disassembly of the current PC */
  requireWithPath(CpuLib, "cpu.dll");
  showWaitCurs();
  /* Reset the browser to the most current PC position with DASM data object */
  if ((newPC := setPCAndDasmRange(self))) cand
      (newDataObj := open(VirtSmrtDasmObject, newPC)) then
    /* Set browser with new data object, nil = not saveHistory to destroy the old one */
    oldDataObj := dataObject(selectedWin);
    resetBrowser(self, newDataObj, nil, selectedWin);
    showTokenAt(selectedWin, tuple(1,0));  /* View is line #1 */ 
    /* Notes: 09/07/93 - Nghia
    ** Check for object type before destroying it
    */
    if generality(oldDataObj) < 0 then
      destroyDataObj(oldDataObj);
    endif;
  endif;

  /* If split then do the same thing for the other browser */
  if split?(self) cand (otherBrowser := getOtherBrowser(self)) then
    if ((newPC := setPCAndDasmRange(self))) cand
      (newDataObj := open(VirtSmrtDasmObject, newPC)) then
      /* Set browser with new data object, nil = not saveHistory to destroy the old one */
      oldDataObj := dataObject(otherBrowser);
      resetBrowser(self, newDataObj, nil, otherBrowser);
      showTokenAt(otherBrowser, tuple(1,0));  /* View is line #1 */ 
      /* Notes: 09/07/93 - Nghia
      ** Check for object type before destroying it
      */
      if generality(oldDataObj) < 0 then
        destroyDataObj(oldDataObj);
      endif;
    endif;
  endif;

  /* Destroy the old browser history stack -
   *    Notes: VirtSmrtDasmObject contains addressDesc
   */
  do (backwardFile,
    {using(savedDataObj)
      if savedDataObj cand (generality(savedDataObj) < 0) then
        destroyDataObj(savedDataObj);
      endif;
     });
  backwardFile    := nil;
  /* Allocate a new History Stack */
  backwardFile    := new(HistoryStack, maxDepth);
  showOldCurs();

}
!!

/* 06/20/92 - PRIVATE
  Set the specified browser to have new SmartFile/DasmDataObject, also close the old
  VirtData Object and if caller request (saveHistory = true) then save it to the History Stack.
*/
Def resetBrowser(self, newDataObj, saveHistory, theBrowser | oldDataObject)
{
  /* Close the old VirtFile and take care of history */
  oldDataObject := dataObject(theBrowser);
  close(oldDataObject);
  /* If saveHistory = #true, do not destroy oldDataObj */
  if saveHistory then
    /* Saved the old location - #(virtualSelectline, startChar, endChar) */
    setCallersCache(oldDataObject, tuple(tokenLocation(theBrowser)[0], 0,0) );
    /* Got a new dataObj, save the old one */
    if not(inMiddle?(backwardFile)) then
      maybeAdd(backwardFile, oldDataObject);
    else
      add(backwardFile, oldDataObject);
    endif ;
    enableMenuItems(self, tuple(SRC_FILE_BACK, SRC_FILE_FORWARD));
  endif;

  /* Reset cursorLinked if registered */
  setCursorLinked(theBrowser, nil);

  /* Set the browser with the new data object */ /* #true - registerMemServer */
  /* nil - do not fill browser cache */
  setBrowserWithDataObj(self, theBrowser, newDataObj, nil, #true);
  showTitle(self);
}
!!

/* 06/20/92 - PRIVATE
   Reset the virtDataObject when browser history position has been pop'ed.
*/
Def resetVirtDataObj(self, historicalDataObj | tokenLocation)
{
  /* Data object already closed so reopen it */
  if not(reOpen(historicalDataObj)) then
    displayFormattedError(ErrorTextLibClass$Inst,
       ER_SRC_REOPEN_FAIL, FORCE_POPUP, nil, nil, nil);
    ^nil;
  endif;
  if not(tokenLocation := callersCache(historicalDataObj)) then
    tokenLocation := tuple(1, 0, 0); /* #(line, startCol, endCol) */
  endif;
  /* Set the browser with the specified data - #true to register memServer */
  setBrowserWithDataObj(self, selectedWin, historicalDataObj, tokenLocation, #true);

  /*
  ** Check the current viewMode to determine if the oldDataObject need to
  ** be changed to a new data type for the current viewMode
  */
  if class(historicalDataObj) <> VirtSmrtDasmObject then
    if ((viewMode = SRC_VIEW_SOURCE) cand (class(historicalDataObj) == VirtSMixedObject))
      cor
       ((viewMode = SRC_VIEW_MIX) cand (class(historicalDataObj) == VirtSmartFileObject)) then
      openNewDataView(self, selectedWin);
    endif;
  endif;
}
!!

/* 6/24/92 - Actor */
Def reSize(self, wP, lP)
{
  reSizeChilds(self);
  /* After resize - rehilight the selected browser */
  if selectedWin and split?(self) then
    hilightSelectedWindow(self);
  endif ;
}
!!

/* 6/12/1992 9:04 - PRIVATE
  Resize child browsers according to their parents size.
  Also check for splitting Source Presenter into 2 separate browsers.
*/
Def resizeBrowsers(self, parentRect | cr, virtDataObj, newPC )
{
  /* Set splitPos to try to keep % split the same */
  recalcSplitPos(self, parentRect);

  if split?(self) then
    /* top */
    cr := rect(left(parentRect), top(parentRect)+tmHeight+4, right(parentRect), splitPos-2 ) ;
    if (browser = selectedWin)  then
      inflate(cr, 0, -2 );
      setLeft(cr, left(cr)+2);
    endif ;
    setCRect(browser, cr);
    /* middle */
    cr := rect(right(parentRect)-17, splitPos-2, right(parentRect), splitPos+2);
    if (browser = selectedWin) then
      setTop(cr, top(cr)-2);
    else
      setBottom(cr, bottom(cr)+2);
    endif;
    setCRect(splitArea, cr);
    setYadjust(splitArea, splitPos-2);
    /*
    ** User open the split bottom browser, let open a new browser at the current
    ** PC location.
    */
    if not(browser2) then
      /* OPEN THE 2ND BROWSER HERE */
      if (newPC := getProgramCounter(CpuLibClass$Inst)) cand
        (maskAddressMSB(AddressLibClass$Inst, newPC)) cand
        (virtDataObj := openDataObject(self, newPC)) then
        browser2 := openBrowser(self, virtDataObj,
            rect(left(cr), top(cr)+tmHeight+4, right(cr), bottom(cr) ));   /* browserRect */
        /* Register Memory Changed event notification */
        setSourceMemoryRange(MemServLibClass$Inst, addressRange(virtDataObj), MEM_SOURCE2);
      else
        /* Failed to open the Browser2, so do not split. Only one browser is opened */
        if newPC then destroyAddress(AddressLibClass$Inst, newPC); endif;
        setCRect(browser, init(new(Rect), left(parentRect), top(parentRect)+18, right(parentRect), bottom(parentRect) ));
        setCRect(splitArea, rect(right(parentRect)-17, 12, right(parentRect), tmHeight+4));
        setYadjust(splitArea, tmHeight+4);
        ^GOOD;
      endif;
    else
      /* Update the browser2 execution info */
      getCurrentExecPoint(dataObject(browser2));
      rebuildBkptDict(dataObject(browser2));
    endif;
    cr := rect(left(parentRect), splitPos, right(parentRect), bottom(parentRect) ) ;
    if (browser2 = selectedWin) then
      inflate(cr, 0, -2);
      setLeft(cr, left(cr)+2);
    endif;
    setCRect(browser2, cr);
  else
    /* Only one browser is opened */
    setCRect(browser, init(new(Rect), left(parentRect), top(parentRect)+18, right(parentRect), bottom(parentRect) ));
    setCRect(splitArea, rect( right(parentRect)-17, 12, right(parentRect), 12+6 ) ) ;
    setYadjust(splitArea, tmHeight+4) ;
  endif;
}
!!

/* 06/24/92 - PRIVATE
  Resize all child object of the Source Presenter.
*/
Def reSizeChilds(self | cr, butWidth, butHeight, maxButtonRight, numButton, winWidth width)
{
  if not(childDict[GO_BUTTON]) then
    ^nil;
  endif;

  cr := clientRect( self );
  maxButtonRight := right(cr) - 18;
  winWidth := (maxButtonRight - left(cr));
  /* Use the maximum text lenght available or min text width */
  butWidth := max((winWidth / 8), minButtonWidth);
  width:= butWidth * 7 / 10;
  butHeight := tmHeight + 4;

  /* Resize the child controls  - 8 buttons */

  setCRect(at(childDict, GO_BUTTON),
    rect(0, 0,width,  butHeight ) );
  setCRect(at(childDict, HALT_BUTTON),
    rect(width+1,  0,width*2, butHeight) );
  width := width*2;
  setCRect(at(childDict, STEP_BUTTON),
    rect(width+1,0,width+butWidth, butHeight) );
  width := width+butWidth;
  setCRect(at(childDict, STEPOVER_BUTTON),
    rect(width+1,0,width+butWidth, butHeight) );
  width := width+butWidth;
  setCRect(at(childDict, UNTILCALL_BUTTON),
    rect(width+1,0,width+butWidth-6, butHeight) );
  width := width+butWidth-6;
  setCRect(at(childDict, TILRETURN_BUTTON),
    rect(width+1,0,width+butWidth+3, butHeight) );
  width := width+butWidth+3;
  setCRect(at(childDict, GOTOCURSOR_BUTTON),
    rect(width+1,0,width+butWidth+10, butHeight) );
  width := width+butWidth+10;
  setCRect(at(childDict, 809),
    rect(width+1,0,width+(butWidth*7/10), butHeight) );

  /* Move buttons to new positions accordingly - Hide buttons that does not fit */
  numButton := 1;
  do (childDict,
     {using(childId | crButton)
        moveWindow(childId);
        crButton := clientRect(childId);
/*        if ((right(crButton)*numButton) > maxButtonRight) then
          show(childId, SW_HIDE);
        else
          show(childId, SW_SHOW);
        endif;
*/
        show(childId, SW_SHOW);
        numButton := numButton +1;
  });

  /* Resize child browsers */
  resizeBrowsers(self, cr);
  prevCRect := cr;
  /* Move childs to the new positions */
  moveWindow(splitArea);
  /* Position browser */
  moveWindow(browser);
  show(browser, SW_SHOW);
  if split?(self) then
    moveWindow(browser2);
    show(browser2, SW_SHOWNA);
  endif ;
}
!!

/* 8/6/1992 16:32 - PRIVATE
  Save self option to the ProfileControl object.
*/
Def saveSelf(self | appName, iniVal)
{
  appName := "SourceInfo";
  saveProfileEntry(TheProfileInfoObj, appName, "TabWidth",
            asString(tabWidth));
  saveProfileEntry(TheProfileInfoObj, appName, "DisplayLineNum",
            asString(asCBoolean(TheProfileInfoObj, displayLineNum)));
  saveProfileEntry(TheProfileInfoObj, appName, "StepCount",
            asString(stepCount));
  /* View Mode */
  iniVal := 0;
  if (viewMode = SRC_VIEW_SOURCE) then
     iniVal := 1;
  endif;
  saveProfileEntry(TheProfileInfoObj, appName, "ViewSource",
            asString(iniVal));
  /* Go Button Type */
  iniVal := 0;
  if (goType = SRC_OPTN_EXEC_GO_INTO) then
     iniVal := 1;
  endif;
  saveProfileEntry(TheProfileInfoObj, appName, "UseGoInto",
            asString(iniVal));
  /* Exec. Granularity Type */
  iniVal := 0;
  if (execGran = SRC_OPTN_STEP_LINE_GRAN) then
     iniVal := 1;
  endif;
  saveProfileEntry(TheProfileInfoObj, appName, "UseLineExecGranularity",
            asString(iniVal));

  saveProfileEntry(TheProfileInfoObj, appName, "HistoryDepth",
            asString(maxDepth));
  saveProfileEntry(TheProfileInfoObj, appName, "SourceDelimiterUseCRLF",
            asString(asCBoolean(TheProfileInfoObj, sourceDelimiter)) );
  ^GOOD;
}
!!

/* 06/20/92 - PRIVATE
  Request a search string. If nextFlag is true and there is
  something in searchText, find the next occurence.
*/
Def searchText(self, nextFlag, theBrowser |
  inputDlg, cont, virtFO, txtMsg, len, addrRangeDesc )
{

  /* search for Next occurance */
  if not(nextFlag cand searchText) then
     inputDlg := open(InputDlgWithHelp);
     setUp(inputDlg, "Search", "Search for:", 
        searchText cor "", HE_DLGD_SOURCEPR_4);
     if (runModal(inputDlg, DLG_INPUT_WITH_HELP, ThePort) <> IDOK) cor
      ((searchText := getText(inputDlg)) = nil) then
       ^nil;
     endif;
  endif;
  showWaitCurs();
  /* If we have something to search for and user did not cancel... */
  if ((len := size(searchText)) > 0) then
    /* nil if found, else last line searched */
    if (cont := findAndShowString(theBrowser, searchText)) then
      /* if not in cache, then look in file */
      virtFO := dataObject(theBrowser);
      if (cont := searchText(virtFO, searchText, cont,
            getTxtLineRoutine(theBrowser))) then
        /* Found token at location cont, show it in hilight */
        showTokenAt(theBrowser, cont);
      else
        /* Check for too long search text */
        txtMsg := searchText;
        if (len > 40) then
            txtMsg := ".."+subString(searchText, len-40, len); /* truncate search text */
        endif;
        txtMsg := "'"+txtMsg+"'";
        /* Get address range of virtual dasm object */
        if (class(virtFO) = VirtSmrtDasmObject) cand
              (addrRangeDesc := addressRange(virtFO)) then
          /* Show searched address Range */
          txtMsg := txtMsg + " in range "
            + format(AddressLibClass$Inst, getOffset(AddressLibClass$Inst, addrRangeDesc))
            + ".."
            + format(AddressLibClass$Inst, getEndOffset(AddressLibClass$Inst, addrRangeDesc));
          /* Get rid of the unused address */
          destroyAddress(AddressLibClass$Inst, addrRangeDesc);
        endif;
        displayFormattedError(ErrorTextLibClass$Inst,
           ER_SRC_CANT_FIND, FORCE_POPUP, asciiz(txtMsg), nil, nil);
      endif ;
    endif;
  endif;
  showOldCurs();
}!!

/* PRIVATE (Public to kids) */
Def selectedWin(self)
{ ^selectedWin }
!!

/* PRIVATE - Create dasm object with the input address desc. */
Def selectDasmObject(self, addrDesc | tmpDesc, temp, newOffset, addrRange
                     dataRef, dataType) {
  dataType := VirtSmrtDasmObject;
  dataRef := addrDesc;
  addrRange := 128; /* bytes */
  /* If the address descriptor is not an address range or range too small then
  *** set default to 128 bytes, or (MaxOutputAddress & 0xfffffffe) - startOffset. 
   */
  if not(newOffset := getOffset(AddressLibClass$Inst, addrDesc)) cor
    not(tmpDesc := duplicateAddress(AddressLibClass$Inst, addrDesc)) then
    destroyAddress(AddressLibClass$Inst, addrDesc);
    ^nil;
  endif;
  if (isAnAddrRangeDesc?(AddressLibClass$Inst, addrDesc))
    /* figure out if the range is too small */
    if not(addToAddress(AddressLibClass$Inst, tmpDesc, addrRange)) cor
         not(temp := compareAddresses(AddressLibClass$Inst, tmpDesc, addrDesc)) then
      freeDesc(self, addrDesc, tmpDesc);
      ^nil;
    endif;
  else
    temp := 3; /* invalid number not generated by compareAddresses */
  endif;

  if (not(isAnAddrRangeDesc?(AddressLibClass$Inst, addrDesc)) cor
     (temp = ADRLIB_ADDR_GREATER_THAN))  /* newOffset + addrRange > endAddrOffset */
    if not(setOffset(AddressLibClass$Inst, tmpDesc,
           (maxOutputOffset(AddressLibClass$Inst, tmpDesc) bitAnd 0xfffffffeL)))
      freeDesc(self, addrDesc, tmpDesc);
      ^nil
    endif;
    /* NOTES: addrDesc = maxOutputOffset(AddressLibClass$Inst, addrDesc),
    ** adjust addrDesc to MAX_ADDRESS - (number of visible lines * 2)
    ** {2 bytes for each lines}
    */
    if (compareAddresses(AddressLibClass$Inst, tmpDesc, addrDesc) =
          ADRLIB_ADDR_EQUAL) then
      if not(subtractFromAddress(AddressLibClass$Inst, addrDesc, visLines(self)*2)) cor
        not(newOffset := getOffset(AddressLibClass$Inst, addrDesc)) then
        freeDesc(self, addrDesc, tmpDesc);
        ^nil
      endif;
    endif;
    if (not(subtractFromAddress(AddressLibClass$Inst, tmpDesc, newOffset))
      cor not(temp := getOffset(AddressLibClass$Inst, tmpDesc))) then
      freeDesc(self, addrDesc, tmpDesc);
      ^nil
    endif;

    if not(addrRange := minAddressOffset(AddressLibClass$Inst, 128L, temp))
      freeDesc(self, addrDesc, tmpDesc);
      ^nil
    endif;
  endif;
  if not(dataRef := setAddrRangeLength(AddressLibClass$Inst, addrDesc,
         addrRange )) then
    freeDesc(self, addrDesc, tmpDesc);
    ^nil;
  endif;
  /* NOTES: do not destroy the addrDesc if open DASM */
  destroyAddress(AddressLibClass$Inst, tmpDesc);
  /* Call the dataType class to open the selected data object */
  ^open(dataType, dataRef);
}!!

/* PRIVATE */
Def selectSymbolicDataObject(self, addrDesc, symInfo | dataRef, dataType, modulesDict)
{
  /* Map symInfo = moduleDesc - Use the moduleInfo from the currentLoadFile of the ProjectInfo */
  if not(modulesDict := modulesDict(currentLoadFile(TheProjectInfoObj))) cor
     (size(modulesDict) = 0)
     /* No module information - open a dasm data object instead */
     ^selectDasmObject(self, addrDesc);
  endif;
  /* Cannot map a module descriptor to dictionary - report error */   
  if not(dataRef := modulesDict[symInfo]) then
   /* dataRef = <ModuleInfo> object */
   displayFormattedError(ErrorTextLibClass$Inst,
      ER_SRC_FIND_ADDRESS, FORCE_POPUP, nil, nil, nil);
   /* No module information - open a dasm data object instead */
   ^selectDasmObject(self, addrDesc);
  endif;

  /* Check if the requested module has any linenumber information */
  dataType := getDataTypeOfModule(self, moduleDescriptor(dataRef));
  /* added by cjchen 1996.11.26*/
  if (nameFg=1) then
    dataType:=VirtSmrtDasmObject;
  else
    if not(filePath(dataRef))
      dataType:=VirtSmrtDasmObject;
      nameFg:=1;
    else
      nameFg:=2;
    endif;
  endif;
  /* added by cjchen EOF*/  

  /* Need to adjust dataRef in the case of a dasm object */
  if (dataType == VirtSmrtDasmObject) 
    if not(dataRef := duplicateAddress(AddressLibClass$Inst, addressRange(dataRef))) 
      ^nil;
    endif;
  endif;

  /* Destroy the addrDesc & the unused symInfo, cause we already got the dataRef */
  destroyAddress(AddressLibClass$Inst, addrDesc);
  /* Call the dataType class to open the selected data object */
  ^open(dataType, dataRef);
}
!!

/* 7/26/1992 23:07 - PRIVATE
  Set the specified browser with routine to display lineNumber.
  NOTES:
    dataObjType must be the actual data object of theBrowser.
    shiftHilight? is #true only when call from  cmdViewShowLineNum().
*/
Def setBrowserShowLineNum(self, theBrowser, dataObjType, shiftHilight?)
{
  /* Line number display only effect the VirtFileObject and VirtMixedObject */
  if (dataObjType <> VirtSmrtDasmObject) then
    showLineNum(theBrowser, displayLineNum, shiftHilight?); /* #true - nil */
    if not(displayLineNum) then
      setGetTextLineRoutine(theBrowser,
        {using(txtLine)
          copyFrom(txtLine, 9, size(txtLine)); /* 9 is lineNumber section */
        });
    else
      /* Default routine - show line # */
      setGetTextLineRoutine(theBrowser,
                {using(txtLine)
                  copyFrom(txtLine, 0, size(txtLine));
                });
    endif;
  else
    /* Default that DASM dataObject do not have lineNumber display */
    showLineNum(theBrowser, nil, nil);
    /* Copy full text string for dasm */
    setGetTextLineRoutine(theBrowser,
      {using(txtLine)
        copyFrom(txtLine, 0, size(txtLine));
      });
  endif;
  ^GOOD;
}
!!

/* 9/3/1992 16:41 - PRIVATE
  Set the specified browser with the new data object.
  EX: setBrowserWithDataObj(self, selectedWin, <VirtSmartFileObject>, #(1, 0), #true);
*/
Def setBrowserWithDataObj(self, theBrowser, newDataObject, tokenLoc, memRegister?)
{
  /* Set the browser with a new data object */
  if (theBrowser = browser) then
    /* setLanguageName(browser, languageName(newDataObject)); */
    setVirtualLines(browser, numLinesIn(newDataObject));
    setBrowserShowLineNum(self, browser, class(newDataObject), nil);
    setDataObject(browser, newDataObject, tokenLoc);
    /* Register Memory Changed event notification - Server destroy addressRange */
    if memRegister? then
      setSourceMemoryRange(MemServLibClass$Inst, addressRange(newDataObject), MEM_SOURCE1);
    endif;
    setCallbacks(theBrowser);
  else 
    if (theBrowser = browser2) then
      setVirtualLines(browser2, numLinesIn(newDataObject));
      setBrowserShowLineNum(self, browser2, class(newDataObject), nil);
      setDataObject(browser2, newDataObject, tokenLoc);
      /* Register Memory Changed event notification - Server destroy addressRange */
      if memRegister? then
        setSourceMemoryRange(MemServLibClass$Inst, addressRange(newDataObject), MEM_SOURCE2);
      endif;
      setCallbacks(theBrowser);
    endif;
  endif;

  /* Check breakpoint menu commands */
  getBkptsAndCheck(self, selectedWin);

  /* Enable/Disable commands specific to data object type */
  if (class(dataObject(selectedWin)) = VirtSmrtDasmObject) then
    setCmdsForAsm(self);
  else
    setCmdsForSource(self);
  endif;
}
!!

/* 8/6/1992 16:21 - PRIVATE
  Set the state of Go button and commands
*/
Def setGoButtonCmds(self)
{
  /* Flip-the execution buttons */
  if goType = SRC_OPTN_EXEC_GO_UNTIL then
    setText(at(childDict, UNTILCALL_BUTTON), "Till Call");
    setText(at(childDict, TILRETURN_BUTTON), "Till Return");
  else
    setText(at(childDict, UNTILCALL_BUTTON), "Into Call");
    setText(at(childDict, TILRETURN_BUTTON), "Into Return");
  endif;
  /* update button display */
  update(at(childDict, UNTILCALL_BUTTON));
  update(at(childDict, TILRETURN_BUTTON));
}
!!

/* 06/20/92 - PRIVATE (Public to SourceCB children) */
Def setSelectedWin(self, win)
{
  /* Reset PC, CursorLinked */
  if (selectedWin <> win) then
    clearPC(selectedWin);
    setCursorLinked(selectedWin, nil);
  endif;
  selectedWin := win;
  /* Check breakpoints to set menu commands */
  getBkptsAndCheck(self, selectedWin);
  /* Enable/Disable commands specific to data object type */
  if (class(dataObject(selectedWin)) = VirtSmrtDasmObject) then
    setCmdsForAsm(self);
  else
    setCmdsForSource(self);
  endif;

}
!!

/* 06/20/92 - PUBLIC */
Def show(self, displayMode)
{
  /* Show all display objects */
  show(self:ancestor, displayMode);
  /* Show all child control */
  do (childDict,
    {using(childId)
      show(childId, SW_NORMAL);
    });
  show(splitArea, SW_SHOWNA);
  show(browser, SW_SHOW);
  if split?(self) cand browser2 then
    show(browser2, SW_SHOWNA);
  endif ;
}
!!

/* 7/23/1992 10:13 - PUBLIC
  Show the text of the correspond data object of the specified addrDesc.  This
  only effect the selected browsers.
    NOTES: the addrDesc will be consumed by showFileFromAddress().
           tokenHilight = symbol string or nil.
*/
Def showDataObjFromAddress(self, addrDesc, tokenHilight)
{
  /* Show the new data object in the selectedWin - #true to save History, #true to hilight */
  if showFileFromAddress(self, addrDesc, selectedWin, #true, #true) then

    /* Find the address symbol then hilight it. - Only Source need it. */
    if tokenHilight cand (class(dataObject(selectedWin)) = VirtSmartFileObject) then
      findAndShowStringReverse(selectedWin, tokenHilight);
    endif;
  endif;
}
!!

/* 06/20/92 - PRIVATE
  Show a source file corresponding to the address specified.
  return the DataObject if successfully opened.

  NOTES: The addrDesc will be destroyed by this routine.
*/
Def showFileFromAddress(self, addrDesc, theBrowser, hilight?, saveHistory?
  | addrRange, cloneDesc, modInfo, smartDataObj, lNum, cNum)
{
  lNum := 0; cNum := 0;
  /* Clear all upper bits of input address */
  if not(maskAddressMSB(AddressLibClass$Inst, addrDesc)) then
    destroyAddress(AddressLibClass$Inst, addrDesc);
    ^nil; /* NOT portable */
  endif;
  if (smartDataObj := dataObject(theBrowser)) then
    addrRange := addressRange(smartDataObj); /* duplicate address */
  endif;

  /* Check if current data object contains the specified address - or open a new one */
  /* Winky 3/5/98 add isBoth flag */
  if not(addrRange) cor
     not(isAddrInAddressRangeNoError(AddressLibClass$Inst, addrDesc, addrRange)) cor
     (not(ldrSpaceMode(LoaderLibClass$Inst)=getSpaceMode(CpuLibClass$Inst)) cand isBoth = 0) cor
     ((class(smartDataObj) == VirtSmrtDasmObject) cand (addrHasSource?(self, addrDesc))) 
    then 
    /* Open a new data object of the input address descriptor */
    cloneDesc := duplicateAddress(AddressLibClass$Inst, addrDesc);
    if not(smartDataObj := openDataObject(self, cloneDesc)) then
      destroyAddress(AddressLibClass$Inst, addrRange);
      destroyAddress(AddressLibClass$Inst, addrDesc);
      ^nil;
    endif;
    /* reset SelectedWin - Save browser history */
    resetBrowser(self, smartDataObj, saveHistory?, theBrowser);
  else
    /* Update the currentExecutionPoint */
    getCurrentExecPoint(dataObject(theBrowser));
  endif;

  /* destroy the unused address descriptor */
  if addrRange then
    destroyAddress(AddressLibClass$Inst, addrRange);
  endif;

  /* Get the virtual Line of addressDesc to show */
  if (class(smartDataObj) == VirtSmartFileObject) then
    if (modInfo := getAddrInfo(self, addrDesc)) then
      /* WHERE: modInfo = #(moduleDesc, lineNum, colNum) */
      lNum := modInfo[1];
      cNum := modInfo[2];
    endif;
    /* Use (0,0) as default */
  else
    /* Only an object of VirtSmrtDasmObject or VirtSMixedObject type */
    lNum := getOffset(AddressLibClass$Inst, addrDesc); /* NON-PORTABLE */
  endif;

  /* destroy the unused input address descriptor */
  destroyAddress(AddressLibClass$Inst, addrDesc);

  /* Show from the virtual Line if line is out of screen */
  if hilight? cor not(lineOnScreen?(theBrowser, lNum)) then
    /* Remap data object line number to a virtual line number */
    showWaitCurs();
    lNum := getVirtLineNum(theBrowser, lNum);
    showLineAt(theBrowser, tuple(lNum, cNum)); /* startLine, startCol */
    showOldCurs();
  else
    /* Update the screen */
    updatePC(theBrowser);
  endif;

  ^smartDataObj;
}
!!

/* 06/20/92 - PRIVATE
  Show a source|mixed data object of the specified module Info.
    NOTES: Only apply to the selectedWin if 2 browser open.
*/
Def showFileFromLineAndModule(self, startLine, startCol, moduleInfo
                             | moduleInfo, addrRange, dataType, dataRef, dataObject)
{
  /* Open either a MixedObject or a SourceModule Object */
  dataRef := moduleInfo;               /* object: <ModuleInfo> */

  /* Check if the requested module has any linenumber information */
  if not(moduleDescriptor(moduleInfo)) then  
    ^nil;
  endif;

  /* Make sure that it's not a data module (which does not have a valid address range) */
  if not(addrRange := addressRange(moduleInfo)) cor
     not(isAnAddrRangeDesc?(AddressLibClass$Inst, addrRange)) then
   displayFormattedError(ErrorTextLibClass$Inst,
      ER_SRC_MODULE_HAS_NO_ADDR_RANGE, FORCE_POPUP, nil, nil, nil);    
    ^nil;
  endif;

  /* Get the type of data object to open */
  dataType := getDataTypeOfModule(self, moduleDescriptor(moduleInfo));
  /* Need to adjust dataRef in the case of assembly module */
  if (dataType == VirtSmrtDasmObject) then
    if not(dataRef := duplicateAddress(AddressLibClass$Inst, addressRange(dataRef)))
      ^nil;
    endif;
  endif;
  
  /* Open a new data object with its dataType */
  if not(dataObject := open(dataType, dataRef)) then
    ^nil
  endif;
  /* reset SelectedWin is here so we can use modInfo to set file position */
  resetBrowser(self, dataObject, #true /* Save History */, selectedWin);
  showTokenAt(selectedWin, tuple(startLine, startCol));
  ^dataObject;
}
!!

/* 06/20/92 - PRIVATE
  Show a linked cursor of the source file corresponding to
  the address specified.

  NOTES: the addrDesc will be destroyed by this routine.
*/
Def showLinkedFromAddress(self, addrDesc, theBrowser
  | addrRange, cloneDesc, modInfo, smartDataObj, newDataObj, lNum, cNum)
{
  lNum := 0; cNum := 0;

  if smartDataObj := dataObject(theBrowser) then
    addrRange := addressRange(smartDataObj); /* duplicate address */
  endif;

  /* Check if current data object contains the specified address - 
  ** or open a new one 
  */
  if not(addrRange) cor 
     not(isAddrInAddressRangeNoError(AddressLibClass$Inst, addrDesc, addrRange)) cor
     ((class(smartDataObj) == VirtSmrtDasmObject) cand (addrHasSource?(self, addrDesc))) 
    then
    /* Open a new data object of the input address descriptor */
    cloneDesc := duplicateAddress(AddressLibClass$Inst, addrDesc);
    if not(newDataObj := openDataObject(self, cloneDesc)) then
      destroyAddress(AddressLibClass$Inst, addrDesc);
      destroyAddress(AddressLibClass$Inst, addrRange);
      ^nil;
    endif;
    /* reset SelectedWin - nil = not to save browser history */
    resetBrowser(self, newDataObj, nil, theBrowser);
    /* Notes: 09/07/93 - Nghia
    ** Check for object type before destroying it
    */
    if generality(smartDataObj) < 0 then
      destroyDataObj(smartDataObj);  /* Get rid of the old data object */
    endif;
    smartDataObj  := newDataObj;
  endif;

  /* destroy the unused address descriptor */
  if addrRange then
    destroyAddress(AddressLibClass$Inst, addrRange);
  endif;

  /* Get the virtual Line of addressDesc to show */
  if (class(smartDataObj) == VirtSmartFileObject) then
    if (modInfo := getAddrInfo(self, addrDesc)) then
      /* NOTES: modInfo =  #(moduleDesc, lineNum, colNum) */
      lNum := modInfo[1];
      cNum := modInfo[2];
    endif;
    /* Bad thing happened - Use (0,0) as default */
  else
    /* Only an object of VirtSmrtDasmObject or VirtSMixedObject type */
    lNum := getOffset(AddressLibClass$Inst, addrDesc); /* NON-PORTABLE */
  endif;

  /* Update the cursorLinked - destroy the unused input address descriptor */
  setCursorLinked(theBrowser, lNum);
  destroyAddress(AddressLibClass$Inst, addrDesc);

  /* Show from the virtual Line if line is out of screen */
  if not(lineOnScreen?(theBrowser, lNum)) cand
    /* Remap data object line number to a virtual line number */
    (lNum := getVirtLineNum(theBrowser, lNum)) then
    showLineAt(theBrowser, tuple(lNum, cNum)); /* startLine, startCol */
  else
    /* Invalidate to update the screen */
    invalidate(theBrowser);
  endif;
}
!!

/* 06/20/92 - PRIVATE
  Display the current title of the file being edited
  or "Untitled" if no name has been specified.
*/
Def showTitle(self | name spaceText)
{
  /* Default title */
  name := "(Untitled)";
  if dataObject(browser) cand
         virtObjTitle(dataObject(browser)) then
    if (split?(self) cand browser2 cand
      dataObject(browser2)) cand virtObjTitle(dataObject(browser2)) then
      name := "(1: "+virtObjTitle(dataObject(browser))
              + ") || (" +
              "2: "+virtObjTitle(dataObject(browser2))
              + ")";
    else
      name := "("+virtObjTitle(dataObject(selectedWin))+")";
    endif;
  endif ;
  spaceText := getAddrSpaceText(self);
  setText( self, "Source: " + spaceText + name );
}!!

/* 06/20/92 - PRIVATE
   Return true if 2 browsers are shown
*/
Def split?(self)
{
  ^(splitPos <> 0)
}
!!

/* 9/4/1992 9:33 - PUBLIC
  a halt command has issued, exit animating if it running.
*/
Def stopAnimating(self)
{
  if animating then
    ^(animating := nil);
  endif;
}
!!

/* 8/29/1992 15:00 - PRIVATE
  Unregister the SourceMemoryChanged event for the source browser.
  NOTES: id = MEM_SOURCE1 and MEM_SOURCE2;
*/
Def unregisterSourceMemChange(self, id | addrDesc)
{
  /* Set a dummy addr desc to memory server - server will destroy it */
  if (addrDesc := createAddress(AddressLibClass$Inst)) then
    setSourceMemoryRange(MemServLibClass$Inst, addrDesc, id);
  endif;
}
!!

/* 7/20/1992 15:31 - PUBLIC
  Enable all symbolic commands of the Source Presenter.
*/
Def validateSourceCommands(self)
{
  enableMenuItems(self, tuple(SRC_FILE_LOAD_INFO,
                              SRC_FILE_MODULES));
}
!!

/* 06/20/92 - PRIVATE
  Return the number of visible text lines in window.
*/
Def visLines(self)
{
  ^(height(clientRect(self)) / tmHeight);
}!!

/* 06/20/92 - WINDOWS
  Self has been removed from the screen.  Remove from
  the set of open windows.  Close the file.
*/
Def WM_DESTROY(self, wp, lp)
{
  /* Close Source Browsers */
  if browser then
    unregisterSourceMemChange(self, MEM_SOURCE1);
    close(browser);
    /* Notes: 09/07/93 - Nghia
    ** Check for object type before destroying it
    */
    if generality(dataObject(browser)) < 0 then
      destroyDataObj(dataObject(browser));
    endif;
    browser := nil;
  endif;
  if browser2 then
    unregisterSourceMemChange(self, MEM_SOURCE2);
    close(browser2);
    /* Notes: 09/07/93 - Nghia
    ** Check for object type before destroying it
    */
    if generality(dataObject(browser2)) < 0 then
      destroyDataObj(dataObject(browser2));
    endif;
    browser2 := nil;
  endif;
  TheSourcePresenter := nil;
  WM_DESTROY(self:ancestor, wp, lp);
}!!

/* 06/20/92 - WINDOWS (Tbird's message)
  Sent by event manager (evNoteLib)
*/
Def WM_EVENT(self, ignored, event)
{
  if event cand at(eventRespSet, event) then
    perform(self, at(eventRespSet, event));
  else
    /* Happend only if Windows went nut */
    displayFormattedError(ErrorTextLibClass$Inst,
       ER_UNKNOWN_EVENT, FORCE_POPUP, asciiz(asHex(event)), nil, nil);
  endif;
  ^0
}!!

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

/* 5/18/1992 11:41 - WINDOWS
  Trap the WM_KEYDOWN and translate it to its equivalent functions
*/
Def WM_KEYDOWN(self, wP, lP | msgSend)
{
  /* Send message to the selectedwin */
  if $keysDict[wP] then
    if (wP == VK_LEFT) cor (wP == VK_RIGHT) then
      ^WM_HSCROLL(selectedWin, $keysDict[wP], 0L);
    endif;
    ^WM_VSCROLL(selectedWin, $keysDict[wP], 0L);
  endif;
  ^WM_KEYDOWN(self:ancestor, wP, lP);
}
!!

/* 06/20/92 - WINDOWS
  MS-Window's message to paint self -- sends a
  paint(self) message.  This overrides Window:WM_PAINT
  so that TextWindow and its descendants use the
  System Fixed Font instead of the System Font. */
Def WM_PAINT(self, wP, lP | hdc)
{
  hdc := Call BeginPaint(getHWnd(self), paintStruct);
  Call SelectObject(hdc, Call GetStockObject(SYSTEM_FIXED_FONT));
  showTitle(self);
  paint(self, hdc);
  Call SelectObject(hdc, Call GetStockObject(SYSTEM_FONT));
  Call EndPaint(getHWnd(self), paintStruct);
  if selectedWin and split?(self) then
    hilightSelectedWindow(self);
  endif ;
  ^0;
}!!

/* SourcePresenter Class Initialization Code */
$keysDict := new(Dictionary, 2);
add($keysDict, VK_HOME,   SB_TOP);
add($keysDict, VK_END,    SB_BOTTOM);
add($keysDict, VK_UP,     SB_LINEUP);
add($keysDict, VK_DOWN,   SB_LINEDOWN);
add($keysDict, VK_LEFT,   SB_LINEUP);
add($keysDict, VK_RIGHT,  SB_LINEDOWN);
