/* CLASS: VirtDataObject
   Children of this class usually don't all fit in memory at once,
   so in order to browse them, one gets text lines in Chunks.
   A Chunk consists of a tuple: <startLineNumber, textCollection>.
   Each chunk is numbered starting from 1.
   Chunks may give different text each time they are retrieved,
   but are expected to "show the same area" (e.g. memory snapshots over time).
   NOTES: There are common instance variables being moved from the child level
   to supply to all child objects.

   REQUIRE: OBJECT.CLS
*/!!

inherit(Object, #VirtDataObject, #(closed?
addressSize /* Max # of digits in address field */
currentExecPoint /* Current execution point of data object - line# or offset */
highestChunkSeen /* nil or chunkNum */
linesPerChunk /* chunk size */
rememberedLocations /* chunk#->currentObjPosition */
resizeCallback /* aBlock */
totalLinesExact  /* nil or # of lines */
totalLinesGuess /* estimate of total lines */
languageName /* object language */
userAbort /* #true or nil */
errReported /* last error reported */), 2, nil)!!

now(class(VirtDataObject))!!

now(VirtDataObject)!!

/* 4/21/1994 16:45 - PUBLIC
  return the last error reported
*/
Def errReported(self)
{ 
  ^errReported;
}
!!

/* 6/9/1993 6:40
**
** In some modes, line numbers may be adjusted when new text is computed.  This
** callback is used to inform the callee about the adjustment.
*/
Def setLineAdjustCallback(self, aCodeBlock)
{
  /* Default is no line adjustment.  May be overridden by descendents */
}
!!

/* 01/12/1993 16:14 - PRIVATE (Modified from TextCollection class)
  Return a point giving the location in textBuff
  of the supplied string str. Return nil if not found.
*/
Def findStringInChunk(self, textBuff, str, startLine, strtChar, getTxtFunc | sL, fC)
{
  sL := startLine;
  if strtChar > 0 then
    fC := find(eval(getTxtFunc, textBuff[sL]), str, strtChar);
    sL := sL + 1;
  endif;
  if fC then ^point(fC, startLine);  endif;

  do(over(sL, size(textBuff)),
  {using(i)
    if fC := find(eval(getTxtFunc, textBuff[i]), str, 0) then
      ^point(fC, i);
    endif;
  });
  ^nil;
}

!!

/* 12/14/1992 12:54 - PUBLIC (to child object only)
  Set the previously report error to a new value.
*/
Def clearErrReported(self)
{
  ^(errReported := GOOD);
}
!!

/* 12/14/1992 12:54 - PUBLIC (to child object only)
  Set the previously report error to a new value.
*/
Def setErrReported(self, newErr)
{
  ^(errReported := newErr);
}
!!

/* 12/10/1992 16:22 - PRIVATE (to its children object)
  Create a error disassembly buffer text and return the text collection.
  Reported error is last access to server did not get error.
*/
Def getDasmErrorBuffer(self, startAddr, numLines | dasmCol, err, errStr, line, tmpAddrDesc)
{
  /* Allocate buffer to store dasmCollection */
  dasmCol := new(TextCollection, numLines);
  line := 0;
  if ((err := lastError(DisAsmLibClass$Inst)) <> GOOD) then
    /* Only report error if not yet reported */
    if (err <> errReported) then
      displayError(ErrorTextLibClass$Inst, err, CHECK_MODE);
      errReported := err;
    endif;
    if not(tmpAddrDesc := createAndSetAddress(AddressLibClass$Inst, startAddr)) then
      ^nil;
    endif;
    loop
    while (line < numLines)
      /* prefix a space to avoid showing PC mark */
      errStr := " "+getAddressTextParam(AddressLibClass$Inst, tmpAddrDesc, 0, 1) 
        + " UNABLE TO READ MEMORY.";
      add(dasmCol, errStr);
      line := line + 1;
      startAddr := startAddr+2;
      setOffset(AddressLibClass$Inst, tmpAddrDesc, startAddr)
    endLoop;
    destroyAddress(AddressLibClass$Inst, tmpAddrDesc);
    ^dasmCol;
  endif;
  ^nil; /* Can't create the error buffer */
}
!!

/* 12/10/1992 9:40 - PRIVATE (only use by its children objects)
  Report error on requested operation without breakpoint.
*/
Def reportBkptError(self, msg | opErrTxt)
{
  select
    case msg = SRC_BKPT_CLR_BKPT
      opErrTxt := "clear";
    endCase
    case msg = SRC_BKPT_ENABLE_BKPT
      opErrTxt := "enable";
    endCase
    case msg = SRC_BKPT_DISABLE_BKPT
      opErrTxt := "disable";
    endCase
  endSelect;
  beep();
  displayFormattedError(ErrorTextLibClass$Inst,
     ER_DASM_NO_BKPT, FORCE_POPUP, opErrTxt, nil, nil);
  ^nil;
}
!!

/* 7/1/1992 11:09 - PUBLIC
  Reset the currentExecPoint of self to nil.
*/
Def clearCurrentExecPoint(self)
{
  ^(currentExecPoint := nil);
}
!!

