view anagram/vaclgui/trfview.cpp @ 14:a02e9434072e

Fix friend declaration for gcc10. XXX: did not check it against the IBM compiler, might end up needing XXX: to be conditional.
author David A. Holland
date Tue, 31 May 2022 00:59:42 -0400
parents 13d2b8934445
children
line wrap: on
line source

/*
 * AnaGram, A System for Syntax Directed Programming
 * Copyright 1997-2002 Parsifal Software. All Rights Reserved.
 * See the file COPYING for license and usage terms.
 *
 * trfview.cpp
 */

#include <icoordsy.hpp>
#include <windows.h>

#include "agstring.h"
#include "cint.h"
#include "config.h"
#include "ctrlpanel.hpp"
#include "ftpar.h" // for precedes()
#include "ftview.hpp"
#include "minmax.h"
#include "trfview.hpp"
#include "vaclgui.hpp"

//#define INCLUDE_LOGGING
#include "log.h"


#define PARSE_LOCATION (parser.location())

DigSetter::Style TraceFileView::displayStyle[4] = {
  DigSetter::Style(FontSpec::traceFile, ColorSpec::traceFileScanned),
  DigSetter::Style(FontSpec::traceFile, ColorSpec::traceFileUnscanned),
  DigSetter::Style(FontSpec::traceFile, ColorSpec::traceFileHilite),
  DigSetter::Style(FontSpec::traceFile, ColorSpec::dialogBackground)
};

TraceFileView::TraceFileView(IWindow *owner, text_file &file_, 
			     AgString fileName)
  : FileView(owner, file_)
  , windowFont(FontSpec::traceFile)
  , margin(windowFont.avgCharWidth())
  , painting(0)
  , enWidth(windowFont.avgCharWidth())
  , setter(&dataArea, displayStyle)
  , beginHighlight(0,0)
  , endHighlight(0,0)
  , activeHighlight(0)
  , parseAction(actionObject(this, parseToCursor))
  , parser(file_.text)
  , dataColorChange(this, onColorChange)
  , fontChange(this, onFontChange)
  , clickEnabled(0)
  , doubleClickEnabled(0)
{
  LOGSECTION("TraceFileView::TraceFileView");
  LOGV((int) this);

  dataColorChange.attach(&ColorSpec::traceFileScanned);
  dataColorChange.attach(&ColorSpec::traceFileUnscanned);
  dataColorChange.attach(&ColorSpec::traceFileHilite);

  fontChange.attach(&FontSpec::traceFile);

  for (int i = 0; i < 256; i++) {
    charWidth[i] = windowFont.charWidth(i);
  }
  tableWidth = findMaxWidth();
  dataHole.setBackgroundColor(ColorSpec::traceFileUnscanned.bg());
  pixelCursor.x = margin;
  enableCursor();

  IMousePointerHandler::handleEventsFor(&verticalScrollBar);
  IMousePointerHandler::handleEventsFor(&horizontalScrollBar);
  IMousePointerHandler::handleEventsFor(&dataHole);
}


TraceFileView::~TraceFileView() {
  LOGSECTION("TraceFileView::~TraceFileView");
  IMousePointerHandler::stopHandlingEventsFor(&verticalScrollBar);
  IMousePointerHandler::stopHandlingEventsFor(&horizontalScrollBar);
  IMousePointerHandler::stopHandlingEventsFor(&dataHole);
}

void TraceFileView::onFontChange() {
  LOGSECTION("TraceFileView::onFontChange");
  setFont(FontSpec::syntaxFile);
  dataHole.setFont(FontSpec::dataTable);
  windowFont = FontSpec::traceFile;
  //for (int i = 0; i < 256; i++) {
  //  charWidth[i] = windowFont.charWidth(i);
  //}
  doLayout();
  refresh();
}

void TraceFileView::reload() {
  LOGSECTION("TraceFileView::reload");
  LOGV((int) file.text.pointer());
  file.read_file();
  parser.text = file.text;
  LOGV((int) file.text.pointer());
  doLayout();
}

