/* class comment */!!

inherit(EditWindow, #MBrowser, #(addrBegin /* col where address displayed */
addrEnd /* last col of address */
memBegin /* col of first mem element */
memRepeatCount /* # mem elements */
memByteGrouping /*1:byte, 2:word, 4:long */
memDisplaySize /* width of display element */
memDisplaySizePlus1 /* just what it says */
memMaxDisplayUnit /* max display value */
scrollOffset scrollDivisor
  /* scroll range; offset must be positive */
memEnd /* last col of mem elements */
memRadix /* memory display radix */
asciiBegin /* col where ascii displayed */
asciiLength /* length of ascii display */
asciiEnd /* last col of ascii */
lineLength /* length of display line */
maxRow /* largest memory row addr */
minRow /* smallest memory row addr */
workTextTop /* row addr at top of workText */
workTextEnd /* row addr at end of workText */
workTextSize /* number of rows in workText */
workTextBytes /* byte equivalent of screen */
desiredScreenTop /* new screenTop, subject to cancel */
screenTop /* row address at top of screen */
screenEnd /* row addr at bottom of screen */
initializeCalled /* true if initialize called */
formatMemoryDisplay
  /* disp. size specific format mem method */
updateMemoryDisplayUnit /* method for ... */
updateByteArraySize /* byte array method */
displayField /* 0:address, 1:memory, 2:ascii */
cursorWasInAddressArea /* true if ... */
linesOnScreen /* visLines */
dirtyBytes /* info on dirty bytes */
dirtyByteRow  /* row containing db's */
dirtyByteColMin /* min column with db's */
dirtyByteColMax /* max col with db's */
cacheInvalid /* true if cache invalid */
ignoreMemoryChangeEvent
/* true if mem chg event should be ignored */
byteArray /* for writing to server */
inFocus /* true if in focus */
maxCache /* max lines in workText cache */
addrDisplayDigits /* #address display digits */
readError /* true if read err on last mem read */
tempWord tempDWord  /* to format mem */
baseAddressDescriptor /* A. D. for min offset */
workAddressDescriptor /* A. D. for this offset */
minOffset maxOffset /* for scroll range */
errorDetected?  /* unable to paint screen */), 2, nil)!!

