view anagram/vaclgui/ftview.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.
 *
 * ftview.cpp
 */

//#include <imsgbox.hpp>
//#include <itbarcnr.hpp>

#include "agstring.h"
#include "agview.hpp"
#include "arrays.h"
#include "ctrlpanel.hpp"
#include "data.h"
#include "dc.h"
#include "dvplug.hpp"
#include "ftpar.h"
#include "ftview.hpp"
#include "helpview.hpp"
#include "myalloc.h"
#include "p.h"
#include "rule.h"
#include "vaclgui.hpp"

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


#define PARSE_FILE  "&Parse File"
#define SINGLE_STEP "&Single step"
#define RELOAD      "Re&load"
#define RESET       "&Reset"
#define HELP        "&Help"
#define SYNCH_PARSE "&Synch Parse"

//void logChildren(IWindow *window) {
//  LOGSECTION("logChildren");
//  LOGV(window->id());
//  IWindow::ChildCursor cursor(*window);
//  for (cursor.setToFirst(); cursor.isValid(); cursor.setToNext()) {
//    IWindow child(window->childAt(cursor));
//    LOGS("  ") LV(child.id());
//  }
//}

FileTraceView::FileTraceView(FileTraceWindow *frame_,
			     text_file text_,
			     AgString fileName_)
  : ICanvas(nextChildId(), frame_, frame_, IRectangle(),
	      ICanvas::classDefaultStyle
	    | IWindow::clipChildren)
  , mainSplitter(nextChildId(), this, this, IRectangle(),
		   ISplitCanvas::horizontal
		 | IWindow::visible)
  , tracePanels(nextChildId(), &mainSplitter, &mainSplitter, IRectangle(),
		ISplitCanvas::classDefaultStyle)
  , leftPanel(nextChildId(), &tracePanels, &tracePanels, IRectangle(),
	      visible)
  , rightPanel(nextChildId(), &tracePanels, &tracePanels, IRectangle(),
	       visible)
  , stackView(&leftPanel)
  , fileView(&rightPanel, text_, fileName_)
  , fileTitle(nextChildId(), &rightPanel, &rightPanel)
  , fileName(fileName_)
  , textFile(text_)
  , bottomPanel(nextChildId(), &mainSplitter, &mainSplitter, IRectangle(),
		  ISplitCanvas::vertical
		| visible)
  , reductionChoiceView(&bottomPanel)
  , ruleView(&bottomPanel)
  , frame(frame_)
  , dataColorChange(this, onColorChange)
  , fontChange(this, onFontChange)
  , focusControl(FileTraceWindow::fileTab)
  , activePanel(FileTraceWindow::fileTab)
  , fileViewHelp(&rightPanel, "Test File")
{
  LOGSECTION("FileTraceView::FileTraceView");

  LOGV((int) &mainSplitter);
  LOGV((int) &tracePanels);
  LOGV((int) &leftPanel);
  LOGV((int) &rightPanel);
  LOGV((int) &bottomPanel);
  LOGV((int) &stackView);
  LOGV((int) &fileView);
  LOGV((int) &reductionChoiceView);
  LOGV((int) &ruleView);
  LOGV((int) &this);

  LOGV(id()) LCV(handle().asDebugInfo());
  LOGV(leftPanel.id()) LCV((int) &leftPanel) 
    LCV(leftPanel.handle().asDebugInfo());
  LOGV(rightPanel.id()) LCV((int) &rightPanel)
    LCV(rightPanel.handle().asDebugInfo());
  LOGV(stackView.id()) LCV((int) &stackView)
    LCV(stackView.handle().asDebugInfo());
  LOGV(fileView.id()) LCV((int) &fileView)
    LCV(fileView.handle().asDebugInfo());
  LOGV(fileTitle.id()) LCV((int) &fileTitle)
    LCV(fileTitle.handle().asDebugInfo());

  setFont(stackView.dataArea.font());

  ruleView.setEnterAction(AgAction());
  ruleView.copyTitle = "File Trace: Rule Stack";
  reductionChoiceView.copyTitle = "File Trace: Reduction Choices";
  stackView.copyTitle = "File Trace: Parser Stack";

  dataColorChange.attach(&ColorSpec::inactiveTitle);
  dataColorChange.attach(&ColorSpec::activeTitle);
  fontChange.attach(&FontSpec::columnHead);

  reductionMenu = new FtParserReductionDc(fileView.parser);
  AgDataViewPlug *connector = new AgDataViewPlug(reductionMenu);
  reductionMenu->windowConnector = connector;
  reductionChoiceView.init(connector);

  bottomPanel.setSplitWindowPercentage(&reductionChoiceView, 0);
  bottomPanel.setSplitWindowPercentage(&ruleView, 100);

  fileTitle
   . setText(fileName.pointer())
   . setAlignment(IStaticText::centerCenter)
   . setBackgroundColor(ColorSpec::inactiveTitle.bg())
   . setForegroundColor(ColorSpec::inactiveTitle.fg())
   ;
  LOGS("titles set up");
  //dc_ref stackWindow = new FtParserDc(fileView.parser);
  parserDc = new FtParserDc(fileView.parser);

  //fileView.parser.displayControl = stackWindow;
  //stackConnector = new AgDataViewPlug(stackWindow);
  stackConnector = new AgDataViewPlug(parserDc);
  //stackWindow->windowConnector = stackConnector;
  parserDc->windowConnector = stackConnector;
  stackView.init(stackConnector);

  itemStack = fileView.parser.x1x_new();
  //dc_ref ruleWindow = new rule_stack_dc(itemStack,0,stackWindow->head_title);
  //ruleControl = new rule_stack_dc(itemStack,0, stackWindow->head_title);
  ruleControl = new rule_stack_dc(itemStack, 0, parserDc->head_title);
  //fileView.parser.ruleControl = ruleWindow;
  //ruleConnector = new AgDataViewPlug(ruleWindow);
  ruleConnector = new AgDataViewPlug(ruleControl);
  //ruleWindow->windowConnector = ruleConnector;
  ruleControl->windowConnector = ruleConnector;
  ruleView.init(ruleConnector);

  int width = stackView.tableWidth;
  int height = font().externalLeading() + font().maxSize().height();
  ISize minSize(width, 5*height);

  LOGV(minSize.asString());
  leftPanel
   . addToCell(&stackView, 1,1)
   . setColumnWidth(1,width,true)
   . setRowHeight(1,0,true)
   . setMinimumSize(minSize)
   ;
  width = fileTitle.minimumSize().width();
  int maxWidth = IWindow::desktopWindow()->size().width()/3;
  IFont titleFont = fileTitle.font();
  int lineHeight = titleFont.maxCharHeight() + titleFont.externalLeading() 
    + titleFont.maxDescender();
  if (width > maxWidth) {
    width = maxWidth;
  }
  minSize.setWidth(maxWidth);
  minSize.setHeight(lineHeight);
  fileTitle.setMinimumSize(minSize);
  minSize = ISize(width, 5*height);
  LOGV(minSize.asString());
  rightPanel
   . addToCell(&fileTitle, 1, 1)
   . addToCell(&fileView,  1, 2)
   . setColumnWidth(1, width, true)
   . setRowHeight(2, 0, true)
   . setMinimumSize(minSize)
   ;
  mainSplitter.setSplitWindowPercentage(&tracePanels, 60);
  mainSplitter.setSplitWindowPercentage(&bottomPanel, 40);
  tracePanels.setSplitWindowPercentage(&leftPanel, 25);
  tracePanels.setSplitWindowPercentage(&rightPanel, 75);
  LOGS("splitters set");
  AgFocusHandler::handleEventsFor(&stackView.dataArea);
  AgFocusHandler::handleEventsFor(&fileTitle);
  AgFocusHandler::handleEventsFor(&fileView.dataArea);
  AgFocusHandler::handleEventsFor(&reductionChoiceView.dataArea);
  AgFocusHandler::handleEventsFor(&ruleView.dataArea);
  LOGS("Focus handler set");
  IMouseHandler::handleEventsFor(&fileTitle);
  IPaintHandler::handleEventsFor(this);
  LOGS("Mouse handler set");
  focusControl = FileTraceWindow::fileTab;
  LOGS("all done");
}