TraceFileView &TraceFileView::doLayout() {
  setFont(windowFont);
  for (int i = 0; i < 256; i++) {
    charWidth[i] = windowFont.charWidth(i);
  }
  enWidth = windowFont.avgCharWidth();
  margin = enWidth;
  lineHeight = windowFont.maxSize().height() + windowFont.externalLeading();
  FileView::doLayout();
  return *this;
}

cint TraceFileView::getCursorLocation() {
  return cursorLocation;
}

int TraceFileView::findMaxWidth() {
  LOGSECTION("TraceFileView::findMaxWidth");
  int maxWidth = 0;
  int nl = nLines();
  LOGV(nl);

  while (nl--) {
    AgString line = getLine(nl);
    if (!line.exists()) {
      continue;
    }
    LOGV(line.pointer());
    int width = measureWidth(line.pointer());
    if (width > maxWidth) {
      LOGV(nl) LCV(width);
      maxWidth = width;
    }
  }
  maxWidth += 2*margin;
  LOGV(maxWidth);
  return maxWidth;
}

ISize TraceFileView::suggestSize() {
  LOGSECTION("TraceFileView::suggestSize");
  int n = nLines();
  if (n < 5) {
    n = 5;
  }
  if (n > defaultWindowHeight) {
    n = defaultWindowHeight;
  }
  int height = n*(windowFont.maxSize().height() + 
		  windowFont.externalLeading());
  int width = 60*windowFont.avgCharWidth() + 2 * margin;
  int minWidth = 40*windowFont.avgCharWidth() + 2 * margin;
  LOGV(width) LCV(minWidth);
  LOGV(tableWidth);
  if (width > tableWidth) {
    width = tableWidth;
  }
  if (width < minWidth) {
    width = minWidth;
  }
  return ISize(width, height);
}

