/* CLASS: VirtMixedObject
   This Virtual Mixed Object class provides the container to simulate a data 
   object that can hold both source text data and its disassembly data.  It 
   utilizes the VirtFileObject methods to access to a source text file, while the
   VirtDasmObject methods will supply the disassembly data.  The file-size
   of the VirtFileObject will provide the range for chunk management.

   REQUIRE: VIRTDATA.CLS, VIRTFILE.CLS, VIRTDASM.CLS, VIRTSMAR.CLS
   and VIRTSMRT.CLS
*/!!

inherit(VirtDataObject, #VirtMixedObject, #(aFile /* File handle of module */
dasmSession /* Dasm server session */
totalLinesSeen /* source + dasm lines of caching manager */
lineAdjustCallback /* code block to call when line numbers readjusted */

), 2, nil)!!

now(class(VirtMixedObject))!!

/* 7/1/1992 17:01 - PUBLIC
  Open a new Virtual Mixed Object representing the mixed data of a module.
  EX:
    open(VirtMixedObject, newModuleInfo);
*/
Def open(self, newModuleInfo | aVirtualMixedObject)
{ 
  aVirtualMixedObject := new(self:ancestor);
  ^init(aVirtualMixedObject, newModuleInfo);
}
!!

now(VirtMixedObject)!!

/* 7/2/1992 9:10 - PUBLIC 
  Set lineAdjustCallBack of the dataObject.
  When new text is computed, subsequent line numbers may be adjusted.
*/
Def setLineAdjustCallback(self, aCodeBlock)
{ 
  lineAdjustCallback := aCodeBlock ;
}!!

/* 7/2/1992 8:58 - PUBLIC 
   Return number of lines in chunk.
*/
Def getChunkSize(self, chunkNum)
{ 
  /* chunkNum#->#(filePos, sourceLine#, chunkStartLine#, numLinesInChunk) */
  ^at(rememberedLocations,chunkNum)[3]; 
}!!

/* 7/2/1992 8:58 - PUBLIC 
   Return starting line number of chunk.
   The 1st chunks's line numbers range from 1 to linesPerChunk, and so on...
*/
Def getChunkStartLine(self, chunkNum)
{ 
  /* chunkNum#->#(filePos, sourceLine#, chunkStartLine#, numLinesInChunk) */
  ^at(rememberedLocations,chunkNum)[2]; 
}!!

/* 7/9/1992 14:41 - PRIVATE
  Give a guessing number of line in the Virtual Mixed Object. 
  NOTES: To be exact, read the opened text file and find out how many lines that it 
  has. Including the number of disassembly lines of module address range.  This method
  is slow to be useful.
*/
Def guessTotalLines(self, aFile | line, dasmLine, addrRangeDesc, rangeLength)
{ 
  line := 0;  dasmLine := 0;  
  if (line := length(aFile)) < 100 then
    line := 100; /* Too small - so guess it */
  else
    line := asLong(line / 10);
  endif;

  /* Get the address range of module - clone it - Dasm Server will consume it */
  addrRangeDesc := addressRange(self);
  if (rangeLength := getAddrRangeLength(AddressLibClass$Inst, addrRangeDesc)) then 
    /* Guess there are an average of 4 bytes in each dasm instruction */
    dasmLine := asLong(rangeLength / 4);
  endif; 
  /* Clean up addrRangeDesc */
  destroyAddress(AddressLibClass$Inst, addrRangeDesc);
  /* Return the total lines */
  ^line+dasmLine;
}
!!

/* 7/2/1992 9:12 - PRIVATE
  Update total line count by looking at last known location.
*/
Def updateTotalLines(self | lines)
{ 
  if (highestChunkSeen) 
    /* NOTES: rememberedLocations = #(filePos, srcLine, vMixLine, linesInChunk) */
    if (rememberedLocations[highestChunkSeen][3] = 0)                  /* More chunks after this one */
      lines := rememberedLocations[highestChunkSeen][2]                /* starting line of last chunk */
        + ((length(aFile) - rememberedLocations[highestChunkSeen][0])  /* bytes remaining in file */
           /10) * 5; /* 1 source per 10 characters; 5 mixed lines for each source line */
    else
      lines := rememberedLocations[highestChunkSeen][2] + rememberedLocations[highestChunkSeen][3];
    endif;
    setTotalLinesExact(self, lines);
  endif;
  ^totalLinesExact;
}!!