FileTraceView::~FileTraceView() {
  AgFocusHandler::stopHandlingEventsFor(&stackView.dataArea);
  AgFocusHandler::stopHandlingEventsFor(&fileTitle);
  AgFocusHandler::stopHandlingEventsFor(&fileView.dataArea);
  AgFocusHandler::stopHandlingEventsFor(&reductionChoiceView.dataArea);
  AgFocusHandler::stopHandlingEventsFor(&ruleView.dataArea);
  IMouseHandler::stopHandlingEventsFor(&fileTitle);
  IPaintHandler::stopHandlingEventsFor(this);
}

Boolean FileTraceView::paintWindow(IPaintEvent &event) {
  LOGSECTION("FileTraceView::paintWindow");
  LOGV(event.rect().asString());
  event.clearBackground(IGUIColor::dialogBgnd);
  return false;
}

FileTraceView &FileTraceView::refreshRules(int rule) {
  LOGSECTION("FileTraceView::refreshRules");
  LOGV(rule);
  FtParser &parser = fileView.parser;
  delete_tsd(itemStack);
  itemStack = parser.x1x_new();
  //parser.ruleControl->parser_stack = parser.itemStack;
  ruleControl->parser_stack = itemStack;
  int k = itemStack->nt;
  int ln = parser.stateStack.size();
  //parser.ruleControl->des->d_size.y = k;
  ruleControl->des->d_size.y = k;
  //LOGV(k);
  while (k--) {
    int sx, sn, fn, fx;
    xtxf(itemStack, k, &sx, &sn, &fn, &fx);
    int length = Rule(fn)->length();
    //LOGV(length);
    if (sx == ln && rule == 0) {
      break;
    }
    if (rule && fn == rule && fx == 0 && sx+length >= ln) {
      break;
    }
  }
  k = itemStack->nt - 1 - k;
  //LOGV(k);

  ruleView
   . reset()
   . setCursorLine(itemStack->nt-1)
   . setCursorLine(k)
   . synchCursor(k);
   //. refresh();
  return *this;
}

Boolean FileTraceView::mouseClicked(IMouseClickEvent &event) {
  LOGSECTION("FileTraceView::mouseClicked");
  LOGV(event.mouseButton()) LCV(event.mouseAction());
  if (event.mouseAction() != IMouseClickEvent::down) {
    return false;
  }
  IWindow *controlWindow = event.controlWindow();
  if (controlWindow == &fileTitle) {
    fileView.dataArea.setFocus();
    focusControl = FileTraceWindow::fileTab;
    return false;
  }
  return false;
}

Boolean FileTraceView::gotFocus(IEvent &event) {
  LOGSECTION("FileTraceView::gotFocus");
  LOGV(focusControl);
  IWindowHandle handle = (void *) event.parameter1();
  //IWindow *lastWindow = IWindow::windowWithHandle(handle);
  //LOGV(handle.asUnsigned()) LCV((int) lastWindow);
  LOGV((int) event.controlWindow()) LCV((int) event.dispatchingWindow());
  if (event.controlWindow() == &fileView.dataArea) {
    ColorSpec *color = &ColorSpec::activeTitle;
    fileTitle.setBackgroundColor(color->bg());
    fileTitle.setForegroundColor(color->fg());
    focusControl = activePanel = FileTraceWindow::fileTab;

    fileTitle.refresh();
  }
  else if (event.controlWindow() == &stackView.dataArea) {
    focusControl = activePanel = FileTraceWindow::stackTab;
  }
  else if (event.controlWindow() == &reductionChoiceView.dataArea) {
    focusControl = activePanel = FileTraceWindow::choiceTab;
  }
  else if (event.controlWindow() == &ruleView.dataArea) {
    focusControl = activePanel = FileTraceWindow::ruleTab;
  }
  frame->parseButton.disableDefault();
  frame->resetButton.disableDefault();
  frame->reloadButton.disableDefault();
  frame->helpButton.disableDefault();
  frame->stepButton.enableDefault();

  LOGV(focusControl);
  return false;
}