TraceFileView &TraceFileView::parseLine(int ln, AgString line, 
					int begin, int end) {
  LOGSECTION("TraceFileView::parseLine");
  char *p = line.pointer();
  if (p == 0) {
    p = "";
  }
  assert(p != 0);
  nDigs = 0;
  nHoles = 0;
  int ncw = 0;

  int holeX = 0;
  int x = margin;

  int columnWidth = enWidth*tab_spacing;

  int wordWidth = 0;

  cint parseLoc = PARSE_LOCATION;
  if (activeHighlight && precedes(parseLoc, endHighlight)) {
    parseLoc = endHighlight;
  }
  int topToBaseline = windowFont.maxAscender();
  cint where(0, topToBaseline + ln*lineHeight);
  int hPos = horizontalScrollBar.scrollBoxPosition();
  cint size(0, lineHeight);
  cint loc(-1, ln+verticalScrollBar.scrollBoxPosition());
  int styleIndex = !precedes(loc, parseLoc);
  LOGV(loc);
  LOGV(cursorLocation);
  LOGV(styleIndex);
  LOGV(measure);
  LOGV(tableWidth);
  LOGV((char *) dataArea.size().asString());
  if (activeHighlight && styleIndex == 0) {
    styleIndex = 2*(!precedes(loc, beginHighlight) &&
		    precedes(loc, endHighlight));
  }

  loc.x = 0;
  for (; *p && x <= end; p++, loc.x++) {
    int newStyleIndex = !precedes(loc, parseLoc);
    LOGV(loc);
    LOGV(cursorLocation);
    LOGV(parseLoc);
    LOGV(newStyleIndex);

    if (activeHighlight && newStyleIndex == 0) {
      newStyleIndex = 2*(!precedes(loc, beginHighlight) && 
			 precedes(loc, endHighlight));
    }
    if (newStyleIndex != styleIndex) {
      cint holeLocation(holeX - hPos, where.y - topToBaseline);
      LOGV(where.y) LCV(topToBaseline) LCV(holeLocation.y);
      size.x = x - holeX;
      if (x >= begin) {
        hole.push(DigSetter::Hole(holeLocation, size, styleIndex));
        LOGV(holeLocation);
        LOGV(size);
        LOGV(styleIndex);
        nHoles++;
      }
      if (ncw) {
        where.x = x-wordWidth - hPos;
        if (x >= begin) {
          dig.push(DigSetter::Dig(p-ncw, ncw,where, styleIndex));
          nDigs++;
        }
        LOGV(ncw) LCV(p-ncw) LCV(x) LCV(wordWidth);
        wordWidth = ncw = 0;
      }
      styleIndex = newStyleIndex;
      holeX = x;
    }
    if (*p == '\t') {
      LOGSECTION("TraceFileView::parseLine::tab");
      if (ncw) {
        where.x = x-wordWidth - hPos;
        if (x >= begin) {
          dig.push(DigSetter::Dig(p-ncw, ncw,where, styleIndex));
          nDigs++;
	}
        LOGV(ncw) LCV(p-ncw) LCV(x) LCV(wordWidth);
        wordWidth = ncw = 0;
      }
      LOGV(x) LCV(columnWidth);
      x = ((x - margin + columnWidth)/columnWidth) * columnWidth + margin;
      LOGV(x);
      continue;
    }
    if (*p == ' ') {
      if (ncw) {
        where.x = x-wordWidth - hPos;
        if (x >= begin) {
          dig.push(DigSetter::Dig(p-ncw, ncw,where, styleIndex));
          nDigs++;
        }
        LOGV(ncw) LCV(p-ncw) LCV(x) LCV(wordWidth);
        wordWidth = ncw = 0;
      }
      x += enWidth;
      continue;
    }
    int w = charWidth[*(unsigned char *) p];
    x += w;
    wordWidth += w;
    ncw++;
  }
  if (ncw) {
    where.x = x-wordWidth - hPos;
    dig.push(DigSetter::Dig(p-ncw, ncw,where, styleIndex));
    nDigs++;
    LOGV(ncw) LCV(p-ncw) LCV(x) LCV(wordWidth);
    wordWidth = ncw = 0;
  }
  int newStyleIndex = !precedes(loc, parseLoc);
  if (activeHighlight && newStyleIndex == 0) {
    newStyleIndex = 2*(!precedes(loc, beginHighlight) &&
		       precedes(loc, endHighlight));
  }
  if (newStyleIndex != styleIndex) {
    LOGV(newStyleIndex);
    LOGV(styleIndex);
    cint holeLocation(holeX - hPos, where.y - topToBaseline);
    LOGV(holeX);
    LOGV(hPos);
    size.x = x - holeX;
    if (x >= begin) {
      hole.push(DigSetter::Hole(holeLocation, size, styleIndex));
      LOGV(holeLocation);
      LOGV(size);
      LOGV(styleIndex);
      nHoles++;
    }
    styleIndex = newStyleIndex;
    holeX = x;
  }
  where.x = holeX;
  if (*p == 0) {
    x = measure;
  }
  size.x = x - holeX;
  size.x = measure - holeX;
  cint holeLocation(holeX - hPos, where.y - topToBaseline);
  hole.push(DigSetter::Hole(holeLocation, size, styleIndex));
  LOGV(holeLocation);
  LOGV(size);
  LOGV(styleIndex);
  nHoles++;
  LOGV(nHoles) LCV(nDigs);
  return *this;
}

int TraceFileView::measureWidth(const char *line) {
  LOGSECTION("TraceFileView::measureWidth");
  const char *p = line;
  assert(p != 0);
  int x = 0;

  int columnWidth = enWidth*tab_spacing;

  for (; *p; p++) {
    if (*p == '\t') {
      x = ((x + columnWidth)/columnWidth) * columnWidth + enWidth;
      continue;
    }
    if (*p == ' ') {
      x += enWidth;
      continue;
    }
    int width = charWidth[*(unsigned char *) p];
    x += width;
    if (x > 1000) {
      LOGV((unsigned)*p);
      LOGV(charWidth[*(unsigned char *) p]);
      LOGV(charWidth[*p]);
      LOGV(width) LCV(p);
    }
  }
  return x;
}


