/* CLI Command Workspace */!!

inherit(EditWindow, #CliWorkspace, #(historyCommands /* previous commands */
MaxHistoryCommands /* max # cmds saved */
historyPosition /* position in history list */
executingCommand /* true if executing now */), 2, nil)!!

now(class(CliWorkspace))!!

now(CliWorkspace)!!

/* make window accessible to character input */
Def activateWindow(self)
{
  showCaret(self);
  showOldCurs();
}!!

/* Add a new entry in the command history. */
Def addHistoryCommand(self, command)
{
  /* exit if maxHistoryCommands nil or 0 */
  if not(MaxHistoryCommands) cor (MaxHistoryCommands = 0)
    ^nil;
  endif;
  
  historyPosition := getHistorySize(self);
  if (getHistorySize(self) > MaxHistoryCommands)
  then
    removeFirst(historyCommands);
  endif;
  add(historyCommands, command);
  /* when new command added, reset position to most recently added */
  historyPosition := getHistorySize(self);
}!!

/* Handle control-up arrow and control-down arrow as special cases. */
Def arrows(self, wP | xTmp, controlKey)
{
  controlKey := Call GetKeyState(0x11);
  select
    /* check for control-down arrow */
    case (wP == 40) and (controlKey < 0)
      is
        nextHistoryCommand(self);
      endCase
    /* check for control-up arrow */
    case (wP == 38) and (controlKey < 0)
      is
        previousHistoryCommand(self);
      endCase
    /* otherwise, do the regular arrows thing */
    default
      ^arrows(self:ancestor, wP);
  endSelect;
}!!

/* Process MS-Window's character input message. */
Def charIn(self, wP, lP | rect)
{
  /* process special characters */
  select
    case (asChar(wP) == CR) is
      /* let EditWindow do most of the work */
      if executingCommand(self)
        beep();
      else
        charIn(self:ancestor, wP, lP);
        executingCommandSet(self);
        addHistoryCommand(self, workText);
        processCommand(parent, workText);
      endif;
    endCase
    case (wP == VK_ESCAPE) is
      /* if executing a command... */
      if executingCommand(self)
        /* if inside server call, pass on escape; otherwise, abort */
        if serverCallFlagSet?(getCliServerObject(CliServer))
          pendingAbortSet(getCliServerObject(CliServer));  /* remember abort */
          charIn(self:ancestor, wP, lP);
        else
          chkAbort(getExecutionEngine(CLIExecEngine));
        endif;
      else
        beep();
        cls(self);
        initWorkText(self);
      endif;
    endCase
    default
      charIn(self:ancestor, wP, lP);
  endSelect;
}!!

/* intercept the tab key; let others through. */
Def command(self, wP, lP)
{ select
    case wP == EDIT_TAB is
      setFocus(hTrans(parent));
      ^0;
    endCase
  endSelect;
  ^command(self:ancestor, wP, lP)
}!!

/* make window inaccessible to character input */
Def deactivateWindow(self)
{
  hideCaret(self);
  cls(self);
  showWaitCurs();
}!!

Def editCut(self)
{
  if not(xCut(self))
  then
    delChar(self);
  endif;
}
!!

/* return executing command flag */
Def executingCommand(self)
{
  ^executingCommand;
}
!!

/* set flag to "not executing" */
Def executingCommandClear(self)
{
  executingCommand := nil;
  
  /* if not in focus, mark CLI as inactive */
  if CLIULibraryClass$Inst cand not(focusWindow(parent))
    clearCliActive(CLIULibraryClass$Inst);
  endif;
}
!!

/* set flag to "executing" */
Def executingCommandSet(self)
{
  executingCommand := 0;
  
  /* mark CLI as active */
  if CLIULibraryClass$Inst
    setCliActive(CLIULibraryClass$Inst);
  endif;
  
  ^0;  
}
!!

/* do initialization */
Def getHistoryCommand(self, index)
{
  if index >= 0 and index < getHistorySize(self)
  then
    ^historyCommands[index];
  else
    ^"bad";
  endif;
}
!!