Boolean FileTraceView::lostFocus(IEvent &event) {
  LOGSECTION("FileTraceView::lostFocus");
  LOGV(focusControl);
  IWindowHandle handle = (void *) event.parameter1();
  //IWindow *nextWindow = IWindow::windowWithHandle(handle);
  //LOGV(handle.asUnsigned()) LCV((int) nextWindow);
  if (event.controlWindow() == &fileView.dataArea) {
    ColorSpec *color = &ColorSpec::inactiveTitle;
    fileTitle.setBackgroundColor(color->bg());
    fileTitle.setForegroundColor(color->fg());
    fileTitle.refresh();
  }
  return false;
}

Boolean FileTraceWindow::characterKeyPress(IKeyboardEvent &event) {
  LOGSECTION("FileTraceWindow::characterKeyPress");
  ok_ptr(this);
  char character = event.character();
  LOGV(character) LCV(event.isCtrlDown());
  if (event.isCtrlDown()) {
    return true;
  }
  if (canvas.focusControl < stepTab) {
    canvas.activePanel = canvas.focusControl;
  }
  switch (character) {
    case 'p':
    case 'P':
      parseFile();
      return true;
    case 's':
    case 'S':
      doStep();
      return true;
    case 'r':
    case 'R':
      resetParser();
      return true;
    case 'l':
    case 'L':
      reload();
      return true;
    case 'h':
    case 'H':
      AgHelpWindow::showHelp("File Trace");
      helpButton.disableDefault();
      helpButton.unhighlight();
      stepButton.enableDefault();
      canvas.focusControl = fileTab;
      return true;
  }
  return false;
}

Boolean FileTraceWindow::virtualKeyPress(IKeyboardEvent &event) {
  LOGSECTION("FileTraceWindow::virtualKeyPress");
  ok_ptr(this);
  int increment = 0;
  int &focusControl = canvas.focusControl;
  int &activePanel = canvas.activePanel;
  LOGV(focusControl);
  LOGV((int) event.controlWindow()) LCV((int) event.dispatchingWindow());
  LOGV(event.virtualKey());
  switch (event.virtualKey()) {
    case IKeyboardEvent::enter:
    case IKeyboardEvent::newLine: {
      //activePanel = focusControl;
      if (focusControl >= stepTab) {
	tabControl[focusControl].action.performDeferred();
	IPushButton *button = (IPushButton *)tabControl[focusControl].window;
	button->disableDefault();
	button->unhighlight();
      }
      else {
	activePanel = focusControl;
	doStep();
      }
      return true;
    }
    case IKeyboardEvent::right:
    case IKeyboardEvent::down:
      if (focusControl < stepTab) {
	return AgFrame::virtualKeyPress(event);
      }
      if (focusControl + 1 == nTabs) {
	increment = stepTab - focusControl;
      }
      else {
	increment = 1;
      }
      break;
    case IKeyboardEvent::tab:
      increment = 1;
      break;
    case IKeyboardEvent::left:
    case IKeyboardEvent::up:
      if (focusControl < stepTab) {
	return AgFrame::virtualKeyPress(event);
      }
      if (focusControl == stepTab) {
	increment = nTabs - 1 - stepTab;
      }
      else {
	increment = nTabs - 1;
      }
      break;
    case IKeyboardEvent::backTab:
      increment = nTabs - 1;
      break;
    default:
      return AgFrame::virtualKeyPress(event);
  }
  if (event.controlWindow() != event.dispatchingWindow()) {
    return true;
  }
  IPushButton *button;
  if (focusControl >= stepTab) {
    button = (IPushButton *) tabControl[focusControl].window;
    button->unhighlight();
    button->disableDefault();
  }
  do {
    focusControl = (focusControl + increment) % nTabs;
  } while (!tabControl[focusControl].enabled);
  LOGV(focusControl) LCV(increment);
  if (focusControl >= stepTab) {
    button = (IPushButton *) tabControl[focusControl].window;
  }
  else {
    button = &stepButton;
  }
  button->enableDefault();
  setFocus();
  return true;
}

Boolean FileTraceWindow::mouseClicked(IMouseClickEvent &event) {
  LOGSECTION("FileTraceWindow::mouseClicked");
  ok_ptr(this);
  LOGV(event.mouseButton()) LCV(event.mouseAction());
  if (event.mouseButton() != 0) {
    return false;
  }
  int &focusControl = canvas.focusControl;
  int &activePanel = canvas.activePanel;
  IWindow *controlWindow = event.controlWindow();
  LOGV((int)controlWindow) LCV((int)event.dispatchingWindow());

  if (event.mouseAction() == IMouseClickEvent::click) {
    return false;
  }
  if (event.mouseAction() == IMouseClickEvent::doubleClick) {
    return false;
  }

  int tab;
  for (tab = 0; tab < nTabs; tab++) {
    LOGV(tab) LCV((int) tabControl[tab].window);
    if (controlWindow == tabControl[tab].window) {
      break;
    }
  }
  if (tab == nTabs) {
    return False;
  }

  if (ControlPanel::helpCursorSet) {
    if (event.mouseAction() == IMouseClickEvent::up) {
      ControlPanel::helpCursorSet = 0;
      LOGV(tabControl[tab].helpTopic);
      AgHelpWindow::showHelp(tabControl[tab].helpTopic);
      ControlPanel::resetCursor();
    }
    return true;
  }
  LOGV(focusControl) LCV(activePanel);
  if (tab < stepTab) {
    return false;
  }
  if (focusControl >= stepTab) {
    IPushButton *button = (IPushButton *) tabControl[focusControl].window;
    button->unhighlight();
    button->disableDefault();
  }
  IPushButton *button = (IPushButton *) tabControl[tab].window;
  if (event.mouseAction() == IMouseClickEvent::down) {
    if (focusControl < stepTab) {
      activePanel = focusControl;
    }
    button->highlight();
    button->setFocus();
    focusControl = tab;
    button->capturePointer();
    return true;
  }
  if (event.mouseAction() == IMouseClickEvent::up) {
    button->unhighlight();
    button->releasePointer();
    AgAction &action = tabControl[tab].action;
    IWindowHandle h = tabControl[tab].window->handle();
    if (h == event.windowUnderPointer()) {
      action.performDeferred();
    }
    else if (activePanel < stepTab) {
      button->disableDefault();
      parseButton.enableDefault();
      focusControl = activePanel;
      setFocus();
    }
    else {
      button->disableDefault();
      stepButton.enableDefault();
      focusControl = stepTab;
      setFocus();
    }
    return true;
  }
  return false;
}