int TraceFileView::charPosition(int xPos, AgString line) {
  int i = 0;
  int x = margin;
  int columnWidth = enWidth*tab_spacing;
  unsigned char *p = (unsigned char *)line.pointer();

  //LOGSECTION("TraceFileView::charPosition", Log::off);
  LOGSECTION_OFF("TraceFileView::charPosition");

  if (p) for (i = 0; p[i]; i++) {
    int newX = x + charWidth[p[i]];
    if (p[i] == '\t') {
      LOGV(x);
      newX = margin + ((x-margin + columnWidth)/columnWidth) * columnWidth;
      LOGV(x);
    }
    if (p[i] == ' ') {
      LOGV(x);
      newX = x + enWidth;
    }
    //if (newX > xPos) {
    if (xPos <= (x+newX)/2) {
      LOGV(i);
      LOGV(newX);
      return i;
    }
    x = newX;
    LOGV(i);
    LOGV(x);
  }
  return i;
}

int TraceFileView::xPosition(int charPos, AgString line) {
  int i;
  int x = margin;
  int columnWidth = enWidth*tab_spacing;
  unsigned char *p = (unsigned char *) line.pointer();

  //LOGSECTION("TraceFileView::xPosition", Log::off);
  LOGSECTION_OFF("TraceFileView::xPosition");
  LOGV(p);
  LOGV(tab_spacing) LCV(enWidth) LCV(columnWidth);
  LOGV(font().charWidth(' '));

  if (p) for (i = 0; p[i] && i < charPos; i++) {
    if (p[i] == '\t') {
      LOGS("tab") LCV(x);
      x = margin + ((x-margin + columnWidth)/columnWidth) * columnWidth;
      LOGV(x);
      continue;
    }
    if (p[i] == ' ') {
      LOGV(x);
      x += enWidth;
      continue;
    }
    x += charWidth[p[i]];
    LOGV(i);
    LOGV(x);
  }
  return x;
}


Boolean TraceFileView::paintWindow(IPaintEvent &event) {
  if (event.controlWindow() != &dataArea) {
    return false;
  }
  if (painting) {
    return true;
  }
  hideCursor();
  painting++;
  LOGSECTION("TraceFileView::paintWindow");

  LOGV(windowFont.name());
  setter.setEvent(event);

  IRectangle invalidRect(
    ICoordinateSystem::isConversionNeeded()
    ? ICoordinateSystem::convertToApplication(event.rect(),dataArea.size())
    : event.rect()
  );
  LOGV((char*) invalidRect.asString());
  LOGV(dataArea.size().asString());

  LOGV(rect().asString());
  LOGV(verticalScrollBar.rect().asString());

  topLine       = verticalScrollBar.scrollBoxPosition();
  int hPos      = horizontalScrollBar.scrollBoxPosition();

  int minX = invalidRect.minX() + hPos;
  int maxX = invalidRect.maxX() + hPos;

  int baseLine  = verticalScrollBar.scrollBoxPosition();
  int topY = baseLine*lineHeight;
  int minY = invalidRect.minY();
  int maxY = invalidRect.maxY();
  //int bottomY = dataHole.size().height();

  invalidRect = IRectangle(IPoint(minX,minY),
			   (IPoint(maxX, maxY)));

  LOGV(invalidRect.asString());
  LOGV(lineHeight);

  int firstLine = baseLine + invalidRect.minY()/lineHeight;
  int lastLine = baseLine + (invalidRect.maxY()+lineHeight - 1)/lineHeight - 1;

  int windowWidth = dataArea.size().width();
  measure = windowWidth > tableWidth ? windowWidth : tableWidth;
  measure += 2*margin;

  LOGV(windowWidth);
  LOGV(tableWidth);
  LOGV(measure);
  //int topToBaseline = font.maxAscender();

  //int whereX = 0;
  int whereY = firstLine*lineHeight - topY;  //topline location

  LOGV(topLine);
  LOGV(firstLine);
  LOGV(lastLine);
  LOGV(horizontalScrollBar.scrollBoxPosition());

  //int lastInvalidLine = lastLine;
  if (lastLine >= nLines()) {
    lastLine = nLines() - 1;
  }
  //int windowHeight = dataArea.size().height();
  int dataAreaWidth = windowWidth;
  ISize lineSize(dataAreaWidth, lineHeight);
  int k;

  LOGV(beginHighlight);
  LOGV(endHighlight);

  LOGV(minX) LCV(maxX);

  for (k = firstLine; k <= lastLine; k++, whereY += lineHeight) {
    int i;
    LOGV(k) LCV(whereY);
    AgString line = getLine(k);
    LOGV(line.pointer());
    parseLine(k - baseLine, line, minX, maxX);
    LOGV(hole[0].where.y);
    LOGV(k);
    for (i = 0; i < nHoles; i++) {
      if (pixelCursor.x > hole[nHoles-1].where.x) {
        LOGV(i);
        LOGV(hole[i].where);
        LOGV(hole[i].size);
      }
      setter.clear(hole[i]);
    }
    LOGV(nHoles) LCS("holes cleared");
    for (i = 0; i < nDigs; i++) {
      if (pixelCursor.x > hole[nHoles-1].where.x) {
        LOGV(i);
        LOGV(dig[i].where);
      }
      setter.setDig(dig[i]);
    }
    dig.discardData();
    hole.discardData();
    LOGV(nDigs);
    LOGV(line.pointer());
  }
  invalidRect = IRectangle(IPoint(minX - hPos,whereY),
			   (IPoint(maxX - hPos, maxY)));
  if (invalidRect.height() > 0) {
    setter.clear(DigSetter::Hole(invalidRect, 3));
  }
  setter.closeEvent();
  showCursor();
  painting = false;
  return true;
}