/* 6/25/1992 11:35 - PUBLIC
**
** chunkNumberFromSourceLine
**
** All chunks have a constant number of source lines.  This function
** is then a trivial division.
**   line  1..10  chunk 1
**   line 11..20  chunk 2
**   ...
*/
Def chunkNumberFromSourceLine(self, sourceLineNumber)
{ 
  ^(1 + ((sourceLineNumber-1) / linesPerChunk));

}!!

/* 7/2/1992 13:17 - PRIVATE 
   Build a mixed chunk from a known file location.
    Return: #(lineNumber, TextCollection).
*/
Def buildKnownChunk(self, mixedPos, chunkNumber | mixedCol )
{  
  /* 
  ** Seek to the known Mixed position. NOTES: rememberedLocations is 
  ** chunk#->#(filePos, sourceLine, chunkStartLine, linesInChunk) 
  */
  moveTo(aFile, mixedPos[0]); checkError(aFile);
  moveToDasm(self, mixedPos[1]); /* Move to source line# */

  /* Get  the data text of object */ 
  if (mixedCol := getDataText1(self, chunkNumber)) then
    ^tuple(firstLineOfChunk(self, chunkNumber), mixedCol);  
  endif;
  /* Failed to get data - Something very wrong happening here */
  ^nil;
}!!

/* 6/25/1992 11:35 - PUBLIC
**
** chunkNumberFromLineNumber
**
** Given a (virtual) line number, return the chunk number which contains it.
** NOTES: rememberedLocations = #(filePos, sourceLine, chunkStartLine, numLinesInChunk) 
**
** Chunks have non-constant number of lines.  Therefore, we have to
** find the requested line in a chunk already computed or compute new
** chunks until we find the requested line.
**
** Algorithm:
**   if lineNumber < lastLineNumber(highestChunkSeen)
**      search for chunk containing lineNumber and return it.
**   else
**      while (computeChunk(highestChunkSeen)) {
**        if (lineNumber < highestLine computed) return highestChunkSeen-1
**      }
**      return nil (not enough lines in module)
**       
*/
Def chunkNumberFromLineNumber(self, lineNumber | chunkNum )
{ 
  if highestChunkSeen
       cand (lineNumber <= lastLineOfChunk(self, highestChunkSeen)) 
    assocsDo (rememberedLocations,     
      {using(chunk)
      /* If lineNumber is between start and end of Chunk then return chunk# */
      if (lineNumber >= value(chunk)[2])
            cand (lineNumber <= (value(chunk)[2]+value(chunk)[3])) then
        ^key(chunk);
      endif;
    });
  else
    loop while #true begin
      if highestChunkSeen
        chunkNum := computeChunk(self, highestChunkSeen);
      else
        chunkNum := computeChunk(self, 1);
      endif;
      if not(chunkNum) cor chunkNum = highestChunkSeen  /* At end of file */
        ^highestChunkSeen;
      endif;
      if (lineNumber <= lastLineOfChunk(self,chunkNum)) then
        ^chunkNum;
      endif;
    endLoop;
  endif;
  ^nil;
}!!