setClassVars(MBrowser, #())!!

now(class(MBrowser))!!

now(MBrowser)!!

Def doGotoPhysical(self, line | theDlg lineOffset charOffset temp resourceId 
workAddrDescriptor)
{
  workAddrDescriptor := convertAddressToType(AddressLibClass$Inst,
    baseAddressDescriptor, ADDR_PHYSICAL);
  if not(addToAddress(AddressLibClass$Inst, workAddrDescriptor,
     (screenTop + line) * 16L))
    ^nil;
  endif;

  /* get offset, abort if error (msg already posted) */
  if not(temp := getOffset(AddressLibClass$Inst, workAddressDescriptor))
    ^nil;
  endif;
  
  /* see if segment limits et al need to be updated */
  if not(getSegmentInfo(self, workAddrDescriptor, nil))
    ^nil;
  endif;
  ^gotoOffset(self, temp);
}
!!
/*Fixed bug ID#234 by Eric 2/16/98*/ 
Def doGotoVirtual(self, line, workAddrDescriptor | theDlg lineOffset
charOffset temp resourceId)
{
  if not(addToAddress(AddressLibClass$Inst, workAddrDescriptor,
     (screenTop + line) * 16L))
    ^nil;
  endif;

  /* get offset, abort if error (msg already posted) */
  if not(temp := getOffset(AddressLibClass$Inst, workAddressDescriptor))
    ^nil;
  endif;
  
  /* see if segment limits et al need to be updated */
  if not(getSegmentInfo(self, workAddrDescriptor, nil))
    ^nil;
  endif;
  ^gotoOffset(self, temp);
}
!!
/*eof Eric 2/16/98*/ 

/* Respond to mode change. */
Def updateAddrMode(self)
{
  /* update segment limits et al */
  if not(getSegmentInfo(self, baseAddressDescriptor, 0))
    /* if failed, set type to physical, and try again... */
    errorDetected? := nil;
    if not(setAddrType(AddressLibClass$Inst, baseAddressDescriptor,
       ADDR_PHYSICAL)) cor
       not(getSegmentInfo(self, baseAddressDescriptor, 0))
         cleanUp(self);
         ^nil;
    endif;
  endif;
  ^0;
}!!

/* close. */
Def close(self | temp)
{
  cleanUp(self);
  close(self:ancestor);
}!!

/* clean up allocated objects. */
Def cleanUp(self | temp)
{
  if baseAddressDescriptor
    destroyAddress(AddressLibClass$Inst, baseAddressDescriptor);
    baseAddressDescriptor := nil;
  endif;
  
  if workAddressDescriptor
    destroyAddress(AddressLibClass$Inst, workAddressDescriptor);
    workAddressDescriptor := nil;
  endif;
}!!

/* Return a tuple with the current cursor horizontal position information:
   tuple[0]: 1 for memory field, 2 for ascii field
   tuple[1]: current byte position within the line */
Def getCursorInformation(self | field byteOffset)
{
  /* return default if we're starting up */
  if not(initializeCalled)
    ^tuple(1, 0);  /* byte 0 in memory area */
  endif;
  
  field := displayField;
  if field = 2 /* ascii */
    /* can use index as byte offset because 1-to-1 */
    byteOffset := mapAsciiFieldOffsetToIndex(self, xPos + leftChar);
  else
    field := 1; /* memory */
    byteOffset := mapXPosToByteOffset(self, xPos + leftChar);
  endif;
  if not(byteOffset) cor (byteOffset < 0) cor (byteOffset > 15)
    byteOffset := 0;
  endif;
  ^tuple(field, byteOffset);
}
!!

/* Given the index of the memory display element, return the beginning
   offset into the workText line.
   (formerly getMemFieldOffset) */
Def mapMemFieldIndexToOffset(self, x)
{
  if not(memBegin) cor not(memDisplaySizePlus1)
    ^0;
  endif;
  ^asInt((memBegin + (x * memDisplaySizePlus1)));
}!!

/* Given the index of the ascii display element, return the beginning
   offset into the workText line.
   (formerly getAsciiFieldOffset) */
Def mapAsciiFieldIndexToOffset(self, x)
{
  ^asInt((asciiBegin + x));
}!!

/* Given the offset into the workText line, return the index of the
   ascii display element.
   (formerly getAsciiFieldIndex) */
Def mapAsciiFieldOffsetToIndex(self, x)
{
  ^asInt((x - asciiBegin));
}!!

/* Given the offset into the workText line, return the index of the
   memory display element.
   (formerly called getMemFieldIndex) */
Def mapMemFieldOffsetToIndex(self, x)
{
  if not(memBegin) cor not(memDisplaySizePlus1)
    ^0;
  endif;
  ^asInt(((x - memBegin) / memDisplaySizePlus1));
}!!

/* Given the (absolute) horizontal cursor position in the memory field,
   return the byte offset. */
Def mapXPosToByteOffset(self, horizPos |
      temp charFudge byteFudge fieldIndex byteOffset)
{
  fieldIndex := mapMemFieldOffsetToIndex(self, horizPos);
    /* fieldIndex of memory display element. */
  if memRadix = 10
    /* if decimal, start of the display unit is the best we can do */
    byteOffset := (fieldIndex * memByteGrouping);
  else
    temp := mapMemFieldIndexToOffset(self, fieldIndex);
    charFudge := horizPos - temp;
      /* #display chars between cursor pos and start of group */
    byteFudge := charFudge / 2;
      /* #bytes between cursor pos and start of group */
    if (TheProcFamily = PROC_FAMILY_X86)
      byteOffset := (fieldIndex * memByteGrouping) + mapEndianByteOffset(self, byteFudge);
    else
      byteOffset := (fieldIndex * memByteGrouping) + byteFudge;
    endif;
  endif;
  ^byteOffset
}!!

/* Given the byte offset, return the horizontal cursor position. */
Def mapByteOffsetToXPos(self, byteOffset | temp cursorPos)
{
  /* place cursor on proper column */
  if (TheProcFamily = PROC_FAMILY_X86)
    byteOffset := mapEndianByteOffset(self, byteOffset);
  endif;
  temp := byteOffset / memByteGrouping;  /* temp is # display units */
  cursorPos := addrDisplayDigits + 2 + /* addr + separating spaces */
    (temp * memDisplaySize) +  /* # of display units x size */
    temp;  /* # of display unit separators */
    
  /* if this is hex, go to actual byte; forget about decimal. */
  if memRadix = 16
    temp := byteOffset mod memByteGrouping;
    /* to move over a byte, move two char positions */
    cursorPos := cursorPos + (2 * (byteOffset mod memByteGrouping));
  endif;
  ^(cursorPos - leftChar);
}!!

/* input is tuple returned by getCursorPosition:
   field 0: 1 (memory) or 2 (ascii)
   field 1: byte position */
Def setCursorPosition(self, cursorInfo)
{
  if not(cursorInfo) cor (size(cursorInfo) <> 2) cor
    not(cursorInfo[0]) cor not(cursorInfo[1])
      ^nil;
  endif;
  if cursorInfo[0] = 2 /* ascii */
    xPos := mapAsciiFieldIndexToOffset(self, cursorInfo[1]) - leftChar;
  else /* memory */
    xPos := mapByteOffsetToXPos(self, cursorInfo[1]);
  endif;
  cleanUpCursor(self);
}
!!

/* Map byte offset index from one endian to the other (this is reversable). */
Def mapEndianByteOffset(self, x | temp)
{
  select
    case x = nil
      ^nil;
    endCase
    case memByteGrouping = 1 is ^x; endCase;
    case memByteGrouping = 2
      temp := x mod 2;
      select
        case temp = 0 is ^(x + 1); endCase;
        case temp = 1 is ^(x - 1); endCase;
      endSelect;
    endCase;
    default
      /* memByteGrouping = 4 */
      temp := x mod 4;
      select
        case temp = 0 is ^(x + 3); endCase;
        case temp = 1 is ^(x + 1); endCase;
        case temp = 2 is ^(x - 1); endCase;
        case temp = 3 is ^(x - 3); endCase;
      endSelect;
  endSelect;
}!!

/* 7/5/1994 15:24 */
Def debugScroll(self, pos)
{
  WM_VSCROLL(self, SB_THUMBPOSITION, pos);
  ^0;
}!!

/* 7/5/1994 15:24 */
Def debugWorkTextBottom(self)
{
  if not(workText)
    ^nil;
  endif;
  ^workText[size(workText)-1];
}!!

/* 7/5/1994 15:24 */
Def debugWorkTextTop(self)
{
  if not(workText)
    ^nil;
  endif;
  ^workText[0];
}!!

/* position to offset within current display range. */
Def gotoOffset(self, offset | lineOffset charOffset)
{
  lineOffset := displayRow(self, offset);
  charOffset := abs(offset mod 16);
  desiredScreenTop := lineOffset;
  yPos := 0;
  xPos := charOffset;
  /* correct if bad */
  if (desiredScreenTop < 0)
    yPos := yPos + desiredScreenTop;
    desiredScreenTop := 0;
  endif;
  fixScreen(self, nil);

  /* position the cursor on the byte */
  placeCursor(self, lineOffset, charOffset);

  ^0;
}!!

/* 6/23/1994 14:07 return work address descriptor. */
Def workAddressDescriptor(self)
{
  ^workAddressDescriptor;
}!!

/* 6/23/1994 14:07 return base address descriptor. */
Def baseAddressDescriptor(self)
{
  ^baseAddressDescriptor;
}!!

/* Invalidate the cache, but don't call fixScreen; assume this will
   happen later. */
Def invalidateCacheDeferred(self | index)
{
  desiredScreenTop := screenTop;
  workTextTop := nil;  /* make fixWorkTextMemory think nothing is there */
  workTextEnd := 0;  /* make fixWorkTextMemory think nothing is there */
  ^0;
}!!

/* construct and return the indicated word string. */
/* rowIndex is absolute */
Def formatLittleEndianMemDisplayWord(self, rowIndex | string lWord index temp)
{
  string := "";
  index := 0;
  loop
  while index < memRepeatCount
  begin
    temp := index*memByteGrouping;
    /* note: this is moto endian */
    putByte(tempWord, workTextBytes[asInt(rowIndex)][asInt(temp)], 0);
    putByte(tempWord, workTextBytes[asInt(rowIndex)][asInt(temp+1)], 1);

    /* convert to long, and mask off potential sign extension;
       use Long:asUnsignedStringRadix directly, not MBrowser copy. */
    lWord := asLong(wordAt(tempWord, 0)) bitAnd 0x0000ffff;
    string := string + right(
      asUnsignedStringRadix(asLong(lWord), memRadix),
      memDisplaySize, "0") + " ";
    index := index + 1;
  endLoop;
  ^string;
}!!

/* stuff the bytes into the byte array. */
Def updateBigEndianByteArrayDWord(self, value, fieldIndex |
  startIndex tempStruct temp)
{
  tempStruct := new(Struct, 4);
  putLong(tempStruct, value, 0);
  startIndex := fieldIndex * memByteGrouping;

  temp := asInt(yPos+topLine);
  workTextBytes[temp][asInt(startIndex)]   := byteAt(tempStruct, 3);
  workTextBytes[temp][asInt(startIndex+1)] := byteAt(tempStruct, 2);
  workTextBytes[temp][asInt(startIndex+2)] := byteAt(tempStruct, 1);
  workTextBytes[temp][asInt(startIndex+3)] := byteAt(tempStruct, 0);
  
  markDirtyBytes(self, yPos + topLine, startIndex, 4);
}!!

/* stuff the bytes into the byte array. */
Def updateLittleEndianByteArrayDWord(self, value, fieldIndex |
  startIndex tempStruct temp)
{
  tempStruct := new(Struct, 4);
  putLong(tempStruct, value, 0);
  startIndex := fieldIndex * memByteGrouping;

  temp := asInt(yPos+topLine);
  workTextBytes[temp][asInt(startIndex)]   := byteAt(tempStruct, 0);
  workTextBytes[temp][asInt(startIndex+1)] := byteAt(tempStruct, 1);
  workTextBytes[temp][asInt(startIndex+2)] := byteAt(tempStruct, 2);
  workTextBytes[temp][asInt(startIndex+3)] := byteAt(tempStruct, 3);
  
  markDirtyBytes(self, temp, startIndex, 4);
}!!

/* stuff the bytes into the byte array. */
Def updateLittleEndianByteArrayWord(self, value, fieldIndex |
  startIndex tempStruct temp)
{
  tempStruct := new(Struct, 2);
  putWord(tempStruct, value, 0);
  startIndex := fieldIndex * memByteGrouping;

  temp := asInt(yPos+topLine);
  workTextBytes[temp][asInt(startIndex)]   := byteAt(tempStruct, 0);
  workTextBytes[temp][asInt(startIndex+1)] := byteAt(tempStruct, 1);

  markDirtyBytes(self, temp, startIndex, 2);
}!!

/* stuff the bytes into the byte array. */
Def updateBigEndianByteArrayWord(self, value, fieldIndex |
  startIndex tempStruct temp)
{
  tempStruct := new(Struct, 2);
  putWord(tempStruct, value, 0);
  startIndex := fieldIndex * memByteGrouping;

  temp := asInt(yPos+topLine);
  workTextBytes[temp][asInt(startIndex)]   := byteAt(tempStruct, 1);
  workTextBytes[temp][asInt(startIndex+1)] := byteAt(tempStruct, 0);
  
  markDirtyBytes(self, temp, startIndex, 2);
}!!

/* construct and return the indicated word string. */
/* rowIndex is absolute */
Def formatBigEndianMemDisplayWord(self, rowIndex | string lWord index temp)
{
  string := "";
  index := 0;
  loop
  while index < memRepeatCount
  begin
    temp := index*memByteGrouping;
    /* note: this is moto endian */
    putByte(tempWord, workTextBytes[asInt(rowIndex)][asInt(temp)], 1);
    putByte(tempWord, workTextBytes[asInt(rowIndex)][asInt(temp+1)], 0);

    /* convert to long, and mask off potential sign extension;
       use Long:asUnsignedStringRadix directly, not MBrowser copy. */
    lWord := asLong(wordAt(tempWord, 0)) bitAnd 0x0000ffff;
    string := string + right(
      asUnsignedStringRadix(asLong(lWord), memRadix),
      memDisplaySize, "0") + " ";
    index := index + 1;
  endLoop;
  ^string;
}!!

/* construct and return the indicated dword string. */
/* rowIndex is absolute */
Def formatLittleEndianMemDisplayDword(self, rowIndex | string index temp)
{
  string := "";
  index := 0;
  loop
  while index < memRepeatCount
  begin
    temp := index*memByteGrouping;
    
    /* note: this is intel endian */
    putByte(tempDWord, workTextBytes[asInt(rowIndex)][asInt(temp)], 0);
    putByte(tempDWord, workTextBytes[asInt(rowIndex)][asInt(temp+1)], 1);
    putByte(tempDWord, workTextBytes[asInt(rowIndex)][asInt(temp+2)], 2);
    putByte(tempDWord, workTextBytes[asInt(rowIndex)][asInt(temp+3)], 3);
    string := string + right(
      asUnsignedStringRadix(asLong(longAt(tempDWord, 0)), memRadix),
      memDisplaySize, "0") + " ";
    index := index + 1;
  endLoop;
  ^string;
}!!

/* construct and return the indicated dword string. */
/* rowIndex is absolute */
Def formatBigEndianMemDisplayDword(self, rowIndex | string index temp)
{
  string := "";
  index := 0;
  loop
  while index < memRepeatCount
  begin
    temp := index*memByteGrouping;
    
    /* note: this is moto endian */
    putByte(tempDWord, workTextBytes[asInt(rowIndex)][asInt(temp)], 3);
    putByte(tempDWord, workTextBytes[asInt(rowIndex)][asInt(temp+1)], 2);
    putByte(tempDWord, workTextBytes[asInt(rowIndex)][asInt(temp+2)], 1);
    putByte(tempDWord, workTextBytes[asInt(rowIndex)][asInt(temp+3)], 0);
    string := string + right(
      asUnsignedStringRadix(asLong(longAt(tempDWord, 0)), memRadix),
      memDisplaySize, "0") + " ";
    index := index + 1;
  endLoop;
  ^string;
}!!

/* get info on this address, update address information, recalculate
   scroll range info if any address information changed. */
Def getSegmentInfo(self, addressDescriptor, initFlag | temp segChanged rc)
{
  invalidateCacheDeferred(self);  /* make sure to re-display */
  if not(temp := getAddressLimitsNoError(AddressLibClass$Inst, addressDescriptor))
    errorDetected? := 0;
    ^nil;
  endif;
  minOffset := temp[0];
  rc := getAddressType(AddressLibClass$Inst, addressDescriptor);
  if (rc = 2)
    maxOffset := temp[1];
  else
    maxOffset := 0xFFFF;
  endif;
  
  /* calculate the min and max row index */
  maxDisplayRows(self);
  
  /* adjust scroll parms so they don't exceed Actor's 32-bit signed */
  scrollOffset := maxRow;
  scrollDivisor := 1;
  loop
  while (scrollOffset < 0)
    scrollDivisor := scrollDivisor * 10;
    temp := asUnsignedStringRadix(scrollOffset, 10);
    scrollOffset := asInt(subString(temp, 0, size(temp)-1), 10);
  endLoop;
  
  addrDisplayDigits := maxOutputAddrDigits(AddressLibClass$Inst,
    addressDescriptor);
    
  /* update other display offsets */
  calcDisplayAttributes(self);
  
  /* update base address descriptor */
  if not(initFlag)
    if not(copyAddress(AddressLibClass$Inst, addressDescriptor,
      baseAddressDescriptor))
        errorDetected? := 0;
        ^nil;
    endif;
    if not(setOffset(AddressLibClass$Inst, baseAddressDescriptor,
      minOffset))
        errorDetected? := 0;
        ^nil;
    endif;
  endif;
}!!

/* return next x,y point; 3rd element is 0 to show display item changed. */
Def getPreviousCharPosAscii(self, x, y)
{
  if (x+leftChar) = asciiBegin
    ^tuple(asciiEnd - leftChar, y-1, 0);
  else
    ^tuple(x-1, y, 0);
  endif;
}!!

/* return previous x,y point; 3rd element true if display element changed */
Def getPreviousCharPosMem(self, x, y)
{
  if (x + leftChar) = memBegin
    ^tuple(memEnd - leftChar, y-1, 0);
  else
    if spaceChar(self, x + leftChar - 1)
      ^tuple(x-2, y, 0);
    else
      ^tuple(x-1, y, nil);
    endif;
  endif;
}!!

Def cacheInvalidClear(self)
{
  cacheInvalid := nil;
}!!

Def cacheInvalidSet(self)
{
  cacheInvalid := 0;
}!!

/* Filter out undesireable behaviors. */
Def command(self, wP, lP)
{ select
    case wP == EDIT_CUT cor
         wP == EDIT_COPY cor
         wP == EDIT_PASTE cor
         wP == EDIT_CLEAR cor
         wP == EDIT_SELALL cor
         wP == EDIT_SRCH cor
         wP == EDIT_RPLC cor
         wP == RPLC_ALL cor
         wP == VK_F3 cor
         wP == EDIT_TAB is
             beep();
             ^0;
     endCase;
   endSelect;
   ^command(self:ancestor, wP, lP);
}!!

/* 2/15/1993 13:37 */
Def displayRow(self, offset | temp)
{ 
  if (offset < 0) then
    /* the max output address is 0xffffffffL so strip the sign bit. */
    /* this algorithm will fail when the address is > 0xFFFFFFFFL */
    temp := (offset bitAnd 0x7FFFFFFFL) / 16L;
    /* now add the msb back into the equation */
    temp := temp bitOr 0x8000000L;
  else
    temp := offset / 16L;
  endif;
  ^temp;
}
!!

/* 2/15/1993 13:37 - Calculate the min and max row index. */
Def maxDisplayRows(self | temp rows)
{ 
  minRow := minOffset / 16L;  /* assumes no overflow */
  if (maxOffset < 0)
    /* maxOffset has a 1 in the high bit, so it appears negative to
       Actor.  Strip the sign bit. */
    /* this algorithm will fail when the address is > 0xFFFFFFFFL */
    maxRow := ((maxOffset bitAnd 0x7FFFFFFFL) -
             (minOffset bitAnd 0x7FFFFFFFL)) / 16L;
    /* now add msb back in (shifted by 4 bits due to divide by 16) */
    maxRow := maxRow bitOr 0x8000000L;
  else
    maxRow := maxOffset / 16L;
  endif;
}
!!

/* gaining focus */
Def activateWindow(self)
{
  /* if already in focus or working on it, shortcut recursion */
  if inFocus
    ^0;
  endif;
  
  inFocus := 0;

  /* is invalidate cache pending? */
  if cacheInvalid cor readError?(self)
    doRefresh(self);
  endif;
  
  cleanUpCursor(self);
  
  ^0
}!!

/* Make sure cursor (xPos and yPos) is resting on a valid location. */
Def adjustCursorPos(self | where)
{
  hideCaret(self);
  
  cursorInAddrAreaClear(self);
  /* now move cursor to valid area if not already there */
  where := xPos + leftChar;
  
  /* if read error, no caret */
  if readError?(self)
    if where <= (addrEnd + 1)
      cursorInAddrAreaSet(self);
    endif;
    ^0;
  endif;
  
  /* don't go to partial line at bottom */
  if yPos = visLines(self)
    yPos := yPos - 1;
  endif;
  select
    case (where < memBegin) is
      xPos := memBegin - leftChar;
      displayField := 1;  /* memory */
      /* record double-click in address field, or one space beyond */
      if where <= (addrEnd + 1)
        cursorInAddrAreaSet(self);
      endif;
    endCase
    case (where > asciiEnd) is
      xPos := asciiEnd - leftChar;
      displayField := 2;  /* ascii */
    endCase
    case (where > memEnd cand where < asciiBegin) is
      xPos := asciiBegin - leftChar;
      displayField := 2;  /* ascii */
    endCase
    case (where >= memBegin cand where <= memEnd) is
      displayField := 1;  /* memory */
    endCase
    case (where >= asciiBegin cand where <= asciiEnd) is
      displayField := 2;  /* ascii */
    endCase
  endSelect;

  /* move off a space if we're on one */
  if displayField = 1 cand spaceChar(self, xPos + leftChar)
    xPos := xPos + 1;
  endif;
  
  moveCaret(self);
  showCaret(self);

}!!

/* Respond to arrow keys and home and page-end keys. */
Def arrows(self, wP | nextPos vis)
{
  /* reject arrows */
  if readError?(self)
    beep();
    ^0;
  endif;
  
  writeDirtyBytes(self, 0);
  vis := visLines(self);
  hideCaret(self);
  invSelTxt(self);
  endChar := startChar := xPos + leftChar;
  endLine := startLine := yPos;
  select
    case wP == 37   /* left */ is
      nextPos := getPreviousCharPos(self, xPos, yPos);
      /* use results only if valid */
      if (screenTop + nextPos[1]) >= 0
        yPos := nextPos[1];
        xPos := nextPos[0];
      endif;
    endCase;
    case wP == 39   /* right */ is
      nextPos := getNextCharPos(self, xPos, yPos);
      if (screenTop + nextPos[1]) <= maxRow
        yPos := nextPos[1];
        xPos := nextPos[0];
      endif;
    endCase;
    case wP == 38   /* up */ is
      if (screenTop + yPos) >= 1
        yPos := yPos - 1;
      endif;
    endCase;
    case wP == 40   /* down */ is
      if (yPos + screenTop) < maxRow
        yPos := yPos + 1;
      endif;
    endCase;
    case wP == EDIT_HOME   /* home */ is
      xPos := yPos := 0;
    endCase;
    case wP == EDIT_END   /* end */ is
      xPos := 0;
      yPos := vis - 1;
    endCase;
  endSelect;

  /* process cursor-outside-screen conditions */
  select
    case yPos < 0 is
      desiredScreenTop := screenTop - 1L;
      yPos := yPos + 1;
      fixScreen(self, 0);
    endCase;
    case yPos > (vis - 1) is
      desiredScreenTop := screenTop + 1L;
      yPos := yPos - 1;
      fixScreen(self, 0);
    endCase;
  endSelect;
  initSelParms(self);
  viewInsertPoint(self);
  adjustCursorPos(self);
  ^0;
}!!

/* calculate remaining display attributes */
Def calcDisplayAttributes(self)
{
  /* bail out if info isn't available */
  if not(addrBegin) cor not(addrDisplayDigits) cor
     not(memDisplaySize) cor not(memByteGrouping)
       ^0;
  endif;
  
  /* calculate remaining values */
  addrEnd := addrBegin + addrDisplayDigits - 1;
  memBegin := addrEnd + 3;  /* 2 spaces between addr and mem display */
  memDisplaySizePlus1 := memDisplaySize + 1;
  memRepeatCount := 16 / memByteGrouping;
  memEnd := memBegin + (memDisplaySizePlus1 * memRepeatCount) - 2;
  asciiBegin := memEnd + 2;  /* 1 space between mem and ascii display */
  asciiEnd := asciiBegin + asciiLength - 1;
  lineLength := asciiEnd;
  ^0;
}!!

/* Process MS-Window's character input message. */
Def charIn(self, wP, lP | rect)
{
  if buttonDn
    ^0;
  endif;
  
  /* reject input if read error */
  if readError?(self)
    beep();
    ^0;
  endif;
  
  hideCaret(self);
  charInput(self, asChar(wP));
  /* process cursor-outside-screen conditions */
  select
    case yPos < 0 is
      desiredScreenTop := screenTop - 1L;
      yPos := yPos + 1;
      fixScreen(self, 0);
    endCase;
    case yPos > (linesOnScreen - 1) is
      desiredScreenTop := screenTop + 1L;
      yPos := yPos - 1;
      fixScreen(self, 0);
    endCase;
  endSelect;
  initSelParms(self);
  viewInsertPoint(self);
  moveCaret(self);
  showCaret(self);
  ^0;
}!!

/* Handle the inputted character, return true if aChar is
  a CR. Delete selected text first. */
Def charInput(self, aChar | line nextPos fieldIndex offset charVal)
{
  invSelTxt(self);  /* remove inverted text */
  endChar := startChar := xPos + leftChar;
  endLine := startLine := yPos;
  select
    case isPrintable(aChar) is
      /* process memory input */
      if displayField = 1
        if charInputMemory(self, aChar)
          charInputCleanup(self);
        endif;
      else
        /* assume ascii input because address input illegal */
        if charInputAscii(self, aChar)
          charInputCleanup(self);
        endif;
      endif;
    endCase
    case Char == BS is
      nextPos := getPreviousCharPos(self, xPos, yPos);
      xPos := nextPos[0];
      yPos := nextPos[1];
      if nextPos[2]
        writeDirtyBytes(self, 0);
      endif;
    endCase
    case isNewLineChar(self, aChar) is
      writeDirtyBytes(self, 0);
    endCase
    default
      beep();
  endSelect;
  /* always return nil; don't want charIn messing with the screen */
  ^nil;
}!!

/* process character input in the ascii display area.
   Return code 0 */
Def charInputAscii(self, aChar | fieldIndex offset charVal newString newByte)
{
  /* we already know from charInput that the character is printable. */
  /* stuff the byte equivalent of the ascii character into the byte array */
  fieldIndex := mapAsciiFieldOffsetToIndex(self, xPos + leftChar);
  newByte := asInt(aChar);
  workTextBytes[asInt(yPos+topLine)][asInt(fieldIndex)] := newByte;
  /* reflect the ascii input in the memory display area */
  workText[asInt(yPos+topLine)] := formatTextLine(self, yPos+topLine);
  markDirtyBytes(self, yPos + topLine, fieldIndex, 1);
  ^0;
}!!

/* Clean up after the workText has been updated: redraw the modified line,
   and reposition the cursor. */
Def charInputCleanup(self | nextPos)
{
  /* update the line on the screen (syncs both mem and ascii displays) */
  drawLine(self, asInt(startLine+topLine));
  
  /* get next cursor position */
  nextPos := getNextCharPos(self, xPos, yPos);
  xPos := nextPos[0];
  yPos := nextPos[1];
  
  /* write bytes if moved to a new display unit */
  if nextPos[2]
    writeDirtyBytes(self, nil);
  endif;
}!!

/* process character input in the memory display area. */
Def charInputMemory(self, aChar | fieldIndex offset charVal)
{
  /* process valid char, or beep if bad */
  if (aChar := validChar(self, aChar))
    if perform(self, aChar, updateMemoryDisplayUnit)
      /* read memory display unit into byte array */
      updateByteArray(self);
      /* update ascii display from byte array */
      updateAsciiDisplay(self);
      ^0;
    else
      beep();
      ^nil;
    endif;
  else
    beep();
    ^nil;
  endif;
}!!

/* clean up the cursor after screen reformatting. */
Def cleanUpCursor(self)
{
  /* fix up yPos if it is not visible */
  if yPos < 0
    yPos := 0;
  else
    if yPos > (visLines(self) - 1)
      yPos := visLines(self) - 1;
    endif;
  endif;

  /* make sure cursor is in valid location */
  adjustCursorPos(self);
}!!

/* cursor in address area? */
Def cursorInAddrArea?(self)
{
  ^cursorWasInAddressArea;
}!!

/* cursor not in address area. */
Def cursorInAddrAreaClear(self)
{
  cursorWasInAddressArea := nil;
  ^0;
}!!

/* cursor in address area. */
Def cursorInAddrAreaSet(self)
{
  cursorWasInAddressArea := 0;
  ^0;
}!!

/* losing focus */
Def deactivateWindow(self)
{
  /* if already not focused or working on it, shortcut recursion */
  if not(inFocus)
    ^0;
  endif;
  
  writeDirtyBytes(self, 0);
  inFocus := nil;
  
  ^0;
}!!

/* disable delChar. */
Def delChar(self)
{
  beep();
}!!

Def doGoto(self, line | theDlg lineOffset charOffset temp resourceId)
{
  writeDirtyBytes(self, 0);
  errorDetected? := nil;

  /* set up address descriptor */
  if not(copyAddress(AddressLibClass$Inst, baseAddressDescriptor,
    workAddressDescriptor))
      ^nil;
  endif;

  /* use good value as default */
  select
    /* if read error, suggestion is blank */
    case readError?(self) is
      /* do nothing */
    endCase;
    /* if no line specified, use top line as default */
    case not(line) is
      if not(addToAddress(AddressLibClass$Inst, workAddressDescriptor,
        screenTop * 16L))
          ^nil;
      endif;
    endCase
    /* otherwise, use line user double-clicked on */
    default
      if not(addToAddress(AddressLibClass$Inst, workAddressDescriptor,
        (screenTop + line) * 16L))
          ^nil;
      endif;
  endSelect;

  if (TheProcFamily = PROC_FAMILY_X86)
    theDlg := openForMem(GotoIntelAddrDialog, workAddressDescriptor,
      viewSize(parent));
    resourceId := DLG_GOTO_INTEL_ADDRESS;
  else
    theDlg := open(GotoAddrDialog, workAddressDescriptor);
    resourceId := DLG_GOTO;
  endif;

  /* Open the dialog box */
  setHelpEntry(theDlg, HE_DLGR_GOTO_ADDRESS);
  if (runModal(theDlg, resourceId, ThePort) <> IDOK)
    ^nil;
  endif;

  /* Get the address result to perform Goto */
  if not(getAddr(theDlg))
    ^displayFormattedError(ErrorTextLibClass$Inst,
       ER_SRC_INVALID_ADDRESS, FORCE_POPUP, nil, nil, nil);
  endif;

  /* Get the address space */
  temp := getAddrSpace(theDlg);
  updateSpaceMenu(parent, temp);

  /* Don't process view size; field is disabled */

  /* Goto the address */
  showWaitCurs();

  /* get offset, abort if error (msg already posted) */
  if not(temp := getOffset(AddressLibClass$Inst, workAddressDescriptor))
    ^nil;
  endif;
  
  /* see if segment limits et al need to be updated */
  if not(getSegmentInfo(self, workAddressDescriptor, nil))
    ^nil;
  endif;

  ^gotoOffset(self, temp);
}!!

/* respond to memory change event; ignore if we sent it */
Def doRefresh(self)
{
  readErrorClear(self);
  /* if in focus, invalidate now; otherwise, wait until we get focus */
  if inFocus
    if not(ignoreMemoryChangeEvent) cor reRead(parent) cor cacheInvalid
      invalidateCache(self);
      cacheInvalidClear(self);
    endif;
  else
    cacheInvalidSet(self);
  endif;
  ignoreMemoryChangeEvent := nil;
}!!

Def doViewDecBytes(self | temp)
{
  /* exit if display error */
  if errorDetected?
    ^0;
  endif;
  
  if memRadix = 10 cand memByteGrouping = 1
    ^0;
  endif;
  
  showWaitCurs();
  temp := getCursorInformation(self);
  /* set display attributes */
  memRadix := 10;
  memByteGrouping := 1;
  memMaxDisplayUnit := "255";
  updateMemoryDisplayUnit := #updateMemoryDisplayUnit10;
  updateByteArraySize := #updateByteArrayByte;
  setDisplayAttributes(self);

  /* re-construct screen from workTextBytes */
  desiredScreenTop := screenTop;
  invalidateWorkText(self);
  fixScreen(self, nil);
  setCursorPosition(self, temp);
  showOldCurs();
}!!

Def doViewDecDWords(self | temp)
{
  /* exit if display error */
  if errorDetected?
    ^0;
  endif;
  
  if memRadix = 10 cand memByteGrouping = 4
    ^0;
  endif;
  
  showWaitCurs();
  temp := getCursorInformation(self);
  /* set display attributes */
  memRadix := 10;
  memByteGrouping := 4;
  memMaxDisplayUnit := "4294967295";
  updateMemoryDisplayUnit := #updateMemoryDisplayUnit10;
  if (TheProcFamily = PROC_FAMILY_X86)
    updateByteArraySize := #updateLittleEndianByteArrayDWord;
  else
    updateByteArraySize := #updateBigEndianByteArrayDWord;
  endif;
  setDisplayAttributes(self);

  /* re-construct screen from workTextBytes */
  desiredScreenTop := screenTop;
  invalidateWorkText(self);
  fixScreen(self, nil);
  setCursorPosition(self, temp);
  showOldCurs();
}!!

Def doViewDecWords(self | temp)
{
  /* exit if display error */
  if errorDetected?
    ^0;
  endif;
  
  if memRadix = 10 cand memByteGrouping = 2
    ^0;
  endif;
  
  showWaitCurs();
  temp := getCursorInformation(self);
  /* set display attributes */
  memRadix := 10;
  memByteGrouping := 2;
  memMaxDisplayUnit := "65535";
  updateMemoryDisplayUnit := #updateMemoryDisplayUnit10;
  if (TheProcFamily = PROC_FAMILY_X86)
    updateByteArraySize := #updateLittleEndianByteArrayWord;
  else
    updateByteArraySize := #updateBigEndianByteArrayWord;
  endif;
  setDisplayAttributes(self);

  /* re-construct screen from workTextBytes */
  desiredScreenTop := screenTop;
  invalidateWorkText(self);
  fixScreen(self, nil);
  setCursorPosition(self, temp);
  showOldCurs();
}!!

Def doViewDisassembly(self | err lastError str)
{
}!!

Def doViewHexBytes(self | temp)
{
  /* exit if display error */
  if errorDetected?
    ^0;
  endif;
  
  if memRadix = 16 cand memByteGrouping = 1
    ^0;
  endif;
  
  showWaitCurs();
  temp := getCursorInformation(self);
  /* set display attributes */
  memRadix := 16;
  memByteGrouping := 1;
  memMaxDisplayUnit := "";
  updateMemoryDisplayUnit := #updateMemoryDisplayUnit16;
  updateByteArraySize := #updateByteArrayByte;
  setDisplayAttributes(self);

  /* re-construct screen from workTextBytes */
  if initializeCalled
    desiredScreenTop := screenTop;
    invalidateWorkText(self);
    fixScreen(self, nil);
  endif;
  setCursorPosition(self, temp);
  showOldCurs();
}!!

Def doViewHexDWords(self | temp)
{
  /* exit if display error */
  if errorDetected?
    ^0;
  endif;
  
  if memRadix = 16 cand memByteGrouping = 4
    ^0;
  endif;
  
  showWaitCurs();
  temp := getCursorInformation(self);
  /* set display attributes */
  memRadix := 16;
  memByteGrouping := 4;
  memMaxDisplayUnit := "";
  updateMemoryDisplayUnit := #updateMemoryDisplayUnit16;
  if (TheProcFamily = PROC_FAMILY_X86)
    updateByteArraySize := #updateLittleEndianByteArrayDWord;
  else
    updateByteArraySize := #updateBigEndianByteArrayDWord;
  endif;
  setDisplayAttributes(self);

  /* re-construct screen from workTextBytes */
  desiredScreenTop := screenTop;
  invalidateWorkText(self);
  fixScreen(self, nil);
  setCursorPosition(self, temp);
  showOldCurs();
}!!

Def doViewHexWords(self | temp)
{
  /* exit if display error */
  if errorDetected?
    ^0;
  endif;
  
  if memRadix = 16 cand memByteGrouping = 2
    ^0;
  endif;
  
  showWaitCurs();
  temp := getCursorInformation(self);
  /* set display attributes */
  memRadix := 16;
  memByteGrouping := 2;
  memMaxDisplayUnit := "";
  updateMemoryDisplayUnit := #updateMemoryDisplayUnit16;
  if (TheProcFamily = PROC_FAMILY_X86)
    updateByteArraySize := #updateLittleEndianByteArrayWord;
  else
    updateByteArraySize := #updateBigEndianByteArrayWord;
  endif;
  setDisplayAttributes(self);

  /* re-construct screen from workTextBytes */
  if initializeCalled
    desiredScreenTop := screenTop;
    invalidateWorkText(self);
    fixScreen(self, nil);
  endif;
  setCursorPosition(self, temp);
  showOldCurs();
}!!

/* desiredScreenTop has been set to the top row to be displayed.
   Do the following:
     - set screenEnd.
     - call fixWorkText to update the workText if necessary.
     - adjust the screen to show the screenTop.
     - set vscroll position.
   okToScroll? is true if it is OK to scroll when everything else is OK.
*/
Def fixScreen(self okToScroll? |
    index vis oldScreenTop scrollLines fixRect scrollRect fixStat errStat)
{
  if not(maxRow)
    ^0;
  endif;
  showWaitCurs();
  oldScreenTop := screenTop;  /* save for 1-line scroll test later */
  screenTop := desiredScreenTop;
  vis := asLong(visLines(self));
  screenEnd := screenTop + vis - 1L;
  
  /* back off if exposing beyond end; only occurs on reSize? */
  if (screenEnd > maxRow) cand (maxRow > vis)
    yPos := yPos - (screenEnd - maxRow);
    screenEnd := maxRow;
    screenTop := screenEnd - vis + 1L;
  endif;

  /* fill the screen with data; force repaint unless scrollable. */
  errStat := readError?(self);
  fixStat := fixWorkText(self);
  if not(errStat) cand fixStat cand okToScroll?
    scrollLines := screenTop - oldScreenTop; /* if ok, prepare for scroll */
  else
    scrollLines := nil;  /* force repaint */
  endif;
  
  select
    case scrollLines = 1 is
      fixRect := clientRect(self);
      setTop(fixRect, tmHeight*(screenEnd-screenTop));
      Call ScrollWindow(hWnd, 0, negate(tmHeight), 0, 0);
      Call InvalidateRect(hWnd, fixRect, 1);
    endCase
    case scrollLines = -1 is
      fixRect := clientRect(self);
      setBottom(fixRect, tmHeight);
      scrollRect := clientRect(self);
      setTop(scrollRect, tmHeight);
      setBottom(scrollRect, tmHeight*(screenEnd-screenTop));
      Call ScrollWindow(hWnd, 0, tmHeight, scrollRect, 0);
      Call InvalidateRect(hWnd, fixRect, 1);
    endCase
    default
      invalidate(self);
  endSelect;

  /* set thumb position */
  setScrollPos(self);

  /* do cursor processing */
  cleanUpCursor(self);
  showOldCurs();
  ^0;
}!!

/* Read the bytes from the server, and display the corresponding text. */
Def fixWorkText(self)
{
  /* return nil if fixWorkTextMemory fails */
  if not(fixWorkTextMemory(self))
    ^nil;
  endif;
  topLine := asInt(screenTop - workTextTop);
  fixWorkTextLines(self);
  ^0;
}!!

/* Format the workText lines that are visible on the screen. */
Def fixWorkTextLines(self | index screenSize temp)
{
  screenSize := asInt(screenEnd - screenTop + 1);
  index := 0;
  loop
  while index < min(screenSize, size(workText))
  begin
    temp := asInt(topLine + index);
    if not(workText[temp])
      workText[temp] := formatTextLine(self, temp);
    endif;
    index := index + 1;
  endLoop;

  /* fixScreen invalidates screen */
  ^0;
}!!

/* Update the workTextBytes cache. */
Def fixWorkTextMemory(self | screenSize)
{
  screenSize := asInt(screenEnd - screenTop + 1);

  if not(workTextTop) cor (screenTop < workTextTop)
    ^fixWorkTextMemory1(self, screenSize);
  else
    ^fixWorkTextMemory2(self, screenSize);
  endif;
  ^nil;
}!!

/* Update the workTextBytes cache. */
Def fixWorkTextMemory2(self, screenSize
      | index startRead stopRead deleteLineCount maxRead)
{
  /* screenTop >= workTextTop */
  if (screenEnd > workTextEnd)
    /* read below current work text; figure start/stop read rows */
    if readAhead(parent)
      stopRead := min(maxRow, (screenEnd + (3 * screenSize)));
      maxRead := maxCache;
    else
      stopRead := min(maxRow, screenEnd);
      maxRead := screenSize;
    endif;
    if ((stopRead + 1 - maxRead) > workTextEnd)
      /* can't save previous workTextArray */
      if readAhead(parent)
        startRead := max(minRow, (stopRead - (5 * screenSize)));
      else
        startRead := max(minRow, stopRead + 1 - screenSize);
      endif;
      workTextBytes := newWorkTextBytes(self);
      workText := newWorkText(self);
      dirtyBytes := newDirtyBytes(self);

      /* read memory, return if error */
      readErrorClear(self);  /* innocent until proven guilty... */
      if not(readWorkTextBytesEnd(self, startRead, stopRead))
          readErrorSet(self);
          ^nil;
        endif;

      workTextTop := startRead;
      workTextEnd := stopRead;
    else
      /* can save some or all of previous workTextArray */
      startRead := workTextEnd + 1; /* set to read below end line */

      /* discard workTextArray lines if too many */
      index := 0;
      deleteLineCount := stopRead - workTextTop - maxRead;
      loop
      while index < deleteLineCount
      begin
        removeFirst(workTextBytes);
        removeFirst(workText);
        index := index + 1;
      endLoop;

      /* read memory, return if error */
      readErrorClear(self);  /* innocent until proven guilty... */
      if not(readWorkTextBytesEnd(self, startRead, stopRead))
        readErrorSet(self);
        ^nil;
      endif;

      workTextTop := workTextTop + index;
      workTextEnd := stopRead;
    endif;
  endif;
  workTextSize := workTextEnd - workTextTop + 1;
  ^0;
}!!

/* Update the workTextBytes cache. */
Def fixWorkTextMemory1(self, screenSize
      | index startRead stopRead deleteLineCount maxRead)
{
  /* must read lines above current work text; figure start/stop read rows */
  if (readAhead(parent))
    startRead := max(minRow, (screenTop - screenSize));
    maxRead := maxCache;
  else
    startRead := max(minRow, screenTop);
    maxRead := screenSize;
  endif;
  if not(workTextTop) cor ((startRead + maxRead - 1) < workTextTop)
    /* can't save previous workTextArray */
    if (readAhead(parent))
      stopRead := min(maxRow, screenTop + (4 * screenSize));
    else
      stopRead := min(maxRow, screenTop + screenSize - 1);
    endif;
    workTextBytes := newWorkTextBytes(self);
    workText := newWorkText(self);
    dirtyBytes := newDirtyBytes(self);
    /* read memory, return if error */
    readErrorClear(self);  /* innocent until proven guilty... */
    if not(readWorkTextBytesTop(self, startRead, stopRead))
      readErrorSet(self);
      ^nil;
    endif;
    workTextTop := startRead;
    workTextEnd := stopRead;
  else
    /* can save some/all of previous workTextArray */
    stopRead := workTextTop - 1; /* read up to previous top line */

    /* discard workTextArray lines if too many */
    index := 0;
    deleteLineCount := workTextEnd - startRead - maxRead;
    loop
    while index < deleteLineCount
    begin
      removeLast(workTextBytes);
      removeLast(workText);
      index := index + 1;
    endLoop;

    /* read memory, return if error */
    readErrorClear(self);  /* innocent until proven guilty... */
    if not(readWorkTextBytesTop(self, startRead, stopRead))
      readErrorSet(self);
      ^nil;
    endif;

    workTextEnd := workTextEnd - index;
    workTextTop := startRead;
  endif;
  ^0;
}!!

/* construct and return the indicated byte string. */
/* rowIndex is absolute */
Def formatMemoryDisplayByte(self, rowIndex | string index)
{
  string := "";
  index := 0;
  loop
  while index < memRepeatCount
  begin
    string := string + right(
      asUnsignedStringRadix(asLong(workTextBytes[asInt(rowIndex)][index]),
      memRadix), memDisplaySize, "0") + " ";
    index := index + 1;
  endLoop;
  ^string;
}!!

/* construct and return the formatted text line corresponding to the
   workTextBytes byte array row. */
/* rowIndex is absolute */
Def formatTextLine(self, rowIndex | string index charVal asciiString timer t1 t2)
{
  /*
  string := formatTextLineAddress(self, workTextTop + rowIndex) +
    perform(self, rowIndex, formatMemoryDisplay);
    */
  t1 := formatTextLineAddress(self, workTextTop + rowIndex);
  t2 := perform(self, rowIndex, formatMemoryDisplay);
  string := t1 + t2;

  /* place ascii portion into display */
  asciiString := "";
  index := 0;
  loop
  while index < 16
  begin
    charVal := asChar(workTextBytes[asInt(rowIndex)][asInt(index)]);
    if isPrintable(charVal)
      asciiString := asciiString + asString(charVal);
    else
      asciiString := asciiString + ".";
    endif;
    index := index + 1;
  endLoop;
  ^string + asciiString;
}!!

/* construct and return the address portion of the formatted text line. */
Def formatTextLineAddress(self, rowIndex | addrText temp)
{
  /* set up address descriptor for formatting address */
  if not(copyAddress(AddressLibClass$Inst, baseAddressDescriptor,
    workAddressDescriptor))
      ^nil;
  endif;
  if not(setOffset(AddressLibClass$Inst, workAddressDescriptor,
    (rowIndex * 16L)))
      ^nil;
  endif;

  /* get address text */
  if not (addrText := getAddressText(AddressLibClass$Inst,
    workAddressDescriptor))
      ^nil;
  endif;

  /* return right-justified, blank filled address plus 2 spaces */
  /* ^right(addrText, addrDisplayDigits, " ") + "  "; */
  temp := right(addrText, addrDisplayDigits, " ") + "  ";
  ^temp;
}!!

/* return next x,y point */
Def getNextCharPos(self, x, y)
{
  if displayField = 1
    ^getNextCharPosMem(self, x, y);
  else
    ^getNextCharPosAscii(self, x, y);
  endif;
}!!

/* return next x,y point; 3rd element is 0 to show display item changed. */
Def getNextCharPosAscii(self, x, y)
{
  if (x+leftChar) = asciiEnd
    ^tuple(asciiBegin - leftChar, y+1, 0);
  else
    ^tuple(x+1, y, 0);
  endif;
}!!

/* return next x,y point in memory area; 3rd element is 0 if moving
   to another display unit, nil if not. */
Def getNextCharPosMem(self, x, y)
{
  if (x+leftChar) = memEnd
    ^tuple(memBegin - leftChar, y+1, 0);
  else
    if spaceChar(self, x + leftChar + 1)
      ^tuple(x+2, y, 0);
    else
      ^tuple(x+1, y, nil);
    endif;
  endif;
}!!

/* return offset at cursor */
Def getOffset(self)
{
  if errorDetected?
    ^nil;
  endif;
  
  if (displayField = 1)
    ^(((screenTop + yPos) * 16) + (mapMemFieldOffsetToIndex(self, xPos + leftChar) * memByteGrouping));
  else
    ^(((screenTop + yPos) * 16) + mapAsciiFieldOffsetToIndex(self, xPos + leftChar));
  endif;
}!!

/* return previous x,y point; 3rd element true if display element changed */
Def getPreviousCharPos(self, x, y)
{
  if displayField = 1
    ^getPreviousCharPosMem(self, x, y);
  else
    ^getPreviousCharPosAscii(self, x, y);
  endif;
}!!

/* gaining focus */
Def gotFocus(self, hWndPrev | status)
{
  activateWindow(self);
  status := gotFocus(self:ancestor, hWndPrev);
  
  hideCaret(self);
  
  /* display caret if no read error */
  if not(readError?(self))
    moveCaret(self);
    showCaret(self);
  endif;
  
  ^status;
}!!

/* initialize display */
Def initialize(self | temp)
{
  init(self:ancestor);
  linesOnScreen := 0;
  maxCache := 100;  /* maximum size of workText, workTextBytes cache */

  /* create descriptor for base address */
  if not(baseAddressDescriptor := createAddress(AddressLibClass$Inst))
    ^nil;
  endif;
  if not(setAddrMode(AddressLibClass$Inst, baseAddressDescriptor, ADDR_MODE_CURRENT))
    cleanUp(self);
    ^nil;
  endif;

  /* create descriptor for work address */
  if not(workAddressDescriptor := createAddress(AddressLibClass$Inst))
    cleanUp(self);
    ^nil;
  endif;

  if not(updateAddrMode(self))
    cleanUp(self);
    ^nil;
  endif;
  
  screenTop := 0L;
  desiredScreenTop := screenTop;

  /* globals for formatting display, updating memory */
  byteArray := new(Struct, 4);  
  tempWord := new(Struct, 2);
  tempDWord := new(Struct, 4);
  
  cacheInvalidSet(self);  /* mark cache invalid so it is updated */

  /* initially set to display hex words */
  doViewHexWords(self);

  initializeCalled := 0;
}!!

/* 6/29/1992 15:31  Discard cached memory bytes */
Def invalidateCache(self | index)
{
  invalidateCacheDeferred(self);
  fixScreen(self, nil);
  ^0;
}!!

/* 6/24/1992 15:31  Discard formatted lines if display format changes. */
Def invalidateWorkText(self | index)
{
  if workText
    index := 0;
    loop
    while index < size(workText)
    begin
      workText[asInt(index)] := nil;
      index := index + 1;
    endLoop;
  endif;
  ^0;
}!!

/* return 0 if bad memory display unit index, nil if OK. */
Def invalidMemoryIndex(self, rowIndex | temp)
{
  temp := (16 / memByteGrouping) - 1;
  if (rowIndex < 0 cor rowIndex > temp)
    ^0;
  endif;
  ^nil;
}!!

/* losing focus */
Def losingFocus(self, hWndNew)
{
  /* Do not write memory if the server is gone */
  if memServerValid?(parent) then
    writeDirtyBytes(self, 0);
  endif;
  ^losingFocus(self:ancestor, hWndNew)
}!!

/* save info on dirty bytes. */
Def markDirtyBytes(self, y, x, count)
{
  /* capture row */
  if not(dirtyByteRow)
    dirtyByteRow := y;
  endif;
  
  /* capture min row */
  if not(dirtyByteColMin) cor dirtyByteColMin > x
    dirtyByteColMin := x;
  endif;
  
  /* capture max row */
  if not(dirtyByteColMax) cor dirtyByteColMax < (x + count - 1)
    dirtyByteColMax := x + count - 1;
  endif;
  
  ^0;
}!!

/* 3/23/1992 16:13 */
Def memByteGrouping(self)
{
  ^memByteGrouping;
}!!

/* 3/23/1992 16:13 */
Def memRadix(self)
{
  ^memRadix;
}!!

/* respond to menu selection from parent */
/* @@ MPresenter must call */
Def menuSelection(self)
{
  writeDirtyBytes(self, 0);
}!!

/* return new dirtyBytes structure */
Def newDirtyBytes(self)
{
  ^new(OrderedCollection, 1);
}!!

/* return new workText structure */
Def newWorkText(self)
{
  ^new(TextCollection, 1);
}!!

/* return new workTextBytes structure */
Def newWorkTextBytes(self)
{
  ^new(OrderedCollection, 1);
}!!

/* STOLEN DIRECTLY FROM EditWindow, EXCEPT FIXED BUG AS MARKED.
  Redraw the workText from topLine down, and from
  leftChar rightwards, preserve xPos and yPos.  If
  window has focus, show selected text.*/
Def paint(self, hdc | aStr, xH, yH)
{ xH := xPos;
  yH := yPos;
  home(self);
  initTextColors(self, hdc);
  /* ORIGINAL CODE: <printed too much> 1 + topline + visLines(self))), */
  do(over(topLine, min(size(workText), topLine + visLines(self))),
  {using(idx)
    aStr:= copyFrom(workText[asInt(idx)], leftChar, size(workText[asInt(idx)]));
    Call TextOut(hdc, x(self), y(self), aStr, size(aStr));
    xPos := 0;
    yPos := yPos + 1;
  });
  xPos := xH;
  yPos := yH;
  setScrollPos(self);
  setHScrollPos(self);
  if hasFocus(self) and isSelText(self)
  then dragDC := hdc;
      invSelTxt(self);
      hideCaret(self);
  endif;
  
  hideCaret(self);
  
  /* display caret if no read error */
  if not(readError?(self))
    moveCaret(self);
    showCaret(self);
  endif;
}!!

/* Place the cursor properly */
Def placeCursor(self, lineOffset, charOffset | lineAndCol temp cursorPos)
{
  yPos := lineOffset - screenTop;
  xPos := mapByteOffsetToXPos(self, charOffset);
  
  cleanUpCursor(self);
}!!

/* return read error */
Def readError?(self)
{
  ^readError;
}
!!

/* clear read error */
Def readErrorClear(self)
{
  readError := nil;
  ^0;
}
!!

/* set read error */
Def readErrorSet(self)
{
  readError := 0;
  workTextTop := nil;  /* indicate cache is bad */
  workTextEnd := 0;  /* indicate cache is bad */
  workTextBytes := newWorkTextBytes(self);
  
  /* put something up on the screen */
  workText := newWorkText(self);
  add(workText,
    formatTextLineAddress(self, screenTop) + "UNABLE TO READ MEMORY");
  ^0;
}
!!

/* Read the bytes from the server; place at end of workTextBytes. */
Def readWorkTextBytesEnd(self, startRead, stopRead |
      lineCount rowIndex byteArrayCol)
{
  lineCount := asInt(stopRead - startRead + 1);

  /* set up address descriptors for read */
  if not(copyAddress(AddressLibClass$Inst, baseAddressDescriptor,
    workAddressDescriptor))
      ^nil;
  endif;
  if not(setOffset(AddressLibClass$Inst, workAddressDescriptor,
    (startRead * 16L)))
      ^nil;
  endif;

  /* read bytes from the server; 16 bytes for each row requested */
  if not(byteArrayCol := readLines(memServer(parent), lineCount,
    workAddressDescriptor))
      ^nil;  /* error reporting done by library */
  endif;

  /* stuff the byte arrays into workTextBytes */
  rowIndex := 0;
  loop
  while rowIndex < lineCount
  begin
    add(workTextBytes, byteArrayCol[rowIndex]);
      /* stuff row into workTextBytes */
    add(workText, nil);  /* indicate line is not formatted yet */
    rowIndex := rowIndex + 1;
  endLoop;

  ^0;
}!!

/* Read the bytes from the server; place at top of workTextBytes. */
Def readWorkTextBytesTop(self, startRead, stopRead |
      lineCount rowIndex byteArrayCol)
{
  /* if workTextBytes is empty, operation is the same as (simpler)
     readWorkTextBytesEnd */
  if not(workTextBytes) cor size(workTextBytes) = 0
    ^readWorkTextBytesEnd(self, startRead, stopRead);
  endif;

  lineCount := asInt(stopRead - startRead + 1);

  /* set up address descriptors for read */
  if not(copyAddress(AddressLibClass$Inst, baseAddressDescriptor,
    workAddressDescriptor))
      ^nil;
  endif;
  if not(setOffset(AddressLibClass$Inst, workAddressDescriptor,
    (startRead * 16L)))
      ^nil;
  endif;

  /* read bytes from the server; 16 bytes for each row requested */
  if not(byteArrayCol := readLines(memServer(parent), lineCount,
    workAddressDescriptor))
      ^nil;  /* error reporting done by library */
  endif;

  /* stuff the bytes into workTextBytes backwards */
  rowIndex := lineCount - 1;
  loop
  while rowIndex >= 0
  begin
    insert(workTextBytes, byteArrayCol[rowIndex], 0);
      /* stuff row into workTextBytes */
    insert(workText, nil, 0);  /* work text line not yet formatted */
    rowIndex := rowIndex - 1;
  endLoop;

  ^0;
}!!

/* fix screen, call parent. */
Def reSize(self, wP, lP | visLines)
{
  if not(initializeCalled) cor errorDetected?
    ^0;
  endif;
  
  visLines := visLines(self);
  if visLines <> linesOnScreen
    writeDirtyBytes(self, 0);
    fixScreen(self, 0);
  endif;
  reSize(self:ancestor, wP, lP);
  linesOnScreen := visLines;
  writeDirtyBytes(self, 0);
  adjustCursorPos(self);
}!!

/* Set cursor position (xPos, yPos) according to the
  specified point. */
Def setCurPos(self, aPnt)
{
  setFocus(self);

  /* this sets xPos and yPos */
  setCurPos(self:ancestor, aPnt);

  /* make sure cursor is on a valid location */
  adjustCursorPos(self);

  writeDirtyBytes(self, 0);
}!!

/* set display attributes; assume memRadix and memByteGrouping are set. 
*/
Def setDisplayAttributes(self | proc)
{
  /* stuff the values that we know */
  addrBegin := 0;
  asciiLength := 16;

  /* stuff the radix/grouping dependent value */
  select
    case memRadix = 16 is
      select
        case memByteGrouping = 1  is memDisplaySize := 2; endCase
        case memByteGrouping = 2  is memDisplaySize := 4; endCase
        case memByteGrouping = 4  is memDisplaySize := 8; endCase
        default
           displayFormattedError(ErrorTextLibClass$Inst, 
              ER_INTERNAL, FORCE_POPUP, "Illegal hex byte grouping",
              "MBrowser:setDisplayAttributes", nil);
      endSelect;
    endCase
    case memRadix = 10 is
      select
        case memByteGrouping = 1  is memDisplaySize := 3; endCase
        case memByteGrouping = 2  is memDisplaySize := 5; endCase
        case memByteGrouping = 4  is memDisplaySize := 10; endCase
        default
          displayFormattedError(ErrorTextLibClass$Inst, 
             ER_INTERNAL, FORCE_POPUP, "Illegal decimal byte grouping",
             "MBrowser:setDisplayAttributes", nil);
      endSelect;
    endCase
    default
      displayFormattedError(ErrorTextLibClass$Inst, 
         ER_INTERNAL, FORCE_POPUP, "Illegal radix",
         "MBrowser:setDisplayAttributes", nil);
  endSelect;

  select
    case memByteGrouping = 1  is
      formatMemoryDisplay := #formatMemoryDisplayByte; endCase
    case memByteGrouping = 2  is
      proc := getProcessorFamily(ProcLibClass$Inst);
      if (proc) cand (proc = PROC_FAMILY_X86)
        formatMemoryDisplay := #formatLittleEndianMemDisplayWord;
      else
        formatMemoryDisplay := #formatBigEndianMemDisplayWord;
      endif;
    endCase
    case memByteGrouping = 4  is
      proc := getProcessorFamily(ProcLibClass$Inst);
      if (proc) cand (proc = PROC_FAMILY_X86)
        formatMemoryDisplay := #formatLittleEndianMemDisplayDword;
      else
        formatMemoryDisplay := #formatBigEndianMemDisplayDword;
      endif;
    endCase
    default
      displayFormattedError(ErrorTextLibClass$Inst, 
         ER_INTERNAL, FORCE_POPUP, "Illegal byte grouping",
         "MBrowser:setDisplayAttributes", nil);
  endSelect;

  /* calculate the rest of the values */
  calcDisplayAttributes(self);
  ^0;
}!!

/* receive offset for top of screen */
Def setOffset(self, value | lineOffset charOffset)
{
  lineOffset := displayRow(self,value);
  charOffset := value mod 16;
  
  desiredScreenTop := lineOffset;
  fixScreen(self, 0);
  
  /* position the cursor on the byte */
  placeCursor(self, lineOffset, charOffset);
}!!

/* Set scroll bar position, avoiding divide by 0. */
Def setScrollPos(self | percent qbottom vis)
{
  if not(initializeCalled) cor not(scrollOffset)
    ^0;
  endif;

  /* set thumb position */
  if not (vis := visLines(self))
    vis := 0;
  endif;
  qbottom := max(1, (scrollOffset - vis) / (100 / scrollDivisor));
  percent := screenTop / qbottom;
  Call SetScrollPos(hWnd, SB_VERT, percent, 1);
}!!

/* Return true if the specified character in the worktext is a space. */
Def spaceChar(self, x | lineIndex)
{
  lineIndex := asInt(topLine);
  if not(workText) cor
    (size(workText) <= lineIndex) cor
    (size(workText[lineIndex]) <= x)
      ^nil;
  endif;
  
  /* move off a space if we're on one */
  /* we know that line screenTop is visible */
  if workText[lineIndex][asInt(x)] = ' '
    ^0
  endif;
  ^nil;
}!!

/* Compare a string to a maximum string; return nil if string < max;
   return character offset where string > max otherwise. */
Def stringCompareToMax(self, max, string | maxSize index)
{
  maxSize := size(max);
  index := 0;
  loop
  while index < maxSize
  begin
    if max[index] > string[index]
      ^nil;
    else
      if max[index] < string[index]
        ^index;
      endif;
    endif;
    index := index + 1;
  endLoop;
  ^nil;
}!!

/* Reflect each of the potentially changed bytes in the ascii display. */
Def updateAsciiDisplay(self | fieldIndex startIndex index position char)
{
  fieldIndex := mapMemFieldOffsetToIndex(self, xPos + leftChar);
  startIndex := fieldIndex * memByteGrouping;
  index := 0;
  loop
  while index < memByteGrouping
  begin
    /* byte array position maps 1-1 to ascii position, so calculate once */
    position := startIndex + index;
    char := asChar(workTextBytes[asInt(yPos+topLine)][asInt(position)]);
    if not(isPrintable(char))
      char := '.';
    endif;
    put(workText[asInt(yPos+topLine)], char, position+asciiBegin);
    index := index + 1;
  endLoop;
}!!

/* read memory display unit into byte array */
Def updateByteArray(self | fieldIndex screenOffset value)
{
  /* get display unit # */
  fieldIndex := mapMemFieldOffsetToIndex(self, xPos + leftChar);
  /* where display unit starts on the screen */
  screenOffset := mapMemFieldIndexToOffset(self, fieldIndex);
  /* grab value from display */
  value := asLong(
             asInt(
               subString(workText[asInt(yPos+topLine)],
                 screenOffset, screenOffset + memDisplaySize),
               memRadix)
           );
   /* stuff bytes into byteArray */
   perform(self, value, fieldIndex, updateByteArraySize);
   ^0
}!!

/* stuff the byte directly into the byte array using fieldIndex as offset. */
Def updateByteArrayByte(self, value, fieldIndex | string)
{
  workTextBytes[asInt(yPos+topLine)][asInt(fieldIndex)] := asInt(value);
  markDirtyBytes(self, yPos + topLine, fieldIndex, 1);
  ^0;
}!!

/* update the memory display unit with the char.
   If the result of inserting the character causes the value to be
     too large, attempt to fix by filling the remainder of the values
     with 0; if the revised value is acceptable, stuff that value into
     the display area.  If the revised value is still to big, return
     nil, which indicates that the character is rejected.
   If the result of inserting the character is OK, update the memory
     display area with the character. */
Def updateMemoryDisplayUnit10(self, aChar |
      fieldIndex screenOffset displayUnitOffset diffIndex tempString patchSize)
{
  fieldIndex := mapMemFieldOffsetToIndex(self, xPos + leftChar);
    /* get display unit # */
  screenOffset := mapMemFieldIndexToOffset(self, fieldIndex);
    /* where display unit starts on the screen */
  displayUnitOffset := asInt((xPos + leftChar) - screenOffset);
    /* offset of character within display unit */

  /* create a temporary string with the character inserted */
  tempString := subString(workText[asInt(yPos+topLine)],
    screenOffset, screenOffset + memDisplaySize);
  put(tempString, aChar, displayUnitOffset);

  /* compare the strings; nil: immediately insert character;
     val > diffIndex: legal value can be created by zero fill to right;
     val <= diffIndex: illegal value */
  diffIndex := stringCompareToMax(self, memMaxDisplayUnit, tempString);
  select
    case not(diffIndex) is
      /* the input is valid; stuff the value and return 0 */
      put(workText[asInt(yPos+topLine)], aChar, asInt(leftChar + xPos));
      ^0;
    endCase;
    case diffIndex <= displayUnitOffset is
      /* input is illegal; return nil */
      ^nil;
    endCase;
  endSelect;
  /* otherwise, we have a repairable value; zero fill, and return zero */
  patchSize := memDisplaySize - diffIndex;  /* size of zero fill */
  tempString := replace(tempString, "000000000000", 0, patchSize,
    diffIndex, memDisplaySize);

  /* now stuff revised memory display unit into work text */
  workText[asInt(yPos+topLine)] := replace(workText[asInt(yPos+topLine)],
      tempString, 0, memDisplaySize,
      screenOffset, screenOffset + memDisplaySize);
  ^0;
}!!

/* update the memory display unit with the char. */
Def updateMemoryDisplayUnit16(self, aChar)
{
  /* hex values are always acceptable */
  put(workText[asInt(yPos+topLine)], aChar, asInt(xPos + leftChar));
  ^0;
}!!

/* check for valid radix, return approved char or nil if bad.
   Note: even though code implies hex character handling for
         decimal, the radix-specific asDigit call
         short-circuits illegal values. */
Def validChar(self, char | temp)
{
  if not(temp := asDigit(char, memRadix))
    ^nil;
  endif;
  select
    case temp >=0 cand temp <= 9
    is ^char;
    endCase
    case temp >= 10 cand temp <= 15
    is ^asUpperCase(char);
    endCase
  endSelect;
  ^nil;
}!!

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

/* Disable normal double-clicking */
Def WM_LBUTTONDBLCLK(self, wp, lp | yt gotoAddr)
{
  setCurPos(self, asPoint(lp));
  
  if readError?(self)
    hideCaret(self);
  endif;
  
  if cursorInAddrArea?(self)
    if readError?(self)
      gotoAddr := nil;
    else
      yt := max(0, y(asPoint(lp))-2)/tmHeight;
      gotoAddr := min((size(workText)-1) - topLine, yt);
    endif;
    doGoto(self, gotoAddr);
    cursorInAddrAreaClear(self);
  else
    /* beep if dclick not in address area */
    beep();
    ^nil;
  endif;
  ^0;
}!!

/* Disable normal button-down processing; only want to position cursor. */
Def WM_LBUTTONDOWN(self, wp, lp)
{
  setCurPos(self, asPoint(lp));
  
  /* pretend we ignored it... */
  if readError?(self)
    hideCaret(self);
    ^nil;
  endif;
  
  ^0;
}!!

/* Disable normal button-up processing; only want to position the cursor. */
Def WM_LBUTTONUP(self, wp, lp)
{
  setCurPos(self, asPoint(lp));
  
  /* pretend we ignored it... */
  if readError?(self)
    hideCaret(self);
    ^nil;
  endif;
  
  ^0;
}!!

/* Respond to MS-Window's vertical scrolling message. wP tells what kind of
  scrolling request has been made.
  Adjust screenTop to reflect the new top of screen.
  Adjust xPos and yPos to maintain the current virtual cursor location. */
Def WM_VSCROLL(self, wP, lP | vis tempMod rowCount displayPercent thumbPos)
{
  writeDirtyBytes(self, 0);
  vis := visLines(self);
  select
    /* move down one line if not at end */
    case wP == SB_LINEDOWN cand screenEnd < maxRow is
      desiredScreenTop := screenTop + 1L;
      yPos := yPos - 1;
      fixScreen(self, 0);
    endCase
    /* move down a page (less 1 line) subject to remaining lines */
    case wP == SB_PAGEDOWN cand screenEnd < maxRow is
      desiredScreenTop := min(screenTop + vis - 1, maxRow - vis + 1);
      desiredScreenTop := asLong(desiredScreenTop);
      if yPos = (vis - 1) then
        yPos := 0;
      else
        xPos := yPos := 0;
      endif;
      fixScreen(self, 0);
    endCase
    /* move up one line if not at top */
    case wP == SB_LINEUP cand screenTop > 0 is
      desiredScreenTop := screenTop - 1L;
      yPos := yPos + 1;
      fixScreen(self, 0);
    endCase
    /* move down a page (less 1 line) if there is room */
    case wP == SB_PAGEUP cand screenTop > 0 is
      desiredScreenTop := max(0, screenTop - vis + 1);
      desiredScreenTop := asLong(desiredScreenTop);
      if yPos = 0 then
        yPos := vis - 1;
      else
        xPos := yPos := 0;
      endif;
      fixScreen(self, 0);
    endCase
    /* move to relative position; do it funny to avoid over/under flow. */
    case wP == SB_THUMBPOSITION is
      rowCount := maxRow - minRow;
      tempMod := rowCount mod 100L;
      displayPercent := rowCount / 100L;
      thumbPos := asLong(low(lP));
      desiredScreenTop := (displayPercent*thumbPos) +
                   ((tempMod * thumbPos) / 100L);
      /* don't exceed last page */
      desiredScreenTop := min(maxRow - asLong(vis) + 1, desiredScreenTop);
      xPos := yPos := 0;
      fixScreen(self, 0);
    endCase
  endSelect;
}!!

/* Write the workTextBytes array to the server; flush if indicated. */
Def writeDirtyBytes(self, flush | index byteCount writeStatus)
{
  /* exit if there are no dirty bytes */
  if not(dirtyByteRow) cor readError?(self)
    ^0;
  endif;
  
  showWaitCurs();
  
  /* stuff bytes into structure */
  byteCount := dirtyByteColMax - dirtyByteColMin + 1;
  index := 0;
  loop
  while index < byteCount
  begin
    putByte(byteArray,
      workTextBytes[asInt(dirtyByteRow)][asInt(dirtyByteColMin + 
index)],
      index);
    index := index + 1;
  endLoop;
  
  /* since we are invalidating the cache, ignore it one time only */
  ignoreMemoryChangeEvent := 0;
  
  /* set up address descriptors for write */
  if not(copyAddress(AddressLibClass$Inst, baseAddressDescriptor,
    workAddressDescriptor))
      ^nil;
  endif;
  if not(setOffset(AddressLibClass$Inst, workAddressDescriptor,
    asLong(((workTextTop + dirtyByteRow) * 16) + dirtyByteColMin)))
      ^nil;
  endif;

  /* write bytes out; last 3 args are start address, # bytes, bytes. */
  writeStatus := writeMemAD(memServer(parent), workAddressDescriptor,
    byteCount, byteArray);
    
  /* clear out old dirty byte info */
  dirtyByteRow := dirtyByteColMin := dirtyByteColMax := nil;
  
  /* invalidate if there is a write error */
  if not(writeStatus)
    invalidateCache(self);
  endif;

  showOldCurs();
  
  ^0;  /* error reporting done by library */
}!!

/* MBrowser Class Initialization Code */