TraceFileView &TraceFileView::refreshLines(int first, int last) {
  LOGSECTION("refreshLines");
  int baseLine = verticalScrollBar.scrollBoxPosition();
  first -= baseLine;
  last  -= baseLine;
  first = first * lineHeight;
  last =  (last+1) * lineHeight;
  IRectangle r(IPoint(0,first),IPoint(measure, last));
  LOGV(r.asString());
  //hideCursor();
  dataArea.refresh(r,true);
  //showCursor();
  return *this;
}

TraceFileView &TraceFileView::turnHighlightOff() {
  LOGSECTION("turnHighlightOff");
  int flag = activeHighlight;
  activeHighlight = 0;
  if (flag) {
    LOGV(beginHighlight);
    refreshLines(beginHighlight.y, endHighlight.y);
    //setCursorLocation(parser.state.position());
  }
  return *this;
}

TraceFileView &TraceFileView::turnHighlightOn(unsigned ln) {
  LOGSECTION("turnHighlightOn");
  if (activeHighlight) {
    activeHighlight = 0;
    refreshLines(beginHighlight.y, endHighlight.y);
  }
  unsigned stackDepth = parser.stateStack.size();
  LOGV(parser.state.position());
  LOGV(parser.reductionState.position());
  LOGV(parser.processState);
  LOGV(ln);
  LOGV(parser.reductionIndex);

  if (parser.processState == FtParser::selectionRequired && 
      ln >= parser.reductionIndex) {
    beginHighlight = parser.reductionState.position();
    endHighlight = parser.state.position();
  }
  else if (ln >= stackDepth) {
    //setCursorLocation(parser.state.position());
    scrollTo(parser.state.position());
    return *this;
  }
  else {
    beginHighlight = parser.stateStack[ln++].position();
    if (ln < stackDepth) {
      endHighlight = parser.stateStack[ln].position();
    }
    else if (parser.processState == FtParser::selectionRequired
          && parser.reductionIndex >= parser.stateStack.size()) {
      endHighlight = parser.reductionState.position();
    }
    else {
      endHighlight = parser.state.position();
    }
  }
  activeHighlight = 1;
  LOGV(beginHighlight);
  LOGV(endHighlight);
  //setCursorLocation(beginHighlight);
  scrollTo(beginHighlight);
  refreshLines(beginHighlight.y, endHighlight.y);
  return *this;
}