FileTraceWindow &FileTraceWindow::setFocus() {
  LOGSECTION("FileTraceWindow::setFocus");
  ok_ptr(this);
  int &focusControl = canvas.focusControl;
  if (focusControl < stepTab) {
    canvas.activePanel = focusControl;
  }
  LOGV(focusControl) LCV((int) tabControl[focusControl].window);
  tabControl[focusControl].window->setFocus();
  IPushButton *button;
  for (int i = stepTab; i < nTabs; i++) {
    button = (IPushButton *)tabControl[i].window;
    button->disableDefault();
    button->unhighlight();
  }
  button = &stepButton;
  if (focusControl > stepTab) {
    button = (IPushButton *)tabControl[focusControl].window;
  }
  button->enableDefault();
  return *this;
}


FileTraceWindow::FileTraceWindow(AgString fileName_,
				 int flags)
  : AgFrame(  IFrameWindow::dialogBackground
	    | IFrameWindow::systemMenu
	    | IFrameWindow::maximizeButton
	    | IFrameWindow::sizingBorder)
  , canvas(this, text_file(fileName_, flags), fileName_)
  , fileName(fileName_)
  , toolBar(this, ISetCanvas::packTight | ISetCanvas::centerVerticalAlign)
  , locationField(nextChildId(), &toolBar, &toolBar, IRectangle(),
		    IStaticText::defaultStyle()
		  | IStaticText::center
		  | IStaticText::vertCenter
		  | IStaticText::border3D)
  , locationFieldHelp(&locationField, "Parse Location")
  , statusField(nextChildId(), &toolBar, &toolBar, IRectangle(),
		  IStaticText::defaultStyle()
		| IStaticText::center
		| IStaticText::vertCenter
		| IStaticText::border3D)
  , statusFieldHelp(&statusField, "Parse Status")
  , buttonGroup(&toolBar, ISetCanvas::packExpanded)
  , stepButton(IDTB_STEP, &buttonGroup, SINGLE_STEP)
  , parseButton(IDTB_PARSE_FILE, &buttonGroup, PARSE_FILE)
  , resetButton(IDTB_RESET, &buttonGroup, RESET)
  , reloadButton(IDTB_RELOAD, &buttonGroup, RELOAD)
  , helpButton(IDTB_HELP, &buttonGroup, HELP)
{
  LOGSECTION("FileTraceWindow::FileTraceWindow");
  ok_ptr(this);

  tabControl[stackTab] = TabControl(&canvas.stackView.dataArea,
				    "Parser Stack Pane");
  tabControl[fileTab] = TabControl(&canvas.fileView.dataArea,
				   "Test File Pane");
  tabControl[choiceTab] = TabControl(&canvas.reductionChoiceView.dataArea,
				     "Reduction Choices Pane");
  tabControl[choiceTab].enabled = 0;
  tabControl[ruleTab] = TabControl(&canvas.ruleView.dataArea, 
				   "Rule Stack Pane");
  tabControl[stepTab]
    = TabControl(&stepButton, "Single Step", actionObject(this, doStep));
  tabControl[parseTab]
    = TabControl(&parseButton, "Parse File", actionObject(this, parseFile));
  tabControl[resetTab]
    = TabControl(&resetButton, "Reset", actionObject(this, resetParser));
  tabControl[reloadTab]
    = TabControl(&reloadButton, "Reload", actionObject(this, reload));
  tabControl[helpTab]
    = TabControl(&helpButton, "File Trace", actionObject(this, showHelp));

#ifdef INCLUDE_LOGGING
  {
    for (int tab = 0; tab < nTabs; tab++) {
      LOGV(tab) LCV((int) tabControl[tab].window);
    }
  }
#endif
  setFont(canvas.stackView.dataArea.font());

  ISize buttonSize= stepButton.minimumSize();
  IFont buttonFont = stepButton.font();
  int buttonWidth = buttonFont.textWidth(" Synch Parse ");
  LOGV(buttonWidth);
  buttonSize.setWidth(buttonWidth);
  stepButton.setMinimumSize(buttonSize);
  parseButton.setMinimumSize(buttonSize);
  int buttonHeight = buttonSize.height();

  syntaxDependent = 1;
  IColor buttonTextColor = stepButton.foregroundColor();

  ISize minimum(buttonWidth, buttonHeight);
  LOGV(minimum.asString());

  locationField.setFont(buttonFont);

  locationField.setMinimumSize(
    ISize(locationField.font().textWidth("9999:999"), 
	  minimum.height())
  );
  locationField.setBackgroundColor(IGUIColor::dialogBgnd);
  locationField.setForegroundColor(buttonTextColor);
  LOGV(locationField.minimumSize().asString());

  statusField.setFont(buttonFont);
  LOGV(statusField.minimumSize().asString());
  statusField.setMinimumSize(
    ISize(statusField.font().textWidth("MSelect reduction tokenM"), 
	  minimum.height())
  );
  LOGV(statusField.minimumSize().asString());
  statusField.setBackgroundColor(IGUIColor::dialogBgnd);
  statusField.setForegroundColor(buttonTextColor);

  ISize margin = toolBar.margin();
  margin.setHeight(0);
  toolBar.setMargin(margin);
  margin = buttonGroup.margin();
  margin.setWidth(0);
  margin.setHeight(margin.height()/2);
  buttonGroup.setMargin(margin);
  ISize pad = buttonGroup.pad();
  pad.setWidth(0);
  buttonGroup.setPad(pad);

  stepButton.enableDefault();

  LOGV(stepButton.minimumSize().asString());
  LOGV(parseButton.minimumSize().asString());
  LOGV(resetButton.minimumSize().asString());
  LOGV(reloadButton.minimumSize().asString());
  LOGV(helpButton.minimumSize().asString());

  LOGV((int) this);
  setClient(&canvas);
  AgString objectTitle = AgString::format("AnaGram : %s",
					  simple_file_name.pointer());
  windowTitle.setObjectText(objectTitle.pointer());

  addExtension(&toolBar, IFrameWindow::belowClient, IFrameWindow::thickLine);
  canvas.fileView.desynchAction = actionObject(this, onDesynch);
  canvas.fileView.resynchAction = actionObject(this, onResynch);

  windowTitle.setViewText("File Trace");
  registerTitle("File Trace");

  canvas.stackView.setColumnTitles("\tState\tToken\tToken Name");
  ISize sizeLeft = canvas.stackView.suggestSize();
  ISize sizeRight = canvas.fileView.suggestSize();
  LOGV(sizeLeft.asString());
  LOGV(sizeRight.asString());
  int thickness =
    2*canvas.tracePanels.splitBarThickness(ISplitCanvas::splitBarEdge);
  thickness +=
    canvas.tracePanels.splitBarThickness(ISplitCanvas::splitBarMiddle);
  int width = sizeLeft.width() + sizeRight.width() + thickness;
  int height = max(2*sizeLeft.height(), sizeRight.height());
  height += height/2;

  LOGV(width) LCV(height);
  int maxWidth = 2*IWindow::desktopWindow()->size().width()/3;
  LOGV(maxWidth);
  int toolWidth = toolBar.minimumSize().width();
  if (maxWidth < toolWidth) {
    maxWidth = toolWidth;
  }
  if (width > maxWidth) {
    width = maxWidth;
  }
  if (width < toolWidth) {
    width = toolWidth;
  }

  int left = canvas.stackView.columnHeadWidth;
  left += 2*font().avgCharWidth();
  int maxRight = maxWidth - left;
  int right = sizeRight.width();
  LOGV(right);
  LOGV(maxRight);
  if (right > maxRight) right = maxRight;
  int sum = left+right;
  LOGV(left) LCV(right) LCV(sum);
  canvas.tracePanels.setSplitWindowPercentage(
    &canvas.leftPanel, (100*left)/sum);
  canvas.tracePanels.setSplitWindowPercentage(
    &canvas.rightPanel, (100*right)/sum);
  LOGS("percentages set") LCV((100*left)/sum) LCV((100*right)/sum);
  ISize clientSize(width, height);
  LOGV(clientSize.asString());
  IRectangle frameRect(frameRectFor(clientSize));
  LOGV(frameRect.asString());
  sizeTo(frameRect.size());
  LOGV(frameRect.size().asString());

  positionFrame();
  canvas.stackView
   . setFrame(this)
   . setEnterAction(AgAction())
   . setSelectAction(actionObject(this, stackSelect))
   ;

  canvas.fileView
   . setFrame(this)
   . setEnterAction(actionObject(this, fileEnter))
   ;

  canvas.ruleView
   . setEnterAction(AgAction())
   . setSelectAction(actionObject(this, ruleSelect))
   . setFrame(this)
   ;

  canvas.reductionChoiceView
   . setEnterAction(actionObject(this, reductionChoiceEnter))
   . setSelectAction(actionObject(this, tokenSelect))
   ;

  FtParser &parser = canvas.fileView.parser;
  setLocationField(canvas.fileView.parser.displayLocation());
  statusField.setText(FtParser::processStateText[parser.processState]);

  for (int i = 0; i < nTabs; i++) {
    IKeyboardHandler::handleEventsFor(tabControl[i].window);
    IMouseHandler::handleEventsFor(tabControl[i].window);
  }
  frameHandler->setActivateAction(actionObject(this, onActivate));
  AgFocusHandler::handleEventsFor(this);
  LOGS("Handlers set");
  canvas.focusControl = canvas.activePanel = fileTab;
  canvas.fileView.dataArea.setFocus();
  //setFocus();
  show();
  LOGS("all done");
}