/*
** computeChunk:
**   compute rememberedLocations(..) for given chunk without computing text.
**   If necessary, computes previous chunks.
**   Increments highestChunkComputed if necessary.
**   Returns chunk number computed.
**
**   Chunks have a constant number of source lines and a variable number of
**   assembly lines.  Until the text is actually requested, the number of mixed
**   lines in the chunk is estimated at sourcelines + 4*bytesinchunk.  The
**   number of mixed lines is updated by getDataText1 when the text is collected.
*/
Def computeChunk(self, chunkWanted | chunkNum, mixedPos, mixedCol, 
  lineCount, srcLine, fpPos, sourceCount, vMixLine, linesInChunk )
{
  /* Seek as far as we know how - initially highestChunkSeen is nil */
  if chunkNum := highestChunkSeen then  
    /* NOTES: rememberedLocation = [chunkNum->#(filePos, sourceLine, chunkStartLine, numLinesInChunk),...] */    
    if not(mixedPos := at(rememberedLocations, chunkNum)) then ^nil; endif ;
    if (chunkWanted < chunkNum) then ^chunkWanted; endif;   /* Already computed */
    if (rememberedLocations[chunkNum][3] <> 0) then
      if (chunkWanted > highestChunkSeen) then
        ^nil;          /* ChunkWanted is past end of data */
      else 
        ^chunkWanted;  /* ChunkWanted is Last chunk and it's already computed */
      endif;
    endif;
    fpPos     := mixedPos[0];
    srcLine   := mixedPos[1];
    vMixLine  := mixedPos[2];
  else
    /* Begin with chunk #1 and filePointer = 0 - Move to location */
    chunkNum  := 1;
    fpPos     := 0L;
    srcLine   := 1;
    vMixLine  := 1;
    add(rememberedLocations, chunkNum, 
      tuple(position(aFile), srcLine, vMixLine, 0));
    highestChunkSeen := chunkNum ;
  endif ;

  /* Set the filePos and dasm offset to start mixing data */
  moveTo(aFile, fpPos);
  moveToDasm(self, srcLine);
 
  /* Locate the chunkWanted from the highestChunkSeen, find starting line
   * remembering along the way
   */ 
  loop while  not(atEnd(aFile)) cand (chunkNum <= chunkWanted) begin
    /* Get dasm lines plus source lines for a chunk */ 
    if checkAbort(self) then 
      ^nil;
    endif;
    sourceCount := skipLines(aFile, linesPerChunk);
    srcLine := srcLine + sourceCount;
    lineCount := 5*sourceCount;  /* Guess 4 dasm for each 1 source line */
    
    (rememberedLocations[chunkNum])[3] := lineCount;  /* mixed line count */
    
    /* Check termination conditions */
    if atEnd(aFile) then
      updateTotalLines(self);    /* reached end of file...adjust guess of file size */
      if (chunkNum = chunkWanted)
        ^chunkWanted;
      else 
        ^nil ; /* reached end of file before reaching desired chunk */
      endif;
    else
      /* Advance to next chunk */
      chunkNum := chunkNum + 1 ; /* read through next unwanted chunk */
      vMixLine := vMixLine + lineCount; /* Bookeeping the vMixLine seen */
      add(rememberedLocations, chunkNum, 
        tuple(position(aFile), srcLine, vMixLine, 0));
      highestChunkSeen := chunkNum ;
    endif ;
  endLoop;

  ^chunkWanted;
}!!


!!

/* 7/1/1992 17:25 - PUBLIC */
Def close(self)
{ 
  closed? := #true;
  close(aFile);
  if dasmSession then
    closeDasm(DisAsmLibClass$Inst, dasmSession);
  endif;
  dasmSession := nil;  
}!!

/* 7/2/1992 8:58 - PRIVATE 
   Return starting line number of chunk.
   The 1st chunks's line numbers range from 1 to linesPerChunk, and so on...
*/
Def firstLineOfChunk(self, chunkNum)
{ 
  /* chunkNum#->#(filePos, sourceLine#, chunkStartLine#, numLinesInChunk) */
  ^at(rememberedLocations,chunkNum)[2]; 
}!!

/* 7/2/1992 8:58 - PRIVATE 
   Return ending line number of chunk.
*/
Def lastLineOfChunk(self, chunkNum)
{ 
  /* chunkNum#->#(filePos, sourceLine#, chunkStartLine#, numLinesInChunk) */
  ^at(rememberedLocations,chunkNum)[2] + at(rememberedLocations,chunkNum)[3] - 1; 
}!!