TraceFileView &TraceFileView::step() {
  LOGSECTION("TraceFileView::step");
  cint oldLoc = PARSE_LOCATION;
  LOGV(oldLoc);
  switch (parser.processState) {
    case FtParser::selectionError:
      parser.processState = FtParser::selectionRequired;
      /* FALLTHROUGH */  /* ? - I think so */
    case FtParser::selectionRequired:
      parser.completeReduction();
  }
  if (parser.processState > FtParser::running) {
    if (precedes(cursorLocation, PARSE_LOCATION)) {
      if (parser.processState == FtParser::finished) {
	parser.reset();
      }
      parser.processState = FtParser::running;
      refreshHighlight(oldLoc);
    }
    else {
      messageBeep();
      return *this;
    }
  }
  FileTraceWindow *x = (FileTraceWindow *) frameWindow;
  ISystemPointerHandle newHandle(ISystemPointerHandle::wait);
  x->setMousePointer(newHandle);
  x->setStatusField("Running");

  turnHighlightOff();
  if (oldLoc != cursorLocation) {
    parseToCursor();
    return *this;
  }
  parser.step();
  setCursorLocation(PARSE_LOCATION);
  refreshHighlight(oldLoc);
  onEnter();
  return *this;
}


TraceFileView &TraceFileView::parse() {
  LOGSECTION("TraceFileView::parse");
  switch (parser.processState) {
    case FtParser::finished:
      messageBeep();
      return *this;
    case FtParser::selectionError:
      parser.processState = FtParser::selectionRequired;
      /* FALLTHROUGH */  /* ? - I think so */
    case FtParser::selectionRequired:
      parser.completeReduction();
  }
  if (parser.processState > FtParser::running) {
    return *this;
  }

  FileTraceWindow *x = (FileTraceWindow *) frameWindow;
  ISystemPointerHandle newHandle(ISystemPointerHandle::wait);
  x->setMousePointer(newHandle);
  x->setStatusField("Running");

  turnHighlightOff();
  cint oldLoc = PARSE_LOCATION;
  LOGV(oldLoc);
  parser.parse();
  setCursorLocation(PARSE_LOCATION);
  refreshHighlight(oldLoc);
  onEnter();
  return *this;
}

Boolean TraceFileView::virtualKeyPress(IKeyboardEvent &event) {
  LOGSECTION("TraceFileView::virtualKeyPress");
  LOGV(event.virtualKey());
  switch (event.virtualKey()) {
    case IKeyboardEvent::left:
    case IKeyboardEvent::up:
    case IKeyboardEvent::pageUp:
    case IKeyboardEvent::right:
    case IKeyboardEvent::down:
    case IKeyboardEvent::pageDown:
    case IKeyboardEvent::home:
    case IKeyboardEvent::end:
    {
      int flag = PARSE_LOCATION == cursorLocation;
      AgView::virtualKeyPress(event);
      AgString line = getLine(cursorLocation.y);
      pixelCursor.x = xPosition(cursorLocation.x, line);
      cursorLocation.x = charPosition(pixelCursor.x, line);
      if (flag) {
	parseAction();
      }
      else if (PARSE_LOCATION == cursorLocation) {
	resynchAction();
      }
      if (PARSE_LOCATION != cursorLocation) {
	desynchAction();
      }
      return true;
    }
    case IKeyboardEvent::newLine:
    case IKeyboardEvent::enter:
    {
      LOGSECTION("TraceFileView::enter");
      cint oldLoc = PARSE_LOCATION;

      LOGV(oldLoc);
      LOGV(cursorLocation);
      if (cursorLocation != oldLoc) {
        parseAction();
        return true;
      }
      parser.step();
      cursorLocation = PARSE_LOCATION;
      LOGV(cursorLocation);
      cursorLine = cursorLocation.y;
      pixelCursor.x = xPosition(cursorLocation.x,getLine(cursorLine));
      pixelCursor.y = cursorLine*lineHeight;
      setCursorPos(pixelCursor);
      refreshHighlight(oldLoc);
      onEnter();
      return true;
    }
  }
  return false;
};

Boolean TraceFileView::mousePointerChange(IMousePointerEvent &event) {
  LOGSECTION("TraceFileView::mousePointerChange");
  if (event.controlWindow() != &dataHole) {
    return false;
  }
  if (ControlPanel::helpCursorSet) {
    event.setMousePointer(ControlPanel::helpCursor);
  }
  else {
    event.setMousePointer(ISystemPointerHandle(ISystemPointerHandle::text));
  }
  LOGS("Pointer changed");
  return true;
}