void FileTraceWindow::onDesynch() {
  LOGSECTION("FileTrace");
  ok_ptr(this);
  stepButton.setText(SYNCH_PARSE);
}

void FileTraceWindow::onResynch() {
  LOGSECTION("FileTrace");
  ok_ptr(this);
  stepButton.setText(SINGLE_STEP);
}

void FileTraceWindow::onActivate() {
  LOGSECTION("FileTraceWindow::onActivate");
  ok_ptr(this);
  //canvas.setFocus();
}

FileTraceWindow::~FileTraceWindow() {
  AgFocusHandler::stopHandlingEventsFor(this);
  for (int i = 0; i < nTabs; i++) {
    IKeyboardHandler::stopHandlingEventsFor(tabControl[i].window);
    IMouseHandler::stopHandlingEventsFor(tabControl[i].window);
  }
}

Boolean FileTraceWindow::gotFocus(IEvent &event) {
  LOGSECTION("FileTraceWindow::gotFocus");
  ok_ptr(this);
  //int &focusControl = canvas.focusControl;
  //LOGV(focusControl);
  IWindowHandle handle = (void *) event.parameter1();
  //IWindow *lastWindow = IWindow::windowWithHandle(handle);
  //LOGV((int) lastWindow);
  LOGV((int) &canvas.stackView.dataArea);
  LOGV((int) &canvas.ruleView.dataArea);
  return false;
}

AgString FileTraceWindow::copyTitle() {
  ok_ptr(this);

  switch (canvas.focusControl) {
    case 0: return canvas.stackView.copyTitle;;
    case 2: return canvas.reductionChoiceView.copyTitle;;
    case 3: return canvas.ruleView.copyTitle;;
  }
  return AgString();
}

FileTraceWindow  &FileTraceWindow::copyTo(IClipboard &c) {
  ok_ptr(this);

  switch (canvas.focusControl) {
    case 0: canvas.stackView.copyTo(c); break;
    case 2: canvas.reductionChoiceView.copyTo(c); break;
    case 3: canvas.ruleView.copyTo(c); break;
  }
  return *this;
}


Boolean FileTraceWindow::windowResize(IResizeEvent &event){
  LOGSECTION("FileTraceWindow::windowResize");
  ok_ptr(this);

  if (event.controlWindow() != this) {
    return false;
  }
  canvas.mainSplitter.sizeTo(
    clientRectFor(IRectangle(IPoint(), event.newSize())).size()
  );
  LOGV(id());
  return false;
}

FileTraceWindow &FileTraceWindow::setLocationField(cint loc) {
  ok_ptr(this);
  char buffer[100];
  sprintf(buffer, "%d:%d", loc.y+1, loc.x+1);
  locationField.setText(buffer);
  return *this;
}