/* 7/1/1992 17:27 - PUBLIC
  Flush the cach memory.
*/
Def flushCachedMemory(self)
{ 
  highestChunkSeen := totalLinesExact  := nil;
  if rememberedLocations then
    clear(rememberedLocations);
  else  
    /* chunk#->#(filePos, sourceLine, chunkStartLine, linesInChunk) */
    rememberedLocations := new(Dictionary, 2); 
  endif;  
}!!

/* 7/5/1992 15:46 - PRIVATE
  Map a source line to its corresponding address range.  
  Return tuple #(addrRangeDesc, actualLine#, indexDesc) or nil.
*/
Def getAddrRangeOfLine(self, lineNum | lineInfo tmpDesc)
{ 
  if not(lineInfo := getLineNumInfoOrder(SymbolLibClass$Inst,
      moduleDescriptor(self), lineNum)) then
    ^nil;
  endif;
  /* NOTES: Nghia - 05/27/93 - Fixed bug for Bendix King
  ** Do not subtract offset if the start and end offsets are the same
  */
  if (compareOffsets(AddressLibClass$Inst,
      getOffset(AddressLibClass$Inst, lineInfo[0]),
      getEndOffset(AddressLibClass$Inst, lineInfo[0])) = ADRLIB_ADDR_EQUAL) then
    /* NOTES: lineInfo = #(addrRangeDesc, lineNum, lineNumDesc) */
    ^tuple(lineInfo[0], lineInfo[1], lineInfo[2]);      
  endif;
  
  /* HACK - take off 1 bytes from the endOffset */
  if not(tmpDesc := createAndSetAddress(AddressLibClass$Inst, 
                    getEndOffset(AddressLibClass$Inst, lineInfo[0])))
    ^nil;
  endif;
  
  if not(subtractFromAddress(AddressLibClass$Inst, tmpDesc, 1)) 
    destroyAddress(AddressLibClass$Inst, tmpDesc);
    ^nil;
  endif;
  
  setEndAddrOffset(AddressLibClass$Inst, lineInfo[0], 
                   getOffset(AddressLibClass$Inst, tmpDesc));
  
  destroyAddress(AddressLibClass$Inst,tmpDesc);
    
  /* NOTES: lineInfo = #(addrRangeDesc, lineNum, lineNumDesc) */
  ^tuple(lineInfo[0], lineInfo[1], lineInfo[2]);  
}
!!

/* 7/2/1992 9:00 - PUBLIC
   Given a chunkNumber >= 1, return a tuple: #(startingLineNum, textCollection).
   If chunk number is beyond the actual data, return nil.

   NOTES: When we reach EOF (End Of File), set totalLinesExact.
*/
Def getChunk(self, chunkNumber | mixedPos, chunk)
{ 
  if checkAbort(self) then
    ^nil;
  endif;
  if chunkNumber := computeChunk(self, chunkNumber) then
     if mixedPos := at(rememberedLocations, chunkNumber) then
        ^buildKnownChunk(self, mixedPos, chunkNumber);
     endif;
  endif;
  ^nil;
}!!

/* 7/2/1992 16:06 - PRIVATE
  Collect all of the dasm instruction text of a source line 
  including the one out of order. 
  Return a #(TextCollection, lineInfo) or nil.
*/
Def getDasmText1(self, sourceLine, lineInfo | nextSourceLine, dasmCol, textCol, err)
{ 
  /* NOTES: lineInfo = #(addrRangeDesc, mapSourceLine, nextLineDesc); */
  /* Collect all of the out-of-order source line Number */
  dasmCol := new(TextCollection, linesPerChunk);
  nextSourceLine := lineInfo[1];
  loop
    /* Call Dasm Server to return the number of Dasm line within that range */
    textCol := getDasmRangeInstructions(DisAsmLibClass$Inst, dasmSession, lineInfo[0]);
    if not(textCol) then
      if size(dasmCol) > 0 then ^tuple(dasmCol, nil); endif;
      /* Report error accessing DASM server */
      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;
      endif;    
      ^nil; /* SomeThing wrong here - Dasm server should not return nil */
    endif;  

    /* Reformat the dasm text to include source line # */
    do ( over(0, size(textCol)),
      {using(line) 
        textCol[line] := format("[%6.6u] ", sourceLine) + textCol[line];
      });
    /* Insert the new dasm text into the collection */
    insertAll(dasmCol, textCol, size(dasmCol));
    textCol := nil;

    /* Get the next line info using the index descriptor */
    if not(lineInfo := getNextLineByIndex(self, lineInfo[2])) then
      ^tuple(dasmCol, lineInfo);
    endif;  
 
    /* NOTES: lineInfo = #(addrRangeDesc, actualSourceLine, nextLineDesc) */    
    nextSourceLine := lineInfo[1];
  while (nextSourceLine < sourceLine) /* Include all out of order lines */
  begin 
  endLoop;
  errReported := GOOD;
  ^tuple(dasmCol, lineInfo);
}!!