Boolean TraceFileView::mouseClicked(IMouseClickEvent &event) {
  LOGSECTION("TraceFileView::mouseClickEvent");
  LOGV(event.mouseButton());
  LOGV(event.mouseAction());
  if (event.mouseAction() == IMouseClickEvent::down) {
    dataArea.setFocus();
  }
  if (event.controlWindow() == &verticalScrollBar ||
      event.controlWindow() == &horizontalScrollBar) {
    //if (event.mouseAction() == IMouseClickEvent::down) {
    //  dataArea.setFocus();
    //}
    return false;
  }
  if (event.windowUnderPointer() != dataArea.handle()) {
    return false;
  }
  if (event.controlWindow() != event.dispatchingWindow()) {
    return false;
  }

  clickEnabled |= event.mouseAction() == IMouseClickEvent::down;
  clickEnabled &= !ControlPanel::helpCursorSet;
  int line = event.mousePosition().y()/lineHeight;
  line += verticalScrollBar.scrollBoxPosition();
  if (line >= nLines()) {
    return false;
  }

  LOGV((int) event.controlWindow()) LCV((int) event.dispatchingWindow());

  if (event.mouseButton() != IMouseClickEvent::button1) {
    return false;
  }
  switch (event.mouseAction()) {
    case IMouseClickEvent::down:
    case IMouseClickEvent::up:
      return false;
    case IMouseClickEvent::click: {
      if (!clickEnabled) {
	return false;
      }
      clickEnabled = 0;
      doubleClickEnabled = 1;
      LOGSECTION("TraceFileView::click");
      //setFocus();
      cursorLine = line;
      int offset = horizontalScrollBar.scrollBoxPosition();
      int whereX = event.mousePosition().x() + offset;
      LOGV(cursorLine) LCV(offset) LCV(whereX);
      AgString text = getLine(line);
      cint loc(charPosition(whereX, text), line);
      LOGV(loc);

      LOGV(cursorLocation) LCV(PARSE_LOCATION);
      int synchFlag = cursorLocation == PARSE_LOCATION;
      cursorLocation = loc;
      cursorLine = cursorLocation.y;
      pixelCursor.x = xPosition(loc.x, text);
      pixelCursor.y = loc.y*lineHeight;
      LOGV(pixelCursor);
      setCursorPos(pixelCursor);
      LOGV(cursorLocation) LCV(PARSE_LOCATION);
      if (!synchFlag && PARSE_LOCATION == cursorLocation) {
	resynchAction();
      }
      if (synchFlag && cursorLocation != PARSE_LOCATION) {
	desynchAction();
      }
      return false;
    }
    case IMouseClickEvent::doubleClick: {
      if (!doubleClickEnabled) {
	return false;
      }
      doubleClickEnabled = 0;
      LOGSECTION("TraceFileView::doubleClick");
      //setFocus();
      cursorLine = line;
      int offset = horizontalScrollBar.scrollBoxPosition();
      int whereX = event.mousePosition().x() + offset;
      LOGV(cursorLine) LCV(offset) LCV(whereX);
      AgString text = getLine(line);
      cint loc(charPosition(whereX, text), line);
      LOGV(loc);

      cursorLocation = loc;
      cursorLine = cursorLocation.y;
      pixelCursor.x = xPosition(loc.x, text);
      pixelCursor.y = loc.y*lineHeight;
      LOGV(pixelCursor);
      setCursorPos(pixelCursor);
      parseAction();
      return false;
    }
  }
  //return TraceFileView::mouseClicked(event);
  return false;
}

Boolean TraceFileView::findNext(AgString s) {
  int synchFlag = cursorLocation == PARSE_LOCATION;
  int flag = FileView::findNext(s);
  if (!synchFlag && PARSE_LOCATION == cursorLocation) {
    resynchAction();
  }
  if (synchFlag && cursorLocation != PARSE_LOCATION) {
    desynchAction();
  }
  return flag;
}

Boolean TraceFileView::findPrev(AgString s) {
  int synchFlag = cursorLocation == PARSE_LOCATION;
  int flag = FileView::findPrev(s);
  if (!synchFlag && PARSE_LOCATION == cursorLocation) {
    resynchAction();
  }
  if (synchFlag && cursorLocation != PARSE_LOCATION) {
    desynchAction();
  }
  return flag;
}