FileTraceWindow &FileTraceWindow::setStatusField(const char *msg) {
  ok_ptr(this);
  statusField.setText(msg);
  statusField.refresh(IWindow::immediate);
  return *this;
}

FileTraceWindow &FileTraceWindow::showReductionSelection() {
  LOGSECTION("FileTraceWindow::showReductionSelection");
  ok_ptr(this);
  FtParser &parser = canvas.fileView.parser;
  canvas.reductionMenu = new FtParserReductionDc(parser);
  AgDataViewPlug *connector = new AgDataViewPlug(canvas.reductionMenu);
  canvas.reductionMenu->windowConnector = connector;

  canvas.reductionChoiceView.init(connector);

  LOGV(canvas.reductionMenu->columnHeadTitle.pointer());
  LOGV(connector->columnHeadTitle().pointer());

  ISize tableSize = canvas.reductionChoiceView.suggestSize();

  int width = 40*font().avgCharWidth();
  int testWidth = tableSize.width();
  if (testWidth < width/2) {
    width = width/2;
  }
  else if (testWidth > 2*width) {
    width = 2*width;
  }
  else {
    width = testWidth;
  }

  int rightWidth = canvas.bottomPanel.size().width();

  int thickness =
    2*canvas.tracePanels.splitBarThickness(ISplitCanvas::splitBarEdge);
  thickness +=
    canvas.tracePanels.splitBarThickness(ISplitCanvas::splitBarMiddle);
  canvas.bottomPanel.setSplitWindowPercentage(&canvas.reductionChoiceView,
					      width);
  canvas.bottomPanel.setSplitWindowPercentage(&canvas.ruleView,
					      rightWidth - width - thickness);

  canvas.bottomPanel.refresh();
  LOGV(canvas.size().asString());
  int ln = parser.reductionSelection;
  canvas.reductionChoiceView
   . setCursorLine(ln)
   . show()
   ;
  parser.reductionState.token = ibnfs[ibnfb[parser.ruleToReduce]+ln];
  canvas.stackView.repaintLine(parser.stateStack.size());
  canvas.reductionChoiceView.dataArea.setFocus();
  return *this;
}

FileTraceView &FileTraceView::completeReduction() {
  LOGSECTION("FileTraceView::completeReduction");
  reductionMenu->des->d_size.y = 0;
  bottomPanel.setSplitWindowPercentage(&reductionChoiceView, 0);
  bottomPanel.setSplitWindowPercentage(&ruleView, 100);
  bottomPanel.refresh();
  FtParser &parser = fileView.parser;
  int lineNumber = reductionChoiceView.getCursorLine();
  LOGV(lineNumber);
  int token = ibnfs[ibnfb[parser.ruleToReduce]+lineNumber];
  LOGV(token);
  parser.completeReduction(token);
  int fileIndex = (char *) parser.state.pointer - parser.text.pointer();
  fileView.reductionTable[fileIndex] = token;
  LOGV(fileIndex) LCV(token) LCV(fileView.reductionTable[fileIndex]);
  return *this;
}

void FileTraceWindow::reductionChoiceEnter() {
  LOGSECTION("FileTraceWindow::reductionChoiceEnter");
  ok_ptr(this);
  canvas.completeReduction();
  fileEnter();
}

void FileTraceWindow::synchRules(unsigned stackIndex, unsigned token) {
  LOGSECTION("FileTraceWindow::synchRules");
  ok_ptr(this);
  FtParser &parser = canvas.fileView.parser;
  unsigned stackLocation = canvas.itemStack->nt;
  int sx, sn, snx;
  if (stackIndex >= parser.stateStack.size()) {
    sn = parser.state.number;
  }
  else {
    sn = parser.stateStack[stackIndex].number;
  }
  state_number_map *sp = &map_state_number[sn];
  LOGV(stackLocation);
  while (stackLocation--) {
    int fn, fx;
    xtxf(canvas.itemStack,stackLocation, &sx, &snx, &fn, &fx);
    if (sx == stackIndex) {
      break;
    }
  }
  int k = stackLocation;
  LOGV(sn);
  LOGV(token);
  if (stackIndex >= parser.stateStack.size()) {
    int ruleViewLine = canvas.itemStack->nt - stackLocation - 1;
    do {
      int fn, fx;
      xtxf(canvas.itemStack, k, &sx, &snx, &fn, &fx);
      if (fx < Rule(fn)->length()
          && token == Rule(fn).token(fx))
          //&& token == lstptr(map_form_number[fn],tokens)[fx])
      {
        k = canvas.itemStack->nt - k - 1;
        canvas.ruleView.setCursorLine(k).synchCursor(k);
        ruleViewLine = k;
        return;
      }
      unsigned *p = lstptr(*sp,reductions);
      unsigned n = sp->n_reductions;
      while (n--) {
        unsigned tn = *p++;
        unsigned rule = *p++;
        if (rule == fn && tn == token) {
          k = canvas.itemStack->nt - k - 1;
          canvas.ruleView.setCursorLine(k).synchCursor(k);
          ruleViewLine = k;
          return;
        }
      }
      k--;
    } while (k >= 0 && sx == stackIndex);
    canvas.ruleView.setCursorLine(ruleViewLine).synchCursor(ruleViewLine);
    return;
  }
  int nextState;
  if (stackIndex + 1 >= parser.stateStack.size()) {
    nextState = parser.state.number;
  }
  else {
    nextState = parser.stateStack[stackIndex+1].number;
  }
  int charToken = map_state_number[nextState].char_token;
  LOGV(nextState) LCV(charToken);
  do {
    int fn,fx;
    xtxf(canvas.itemStack, k, &sx, &snx, &fn, &fx);
    int length = Rule(fn)->length();
    LOGV(k) LCV(snx);
    LOGV(fn) LCV(fx) LCV(length);
    if (fx < length
        && charToken == Rule(fn).token(fx)) {
        //&& charToken == lstptr(map_form_number[fn],tokens)[fx]) {
      k = canvas.itemStack->nt - k - 1;
      canvas.ruleView.setCursorLine(k).synchCursor(k);
      return;
    }
    k--;
    LOGV(k);
    LOGV(sn) LCV(snx);
  } while (k >= 0 && sx == stackIndex);
}