/* 7/6/1992 11:05 - PRIVATE 
  Get data text of the whole chunk start from the SourceLine.  
    Return a mixed source and dasm textCollection or nil.
*/
Def getDataText1(self chunkNum | sourceLine, lastSrcLine, 
  txtColl, str, line, mixedCol, lineInfo, lineAdjustment, oldFirstLine)
{
  /* Get the chunkWanted - Allocate a new TextCollection */
  mixedCol := new(TextCollection, asInt(rememberedLocations[chunkNum][3]));
  sourceLine := rememberedLocations[chunkNum][1];
  lastSrcLine := sourceLine + linesPerChunk - 1;
  line := 0;
  if checkAbort(self) then ^nil; endif;
  
  lineInfo := getAddrRangeOfLine(self, sourceLine);
    /* NOTES: lineInfo = #(addrRangeDesc, mapSourceLine, nextLineDesc); */

  loop while not(atEnd(aFile)) cand (sourceLine <= lastSrcLine) begin 
    checkError(aFile);
    
    /* Add source text to collection */
    str  := readLine(aFile); 
    add(mixedCol, format("[%6.6u] ", sourceLine)+str);
    
    /* Add dasm text, if any, to collection */
    if lineInfo cand (sourceLine = lineInfo[1])   /* There is dasm code on this line */
      txtColl := getDasmText1(self, lineInfo[1], lineInfo);
        /* NOTES: txtColl := #(dasmCol, lineInfo = #(addrDesc, nextSourceLine, nextIndx)) */
      if txtColl cand txtColl[0]  /* dasm succeeded...add text to collection */
        insertAll(mixedCol, txtColl[0], size(mixedCol));
        line := line + size(txtColl[0]);
        lineInfo := txtColl[1];
      endif;
    endif;
    
    sourceLine := sourceLine + 1;
    line := line + 1;
  endLoop;
  
  if (lineInfo) destroyAddress(AddressLibClass$Inst, lineInfo[0]); endif;
 
  /* Update number of lines in this chunk */
  lineAdjustment := line - rememberedLocations[chunkNum][3];
  rememberedLocations[chunkNum][3] := line; 
  if lineAdjustment <> 0
    oldFirstLine := rememberedLocations[chunkNum][2];
    /* Update starting line number for all subsequent chunks */
    chunkNum := chunkNum+1;
    loop while (chunkNum <= highestChunkSeen) begin
      rememberedLocations[chunkNum][2] := rememberedLocations[chunkNum][2] + lineAdjustment;
      chunkNum := chunkNum+1;
    endLoop;
    updateTotalLines(self);
    if lineAdjustCallback cand oldFirstLine then
      eval(lineAdjustCallback, oldFirstLine, lineAdjustment);
    endif;
  endif;

  ^mixedCol;
}
!!