/* 11/10/1992 15:56 - PUBLIC (to descendant)
  Return #true if User press ESC to abort. Else nil.
*/
Def checkAbort(self)
{
  /* Check Abort */
  if queryAbort(TaskLibClass$Inst) cand
     (new(ErrorBox, TheSourcePresenter, "Abort current operation?"
     ,"Warning", MB_YESNO bitOr MB_ICONQUESTION) == IDYES) then
    /* Set flag to TRUE */
    userAbort := #true;
    if TheSourcePresenter then
      stopAnimating(TheSourcePresenter);
    endif;
  endif;
  ^userAbort;
}
!!

/* 3/25/1992 11:53 - PRIVATE
  Get the color tag of the breakpoint.
    NOTES: Shared by descendant classes:
      virtSmrtDasmObject, virtSmartFileObject, virtSMixedObject
*/
Def getBkptColor(self, bkptState, bkptLife )
{
  /* Return the color tag of breakpoint */
  select
    case bkptState = HLB_BKPT_STATE_DISABLED
      ^AT_BREAK_INACT_FIELD;
    endCase
    case bkptState = HLB_BKPT_STATE_ENABLED
      /* NOTES: Should have 2 separate color coding */
      if (bkptLife = HLB_BKPT_LIFE_PERMANENT) cor
        (bkptLife = HLB_BKPT_LIFE_TEMPORARY) then
        ^AT_BREAK_ACT_FIELD;
      endif;
    endCase
    default
      ^AT_BREAK_ACT_FIELD;
  endSelect;
}
!!



/* 11/10/1992 15:37 - PUBLIC (to its browser class)
  Reset the userAbort flag to nil.
*/
Def clearAbort(self)
{
  /* Clear out previous ESC key */
  queryAbort(TaskLibClass$Inst);
  userAbort := nil;
}
!!

/* 11/10/1992 15:35 - PUBLIC (to its browser class)
  Return the userAbort flag. Clear the flag when calling.
*/
Def userAbort?(self | tmp )
{
  tmp := userAbort;
  userAbort := nil;
  ^tmp;
}
!!

/* 10/17/1992 13:31 - PUBLIC (To its children) 
  Return #true if the addrTxtStr is on an instruction boundary. Else nil.
*/
Def isInstrBoundAddress?(self, addrTxtStr | parseColl, numItems )
{ 
  if (parseColl := findStrings(addrTxtStr, " ")) cand 
    ((numItems := size(parseColl)) > 0) then   
    /* If either the last two parts of the txt string are not 
    ** number then it's ok 
    */ 
    if (numItems >= 2) cand 
       (not(asInt(parseColl[numItems-2], 16)) cor
         not(asInt(parseColl[numItems-1], 16))) then
      ^#true;  
    endif;
    if (class(self) == VirtSMixedObject) cand (numItems = 1) 
      ^#true; /* it's a empty text line */
    endif;  
  endif;
  /* Warn user about non-opcode location */  
  displayFormattedError(ErrorTextLibClass$Inst, 
     ER_DASM_CANT_SET_BKPT, FORCE_POPUP, nil, nil, nil);
  ^nil;  
}
!!

/* 8/27/1992 20:33 */
Def addressSize(self)
{ 
  ^addressSize;
}
!!

/* 06/20/92 - PUBLIC
   Given a (virtual) line number, return the chunk number which contains it.
*/
Def chunkNumberFromLineNumber(self, lineNumber)
{ 
  /* This is a formal class.  No lines of text are contained in any chunk */
  ^nil
}
!!

/* 7/1/1992 11:09 - PUBLIC 
  Return the currentExecPoint of self (e.g. line# or nil).
*/
Def currentExecPoint(self)
{ 
  ^currentExecPoint;
}
!!

/* 06/22/92 - PUBLIC
   Given a chunkNumber >= 1, 
   return a tuple: #(startingLineNum, textCollection).
   If chunk number is beyond the actual data, return nil.
   
   A chunk may be any number of lines of text >= 1.  
   The text may be empty ("").
*/
Def getChunk(self, chunkNumber)
{ 
  ^nil /* this is a formal class.  It is always at the EOD (End Of Data). */
}
!!

/* 7/2/1992 12:57 - PUBLIC (to child objects) */
Def init(self)
{ 
  userAbort     := nil;
  linesPerChunk := 30 ; /* arbitrary chunk size */
  errReported   := GOOD;
  requireWithPath(ProcLib, "proc.dll"); 
  if not(addressSize := maxOutputAddrDigits(AddressLibClass$Inst,0)) then 
    addressSize := 8; /* Default max number of digits per address */
  endif; 
  init(self:ancestor);
}
!!

/* 06/20/92 - PUBLIC 
  Set the language name for token parsing.
*/
Def languageName(self)
{ 
  ^languageName; 
}
!!

/* 06/20/92 - PUBLIC
   Return the number if text lines in self.  Guess if you don't know.
*/
Def numLinesIn(self)
{ 
  ^0   /* this is a formal class.  It always has 0 lines */
}
!!

/* 06/20/92 - PUBLIC 
  Set the language name for token parsing.
*/
Def setLanguageName(self, lang)
{ 
 ^(languageName := asSymbol(lang)); 
}
!!