void FileTraceWindow::ruleSelect() {
  LOGSECTION("FileTraceWindow::ruleSelect");
  ok_ptr(this);
  FtParser &parser = canvas.fileView.parser;
  int ruleLine = canvas.ruleView.getCursorLine();

  int k = canvas.itemStack->nt - ruleLine - 1;
  LOGV(k);
  int sx, sn, fn, fx;
  xtxf(canvas.itemStack, k, &sx, &sn, &fn, &fx);
  int length = Rule(fn)->length();
  LOGV(fn);
  LOGV(fx);
  LOGV(length);
  int stackDepth = parser.stateStack.size();
  int stackLevel = sx;
  LOGV(stackLevel) LCV(stackDepth);
  if (stackLevel == stackDepth && 
      parser.processState == FtParser::selectionRequired) {
    if (fx >= length) {
      return;
    }
    //int tn = lstptr(map_form_number[fn],tokens)[fx];
    int tn = Rule(fn).token(fx);
    LOGV(tn);
    int n = ibnfn[parser.ruleToReduce];
    while (n--) {
      LOGV(n);
      LOGV(ibnfs[ibnfb[parser.ruleToReduce]+n]);
      if (ibnfs[ibnfb[parser.ruleToReduce]+n] == tn) {
	break;
      }
    }
    LOGV(fn) LCV(n) LCV(tn);
    canvas.reductionChoiceView.setCursorLine(n);
    parser.reductionState.token = tn;
    canvas.stackView.repaintLine(stackDepth);
    return;
  }
  canvas.stackView.setCursorLine(stackLevel);
  //canvas.fileView.turnHighlightOn(stackLevel);
  if (parser.processState != FtParser::finished) {
    canvas.fileView.turnHighlightOn(stackLevel);
  }
}

void FileTraceWindow::stackSelect() {
  LOGSECTION("FileTraceWindow::stackSelect");
  ok_ptr(this);
  int ln = canvas.stackView.getCursorLine();
  LOGV(ln);
  FtParser &parser = canvas.fileView.parser;
  if (parser.processState != FtParser::finished) {
    canvas.fileView.turnHighlightOn(ln);
  }
  //int sn = parser.state.number;
  int tn = parser.state.token;
  if (ln < parser.stateStack.size()) {
    tn = parser.stateStack[ln].token;
  }
  synchRules(ln, tn);
}

void FileTraceWindow::tokenSelect() {
  LOGSECTION("FileTraceWindow::tokenSelect");
  ok_ptr(this);
  int ln = canvas.reductionChoiceView.getCursorLine();
  LOGV(ln);
  FtParser &parser = canvas.fileView.parser;
  parser.reductionSelection = ln;
  int flag = parser.validSelection(ln, parser.reductionState.number);
  parser.processState = 
    flag ? FtParser::selectionRequired : FtParser::selectionError;
  statusField.setText(FtParser::processStateText[parser.processState]);
  parser.reductionState.token = ibnfs[ibnfb[parser.ruleToReduce]+ln];
  canvas.stackView.repaintLine(parser.stateStack.size());
}

void FileTraceWindow::fileEnter() {
  LOGSECTION("FileTraceWindow::fileEnter");
  ok_ptr(this);
  LOGV(canvas.focusControl) LCV(canvas.fileView.dataArea.hasFocus());
  LOGV(canvas.fileView.cursorHideCount);
  if (stepButton.text() != SINGLE_STEP) {
    stepButton.setText(SINGLE_STEP);
  }
  FtParser &parser = canvas.fileView.parser;
  LOGV(parser.location());
  unsigned stackDepth = parser.stateStack.size();
  //parser.displayControl->des->d_size.y = stackDepth+1;
  canvas.parserDc->des->d_size.y = stackDepth+1;
  LOGV(stackDepth);
  LOGV(parser.processState);

  if (parser.processState > FtParser::running) {
    messageBeep();
  }

  int stackCursor = stackDepth;
  canvas.focusControl = canvas.activePanel = fileTab;
  if (parser.processState == FtParser::selectionRequired) {
    canvas.fileView.turnHighlightOn(stackCursor);

    int k = parser.stateStack.size() - parser.reductionIndex;
    LOGV(k);
    assert((unsigned) k <= (unsigned) parser.stateStack.size());
    parser.stateStack.discardData(k);
    unsigned stackDepth = parser.stateStack.size();
    //parser.displayControl->des->d_size.y = stackDepth+1;
    canvas.parserDc->des->d_size.y = stackDepth+1;

    showReductionSelection();
    stackCursor = parser.reductionIndex;
    canvas.reductionChoiceView.dataArea.setFocus();
    canvas.focusControl = canvas.activePanel = choiceTab;
    tabControl[choiceTab].enabled = 1;
  }
  else if (parser.ruleToReduce) {
    parser.ruleToReduce = 0;
    canvas.reductionMenu->des->d_size.y = 0;
    canvas.bottomPanel.setSplitWindowPercentage(&canvas.reductionChoiceView,0);
    canvas.bottomPanel.setSplitWindowPercentage(&canvas.ruleView, 100);
    canvas.bottomPanel.refresh();
    statusField.setText(FtParser::processStateText[parser.processState]);
    canvas.fileView.turnHighlightOff();
    canvas.fileView.dataArea.setFocus();
    tabControl[choiceTab].enabled = 0;
  }

  LOGV(canvas.focusControl) LCV(canvas.fileView.dataArea.hasFocus());
  canvas.stackView.reset();
  LOGV(canvas.focusControl) LCV(canvas.fileView.dataArea.hasFocus());
  LOGV(canvas.fileView.cursorHideCount);
  canvas.stackView.setCursorLine(stackCursor);
  LOGV(canvas.focusControl) LCV(canvas.fileView.dataArea.hasFocus());
  LOGV(canvas.fileView.cursorHideCount);
  canvas.stackView. refresh();
  LOGV(canvas.focusControl) LCV(canvas.fileView.dataArea.hasFocus());
  LOGV(canvas.fileView.cursorHideCount);

  LOGV(parser.location());
  canvas.refreshRules(parser.ruleToReduce);
  LOGV(canvas.focusControl) LCV(canvas.fileView.dataArea.hasFocus());
  LOGV(canvas.fileView.cursorHideCount);
  LOGV(parser.location());

  setLocationField(canvas.fileView.parser.displayLocation());
  LOGV(canvas.focusControl) LCV(canvas.fileView.dataArea.hasFocus());
  LOGV(canvas.fileView.cursorHideCount);
  setStatusField(FtParser::processStateText[parser.processState]);
  LOGV(canvas.focusControl) LCV(canvas.fileView.dataArea.hasFocus());
  LOGV(canvas.fileView.cursorHideCount);
  //canvas.fileView.dataArea.setFocus();
  //canvas.focusControl = 1;
  setFocus();
  LOGV(canvas.focusControl) LCV(canvas.fileView.dataArea.hasFocus());
  LOGV(canvas.fileView.cursorHideCount);

  AgFrame::windowRegistry.refresh("Trace Coverage");
  stepButton.enableDefault();
  setMousePointer(IPointerHandle());
}