/* 7/14/1992 11:23 - PRIVATE
  Get the line number information of a lineNumber index descriptor.
    Return #(addrDesc, lineNum, indxDesc);
*/
Def getNextLineByIndex(self, nextIndex | lineNumInfo tmpDesc)
{ 
  /* NOTES: lineNumInfo = #(addrDesc, nextIndxDesc, actualLineNum, actualColNum) */
  if not(lineNumInfo := getLineNumByIndex(SymbolLibClass$Inst, moduleDescriptor(self),
    nextIndex)) then
    ^nil;
  endif;

  /* NOTES: Nghia - 05/27/93 - Fixed bug for Bendix King
  ** Do not subtract offset if the start and end offsets are the same
  */
  if (compareOffsets(AddressLibClass$Inst,
      getOffset(AddressLibClass$Inst, lineNumInfo[0]),
      getEndOffset(AddressLibClass$Inst, lineNumInfo[0])) = ADRLIB_ADDR_EQUAL)
    then
    ^tuple(lineNumInfo[0], lineNumInfo[2], lineNumInfo[1]);      
  endif;

  /* HACK - take off 1 bytes from the endOffset */
  if not(tmpDesc := createAndSetAddress(AddressLibClass$Inst, 
                    getEndOffset(AddressLibClass$Inst, lineNumInfo[0])))
    ^nil;
  endif;
  
  if not(subtractFromAddress(AddressLibClass$Inst, tmpDesc, 1)) 
    destroyAddress(AddressLibClass$Inst, tmpDesc);
    ^nil;
  endif;
  
  setEndAddrOffset(AddressLibClass$Inst, lineNumInfo[0], 
                   getOffset(AddressLibClass$Inst, tmpDesc));
  
  destroyAddress(AddressLibClass$Inst,tmpDesc);

  ^tuple(lineNumInfo[0], lineNumInfo[2], lineNumInfo[1]);      
}
!!

/* 7/1/1992 17:05 - PRIVATE
  Initialize a virtual mixed data object. openMixed() does most of the work.
*/
Def init(self, newModuleInfo)
{ 
  init(self:ancestor);
  linesPerChunk := 10;   /* 10 source lines per chunk */
  ^openMixed(self, newModuleInfo);
}!!

/* 7/5/1992 15:12 - PRIVATE
  Move the current offset of Dasm server to new location of
  source line.
*/
Def moveToDasm(self, sourceLineNum | lineInfo, ret)
{ 
  /* NOTES: lineInfo = #(addrRangeDesc, mapSourceLine#, nextLineDesc) */
  if not(lineInfo := getAddrRangeOfLine(self, sourceLineNum)) then
    ^nil;
  endif;
  
  /* Move to the desired offset position for the DASM server */
  ret := setAddress(DisAsmLibClass$Inst, dasmSession, lineInfo[0]);
  /* Destroy the unused address descriptor */
  destroyAddress(AddressLibClass$Inst, lineInfo[0]);
  ^ret; /* either GOOD or nil */
}
!!

/* 7/2/1992 9:05 - PUBLIC
  Return the number if text lines in self. Guess if don't know.
*/
Def numLinesIn(self)
{ 
  /* if we know how many lines exist, say so--else guess */
  if totalLinesExact then 
    ^totalLinesExact ;
  else 
    ^totalLinesGuess ;
  endif ; 
}!!

/* 7/2/1992 13:14 - PRIVATE 
  Open a file for a VirtMixedObject.
    NOTES: require TheSourcePresenter.
*/
Def openFile(self, aFileName) {
  /* Handle unix files differently */
  if unixFile?(aFileName) then
    if not(aFile := unixOpenFile(CLIULibraryClass$Inst, aFileName))
      beep();
      displayFormattedError(ErrorTextLibClass$Inst, 
         ER_CANT_OPEN_UNIX_FILE, FORCE_POPUP,  ": " + aFileName, nil, nil);
      ^nil;
    endif;
  else
    aFile := new(TextFile);
    if TheSourcePresenter then
      setDelimiter(aFile, sourceDelimiter(TheSourcePresenter));
    else
      setDelimiter(aFile, CR_LF);
    endif;  
    setName(aFile, aFileName);
    open(aFile, 0); 
    if (getError(aFile) <> 0) then
      beep();
      displayFormattedError(ErrorTextLibClass$Inst, 
        ER_CANT_OPEN_SOURCE_FILE, FORCE_POPUP, ": " + aFileName, nil, nil);
      ^nil 
    endif ;
  endif;
  setModulePath(self, aFileName);  
  ^GOOD;    
}
    !!