TraceFileView &TraceFileView::refreshHighlight(cint oldLoc) {
  LOGSECTION("TraceFileView::refreshHighlight");
  int newLine = cursorLocation.y;
  int hPos = horizontalScrollBar.scrollBoxPosition();
  LOGV(hPos);
  if (cursorLocation != oldLoc) {
    LOGV(oldLoc);
    LOGV(cursorLocation);
    int top = min(oldLoc.y, newLine);
    top -= verticalScrollBar.scrollBoxPosition();
    int bottom = max(oldLoc.y, newLine);
    bottom -= verticalScrollBar.scrollBoxPosition();
    bottom++;
    //hideCursor();
    LOGV(top);
    LOGV(bottom);
    updateCursor();
    if (newLine == oldLoc.y) {
      //int whereY = top*lineHeight;
      AgString line = getLine(newLine);
      int oldX = xPosition(oldLoc.x, line);
      if (oldLoc.x == 0) {
	oldX = 0;
      }
      IPoint oldPoint(oldX-hPos, lineHeight*top);
      int newX = xPosition(cursorLocation.x, line);
      if (cursorLocation.x == 0) {
	newX = 0;
      }
      IPoint newPoint(newX - hPos, lineHeight*bottom);
      LOGV(oldPoint.asString());
      LOGV(newPoint.asString());
      IRectangle rectangle(oldPoint, newPoint);
      dataArea.refresh(rectangle, true);
      LOGV(rectangle.asString());
    }
    else {
      int width = dataArea.size().width();
      IRectangle rectangle(0, lineHeight*top, width, lineHeight*bottom);
      dataArea.refresh(rectangle, true);
      LOGV(rectangle.asString());
    }
    //showCursor();
  }
  return *this;
}


void TraceFileView::parseToCursor() {
  LOGSECTION("TraceFileView::parseToCursor");
  cint oldLoc = PARSE_LOCATION;
  LOGV(oldLoc);
  LOGV(cursorLocation);
  switch (parser.processState) {
    case FtParser::finished: {
      if (precedes(cursorLocation, PARSE_LOCATION)) {
        parser.reset();
        refreshHighlight(oldLoc);
        oldLoc = PARSE_LOCATION;
        break;
      }
      messageBeep();
      return;
    }
    case FtParser::selectionError:
      parser.processState = FtParser::selectionRequired;
      /* FALLTHROUGH */  /* ? - I think so */
    case FtParser::selectionRequired:
      parser.completeReduction();
      break;
    case FtParser::syntaxError:
      if (!precedes(cursorLocation, PARSE_LOCATION)) {
        messageBeep();
        return;
      }
    default:
      parser.processState = FtParser::running;
      refreshHighlight(oldLoc);
      break;
  }
  LOGV(cursorLocation) LCV(PARSE_LOCATION);
  turnHighlightOff();
  LOGV(cursorLocation) LCV(PARSE_LOCATION);

  //if (PARSE_LOCATION == cursorLocation) return;
  if (PARSE_LOCATION != cursorLocation) {
    FileTraceWindow *x = (FileTraceWindow *) frameWindow;
    ISystemPointerHandle newHandle(ISystemPointerHandle::wait);
    x->setMousePointer(newHandle);
    x->setStatusField("Running");

    //parser.parseTo(&cursorLocation);
    do {
    	parser.parseTo(&cursorLocation);
	if (parser.processState != FtParser::selectionRequired) {
	  continue;
	}
	int fileIndex = (char *) parser.state.pointer - parser.text.pointer();
	int tn = reductionTable[fileIndex];
	LOGV(fileIndex) LCV(tn);
	if (tn == 0) {
	  continue;
	}
	parser.completeReduction(tn);
    } while (parser.processState <= FtParser::running && 
	     precedes(PARSE_LOCATION, cursorLocation));
    setCursorLocation(PARSE_LOCATION);
    LOGV(oldLoc);
    LOGV(cursorLocation);
    refreshHighlight(oldLoc);
  }
  onEnter();
}