void FileTraceWindow::reload() {
  ok_ptr(this);
  canvas.fileView.reductionTable.reset();
  canvas.fileView.reload();
  canvas.fileView.refresh();
  resetParser();
}

void FileTraceWindow::showHelp() {
  ok_ptr(this);
  AgHelpWindow::showHelp("File Trace");
  helpButton.disableDefault();
  helpButton.unhighlight();
  stepButton.enableDefault();
  canvas.focusControl = FileTraceWindow::fileTab;
}



void FileTraceWindow::resetParser() {
  ok_ptr(this);
  FtParser &parser = canvas.fileView.parser;
  if (parser.ruleToReduce) {
    parser.ruleToReduce = 0;
    canvas.reductionMenu->des->d_size.y = 0;
    canvas.bottomPanel.setSplitWindowPercentage(&canvas.reductionChoiceView,0);
    canvas.bottomPanel.setSplitWindowPercentage(&canvas.ruleView, 100);
    canvas.bottomPanel.refresh();
  }
  parser.reset();
  onResynch();
  setLocationField(canvas.fileView.parser.displayLocation());
  LOGV(parser.state.number) LCV(parser.state.token);
  //parser.displayControl->des->d_size.y = parser.stateStack.size() + 1;
  canvas.parserDc->des->d_size.y = parser.stateStack.size() + 1;
  canvas.refreshRules(0);
  synchRules(parser.stateStack.size(), parser.state.token);
  statusField.setText(FtParser::processStateText[parser.processState]);
  canvas.stackView.reset().setCursorLine(0).refresh();
  canvas.fileView.turnHighlightOff();
  canvas.fileView.setCursorLocation(cint(0,0));
  canvas.fileView.refresh();
  canvas.fileView.dataArea.setFocus();
  canvas.focusControl = canvas.activePanel = fileTab;
  tabControl[choiceTab].enabled = 0;
  stepButton.unhighlight();
  stepButton.enableDefault();
}

Boolean FileTraceWindow::findNext(AgString s) {
  LOGSECTION("FileTraceWindow::findNext");
  ok_ptr(this);
  LOGV(canvas.focusControl);
  int flag = 0;

  switch (canvas.focusControl) {
    case 0: {
      flag = canvas.stackView.findNext(s);
      if (flag) {
	stackSelect();
      }
      return flag;
    }
    case 1: {
      flag = canvas.fileView.findNext(s);
      return flag;
    }
    case 2: {
      flag = canvas.reductionChoiceView.findNext(s);
      if (flag) {
	tokenSelect();
      }
      return flag;
    }
    case 3: {
      flag = canvas.ruleView.findNext(s);
      if (flag) {
	ruleSelect();
      }
      return flag;
    }
  }

  return flag;
}

Boolean FileTraceWindow::findPrev(AgString s) {
  LOGSECTION("FileTraceWindow::findPrev");
  ok_ptr(this);
  LOGV(canvas.focusControl);
  int flag = 0;

  switch (canvas.focusControl) {
    case 0: {
      flag = canvas.stackView.findPrev(s);
      if (flag) {
	stackSelect();
      }
      return flag;
    }
    case 1:
      return canvas.fileView.findPrev(s);
    case 2: {
      flag = canvas.reductionChoiceView.findPrev(s);
      if (flag) {
	tokenSelect();
      }
      return flag;
    }
    case 3: {
      flag = canvas.ruleView.findPrev(s);
      if (flag) {
	ruleSelect();
      }
      return flag;
    }
  }
  return flag;
}


void FileTraceWindow::doStep() {
  LOGSECTION("FileTraceWindow::doStep");
  ok_ptr(this);
  FtParser &parser = canvas.fileView.parser;
  LOGV(parser.processState);
  canvas.focusControl = canvas.activePanel = fileTab;
  if (parser.processState == FtParser::selectionRequired &&
      parser.location() == canvas.fileView.cursorLocation) {
    reductionChoiceEnter();
  }
  else {
    canvas.fileView.step();
  }
  setLocationField(parser.displayLocation());
  statusField.setText(FtParser::processStateText[parser.processState]);
  stepButton.unhighlight();
  stepButton.enableDefault();
  setFocus();
  LOGV(parser.processState);
}

void FileTraceWindow::parseFile() {
  LOGSECTION("FileTraceWindow::parseFile");
  ok_ptr(this);
  FtParser &parser = canvas.fileView.parser;
  LOGV(parser.processState);
  if (parser.processState == FtParser::selectionRequired) {
    reductionChoiceEnter();
  }
  if (parser.processState > FtParser::running) {
    messageBeep();
    canvas.focusControl = canvas.activePanel = fileTab;
    parseButton.disableDefault();
    parseButton.unhighlight();
    setFocus();
    return;
  }
  while (parser.processState <= FtParser::running) {
    canvas.fileView.parse();
    if (parser.processState != FtParser::selectionRequired) {
      continue;
    }
    int fileIndex = (char *) parser.state.pointer - parser.text.pointer();
    int tn = canvas.fileView.reductionTable[fileIndex];
    LOGV(fileIndex) LCV(tn);
    if (tn == 0) {
      continue;
    }
    parser.completeReduction(tn);
  }
  setLocationField(parser.displayLocation());
  statusField.setText(FtParser::processStateText[parser.processState]);
  LOGV(parser.processState);
  parseButton.unhighlight();
  parseButton.disableDefault();
}