/* return number of history commands */
Def getHistorySize(self)
{
  ^size(historyCommands);
}
!!

/* return limit on number of history commands */
Def getMaxHistorySize(self)
{
  ^MaxHistoryCommands;
}
!!

/* return height of characters */
Def getTextHeight(self)
{
  ^tmHeight;
}
!!

/* return width of characters */
Def getTextWidth(self)
{
  ^tmWidth;
}
!!

/* Notify parent of focus. */
Def gotFocus(self, hWndPrev)
{
  editGotFocus(parent);
  ^gotFocus(self:ancestor, hWndPrev);
}!!

/* do initialization */
Def init(self)
{
  /* most initialization done by ancestor */
  init(self:ancestor);
  initHistoryCommands(self);
  executingCommandClear(self);
}
!!

/* do initialization */
Def initHistoryCommands(self)
{
  MaxHistoryCommands := 50;
  historyCommands := new(OrderedCollection, 4);
}
!!

/* Replace EditWindow method.  Fool EditWindow's charInput method into
   only recognizing control-CR as a newline.  This prevents the CR from
   breaking a line if the cursor is in the middle of a command when the
   command is accepted */
Def isNewLineChar(self, aChar)
{ ^(aChar == asChar(10));
}!!

/* notify parent. */
Def losingFocus(self, hWndNew | temp)
{
  editLostFocus(parent);
  temp := losingFocus(self:ancestor, hWndNew);
  invalidate(self);
  ^temp;
}!!

/* Retrieve and display the next history command in the window */
Def nextHistoryCommand(self)
{
  /* set history position */
  if not(historyPosition)
  then
    ^0;
  endif;
  historyPosition := min(getHistorySize(self), historyPosition + 1);
  /* clear screen */
  cls(self);
  initWorkText(self);
  /* shove retrieved command in, otherwise leave blank */
  if historyPosition <= (getHistorySize(self) - 1)
  then
    workText := copy(historyCommands[historyPosition]);
  endif;
  invalidate(self);
  /* set x and y to end of restored command */
  yPos := size(workText) - 1;
  xPos := size(workText[yPos]);
  hideCaret(self);
  moveCaret(self);
  showCaret(self);
  promptVisible(parent);
}
!!

/* special additional processing to determine if top visible; hopefully
   preserves myth that prompt is part of display. */
Def paint(self, hdc)
{
  paint(self:ancestor, hdc);
  if (topLine == 0)
  then
    promptVisible(parent);
  else
    promptInvisible(parent);
  endif;
}!!

/* Retrieve and display the previous history command in the window */
Def previousHistoryCommand(self)
{
  /* set history position */
  if not(historyPosition)
    historyPosition := getHistorySize(self) - 1;
  else
    historyPosition := max(-1, (historyPosition - 1));
  endif;
  
  /* clear screen */
  cls(self);
  initWorkText(self);
  
  /* shove retrieved command in, otherwise leave blank */
  if historyPosition >= 0
    workText := copy(historyCommands[historyPosition]);
  endif;
  invalidate(self);
  
  /* set x and y to end of restored command */
  yPos := size(workText) - 1;
  xPos := size(workText[yPos]);
  hideCaret(self);
  moveCaret(self);
  showCaret(self);
  promptVisible(parent);
}
!!

/* set limit on number of history commands */
Def setMaxHistorySize(self, newSize)
{
  MaxHistoryCommands := newSize;
  /* trim saved history if size decreased */
  loop
  while getHistorySize(self) > MaxHistoryCommands
  begin 
    removeFirst(historyCommands);
  endLoop;
  historyPosition := nil;
}
!!

Def WM_HSCROLL(self, wP, lP)
{
  setFocus(self);
  ^WM_HSCROLL(self:ancestor, wP, lP);
}!!

/* 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;
}!!

Def WM_VSCROLL(self, wP, lP)
{
  setFocus(self);
  ^WM_VSCROLL(self:ancestor, wP, lP);
}!!