/* 7/2/1992 13:07 - PRIVATE
  Open a VirtMixedObject (Called by open()).
*/
Def openMixed(self, moduleInfo)
{ 
  lineAdjustCallback := nil;
  if mixedHighestChunk(moduleInfo) then
    ^openMixedWithInfo(self, moduleInfo);
  endif;    
  ^openMixedFirstTime(self, moduleInfo);  
}!!

/* 7/2/1992 13:07 - PRIVATE
  Open a VirtMixedObject for the module the first time (Called by open()). 
*/
Def openMixedFirstTime(self, aModuleInfo | fPath)
{ 
  /* Cannot open the data object if filePath is nil */
  if not(fPath := filePath(aModuleInfo)) then
    ^nil;
  endif;
  if not(openFile(self, fPath)) cor
    not(dasmSession := openDasm(DisAsmLibClass$Inst, addressRange(aModuleInfo))) then
    ^nil;
  endif;
  
  totalLinesSeen := nil;    
  /* NOTES: rememberedLocations = [chunk#->#(filePos, sourceLineNum, chunkStartLine) */
  rememberedLocations := new(Dictionary, 2);   
  /* Guess how many lines the mixed object has */
  totalLinesExact := nil;
  totalLinesGuess := guessTotalLines(self, aFile);
  highestChunkSeen := nil;
}!!

/* 7/2/1992 13:07 - PRIVATE
  Open a VirtMixedObject for the module the first time (Called by open()). 
*/
Def openMixedWithInfo(self, moduleInfo | fPath)
{ 
  /* Cannot open the data object if filePath is nil */  
  if not(fPath := filePath(moduleInfo)) then
    ^nil;
  endif;
  if not(openFile(self, fPath)) cor
    not(dasmSession := openDasm(DisAsmLibClass$Inst, addressRange(moduleInfo))) then  
    ^nil;
  endif;
  totalLinesSeen := nil;    
  /* NOTES: rememberedLocations = [chunk#->#(filePos, sourceLineNum, chunkStartLine) */
  rememberedLocations := mixedLocations(moduleInfo);   
  /* Get how many lines the mixed object has */
  totalLinesGuess := totalMixedLinesGuess(moduleInfo);
  totalLinesExact := totalMixedLinesExact(moduleInfo);
  highestChunkSeen := mixedHighestChunk(moduleInfo);
}!!

/* 7/2/1992 9:06 - PUBLIC
 Reopen a previously opened mixed object.
*/
Def reOpen(self, aModuleInfo | success)
{
  if closed? then
    /* handle unix files differently */
    if unixFile?(aFile.fileName)
      aFile := unixOpenFile(CLIULibraryClass$Inst, aFile.fileName);
      success := #true;
    else
      open(aFile, 0);
      success := ((checkError(aFile) = 0) cand
        aModuleInfo cand
        (dasmSession :=
         openDasm(DisAsmLibClass$Inst, addressRange(aModuleInfo))));
    endif;

    if success
      closed? := nil;
    else
      displayFormattedError(ErrorTextLibClass$Inst, 
         ER_CANT_REOPEN_MIXED, FORCE_POPUP, ".", nil, nil);
      ^nil;
    endif;  

  endif;
  ^self;    
}
!!

/* 7/2/1992 9:10 - PUBLIC 
  Set resizeCallBack of the dataObject. Adjustment is needed for mixed mode.
*/
Def setResizeCallback(self, aResizeBlock)
{ 
  resizeCallback := aResizeBlock ;
}!!

/* 7/2/1992 9:12 - PRIVATE
  Set the totalLineExact to numLines and resize it.
*/
Def setTotalLinesExact(self, numLines)
{ 
  totalLinesExact := numLines;
  setModuleTotalLines(self, numLines);
  if resizeCallback then
    eval(resizeCallback, numLines);
  endif;
}!!
