Package ketUI.responder

Source Code of ketUI.responder.LetterResponder

/*
* Copyright (C) 2011  Alasdair C. Hamilton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package ketUI.responder;

import java.util.*;

import geom.Position;
import ket.*;
import ket.math.*;
import ket.math.convert.Like;
import ket.math.purpose.Word;
import ketUI.*;
import ketUI.chord.*;
import ketUI.modes.*;

/**
* This handles user interaction while awaiting the input of a single character.
*/
public class LetterResponder extends Responder { // Why not CharResponder?

  final Modes modes;
  final MathCollection mathCollection;

 
  public LetterResponder(Modes modes) {
    this.modes = modes;
    this.mathCollection = modes.getMathCollection();
  }

  private Argument getCurrent() {
    return getSelection().getCurrent();
  }

  private Selection getSelection() {
    // TODO: Store and call mathCollection directly?
    return modes.getSelection();
  }

  private void setCurrent(Argument argument) {
    modes.getSelection().setCurrent(argument);
  }
 
  private Message getMessage() {
    return modes.getMessage();
 

  @Override
  public void prepareToRespond() {
    // Do nothing.
  }

  @Override 
  public void cleanAfterRespond() {
    // Do nothing.
  }

  private boolean batchUpdate(Chord chord) { // done?
    char character = chord.getKeyChar(-1);
    String letter = "" + character;
    KeyPress keyPress = chord.getLastKeyPress();
    System.out.println(" --- update letters ("+letter+") --- ");
    System.out.println("\t" + letter);
    chord.setComplete(false);
    boolean searchBackwards = false;
    Function function = getKnownArguments().getFunctionByChar(character);
    System.out.println("function = " + function);
    if ((keyPress.ctrlPressed() && character=='h') || "<BackSpace>".equals(keyPress.getCode())) {
      getSelection().cleanlyRemove(false);
      searchBackwards = true;
    } else if ((keyPress.ctrlPressed() && character=='d') || "<Delete>".equals(keyPress.getCode())) { // Ctrl-d or <Del>
      getSelection().cleanlyRemove(false);

    // If the function is defined and the selection is a token and not unknown, assume you want to add an intermediate parent.
    } else if ( function!=null && getCurrent().isToken() && ! Like.isUnknown(getCurrent()) ) {
      getSelection().addIntermediaryParent(function);
      getSelection().appendNewChild();
      return true;

    } else if ("<Enter>".equals(keyPress.getCode()) && (keyPress.shiftPressed() || keyPress.ctrlPressed())) { // <Ctrl-Enter>
      Equation blank = new Equation(new Token(Symbol.UNKNOWN));
      getSelection().appendEquation(blank);

    } else if (keyPress.ctrlPressed() && character=='s') { // Subsume, Ctrl-s
      getSelection().addNewIntermediaryParent();
      return true; // *Again*
    } else if (keyPress.ctrlPressed() && (character=='F' || character=='o')) {
      getSelection().appendNewSibling();
      return true; // *Again*
    } else if (keyPress.ctrlPressed() && character=='B') {
      getSelection().prependNewSibling();
      //- searchBackwards = true;
      return true; // *Again*

    } else if ("<Space>".equals(keyPress.getCode())) {
      getSelection().getCursor().selectParent(); //ish
      return true; // *Again*

    } else if (keyPress.ctrlPressed() && character=='a') {
      Branch current = getSelection().addNewIntermediaryParent();
      getSelection().appendNewChild();
      getSelection().setCurrent(current);
      return true; // *Again*
    } else if (keyPress.ctrlPressed() && character=='e') {
      Branch current = getSelection().addNewIntermediaryParent();
      getSelection().prependNewChild();
      getSelection().setCurrent(current);
      return true; // *Again*

    } else if (keyPress.ctrlPressed() && (character=='u' || character=='/')) {
      mathCollection.undo();
      return true;

    } else if (keyPress.ctrlPressed() && character=='f') { // Select next
      //< System.out.println("[ctrl-f: forwards]");
      //< // Do nothing.
      //- getSelection().getCursor().selectLeft();
      getSelection().getCursor().selectInOrOutRight();
      return true;
    } else if (keyPress.ctrlPressed() && character=='b') { // Select backwards
      //< System.out.println("[ctrl-b: backwards]");
      //- getSelection().getCursor().selectRight();
      getSelection().getCursor().selectInOrOutLeft();
      return true;
      //- searchBackwards = true;
    } else if ("<Enter>".equals(keyPress.getCode())) { // Duplicate with Ctrl-j Ctrl-m.
      // replace with function
      // keep selected
      // next time around check for a function and accept numbers not as replacements but as #args
      //   parse selected functions differently.
    } else if (character==':' || character=='&') { // &sin
      modes.setDocumentState(DocumentState.WRITE_FUNCTION);
      return true; // *Again*
    } else if (character=='\\') { // \alpha
      // NOTE: Minor bug: Can't insert a null function, but the syntax implies it.
      System.out.println("[Switch to string input]");
      modes.setDocumentState(DocumentState.WRITE_SYMBOL); // TODO: Rename to clarify it applies only to symbols.
      return true; // *Again*
    } else if (Character.isDigit(character) ) { // 012...
      if (getCurrent().isBranch()) { // abc...012...
        //- getSelection().replace(new Token(new Word(letter)));
        if (character=='0') { // ?() -> ?.
          getSelection().replace(new Token(Symbol.UNKNOWN));
        } else { // count->argument number, e.g. 3: ?() -> ?(?,?,?)
          for (char c='0'; c<character; c++) {
            getCurrent().asBranch().append(new Token(Symbol.UNKNOWN));
          }
        }
        return true; // *Again*
      } else {
        // TODO: 'f' -> f() not f.
        getSelection().replace(new Token(character - '0'));
      }
    } else if (getCurrent().isToken() && character=='(') { // ?() -> *()
      Branch conversion = new Branch(Function.UNKNOWN); // TODO: Reuse stuff from current.state?
      getSelection().replace(conversion);
      //< conversion.append(new Token(Symbol.UNKNOWN));
      return true; // *Again*
    } else if (getCurrent().getParentBranch()!=null && character==')') { // ?(?) -> ?(?,?)
      getSelection().appendNewSibling();
      return true; // *Again*
    } else if (getCurrent().isBranch() && function!=null) { // ?() -> *()
      System.out.println("[set function " + getCurrent());
      getCurrent().asBranch().setFunction(function);
    } else if (Character.isLetter(character)) {
      if (getCurrent().isBranch()) { // abc...
        Function f = getKnownArguments().recordUniqueFunction(letter);
        getCurrent().asBranch().setFunction(f);
      } else {
        getSelection().replace(new Token(new Word(letter)));
      }
    // other cases
    } else {
      System.out.println("[error]");
    }
    //+ Reverse direction of search/skip/all/done ....
    boolean found = getNormalMode().selectNextUnknown(searchBackwards);
    if (found) {
      System.out.println("[Found]");
      return true; // *Again*
    } else {
      System.out.println("[Done]");
      //< chord.setComplete(true);
      //< return false;
      return true;
    }
  }

  @Override
  public void respondToChordEvent(Chord chord, Macros macros, KeyboardEventHandler keyboardEventHandler) {
    char character = chord.getKeyChar(-1);
    String letter = "" + character;
    chord.setComplete(true);
    KnownArguments knownArguments = keyboardEventHandler.getDocument().getModes().getKnownArguments();
    switch (modes.getDocumentState()) {
      case WRITE_MODE:
        boolean stay = batchUpdate(chord);
        mathCollection.updateUndoStack();
        if (stay) {
          return;
        } else {
          break;
        }

      case DELETE:
        recognizeDeleteChord(chord, character);
        break;

      case QUICK_RECURSIVE_DELETE:
        System.out.println(" --- quick recursive delete --- ");
        System.out.println("chord = " + character);
        Argument original = getCurrent();
        Argument result = modes.getFind().find(character, Find.FORWARDS, knownArguments); // TODO: Simpliy.
        Vector<Argument> matches = new Vector<Argument>();
        while (result!=null) {
          //- result.remove();
          matches.add(result);
          System.out.println("removing: " + result);
          result = modes.getFind().find(character, Find.FORWARDS, knownArguments);
        }
        Collections.reverse(matches);
        for (Argument m : matches) {
          getSelection().setCurrent(m);
          getSelection().cleanlyRemove(false);
          //? getSelection().remove();
        }
        if ( ! original.isIn(matches) ) {
          setCurrent(original);
        }
        System.out.println("[done]");
        break;

      case PASTE_UNITARY:
        // TODO: letter -> operation.
        Clipboard clipboard = modes.getClipboard();
        Argument clipboardArgument = clipboard.getArgument();
        Function unitary = getKnownArguments().getFunctionByChar(character);
        getSelection().cleanlyInsert(clipboardArgument, unitary);
        break;

      case PASTE_MAP:
        getPasteMode().appendMapFromClipboard();
        break;

      case PASTE:
        recognizePasteChord(chord, character);
        break;

      case RECORD_MACRO:
        macros.startRecordingMacro(letter);
        break;

      case FIND_FORWARDS:
        chord.setLoopSkipped(true);
        for (int i=0; i<chord.getCounts(); i++) {
          modes.getFind().find(character, Find.FORWARDS, knownArguments);
        }
        break;

      case FIND_BACKWARDS:
        chord.setLoopSkipped(true);
        for (int i=0; i<chord.getCounts(); i++) {
          modes.getFind().find(character, Find.BACKWARDS, knownArguments);
        }
        break;

      case PLAY_MACRO:
        int count = 1;
        if (chord.countChanged()) {
          count = chord.getCounts();
          chord.setLoopSkipped(true);
        }
        for (int i=0; i<count; i++) {
          macros.execute(keyboardEventHandler, letter);
        }
        break;

      case SET_MARK:
        getEquationList().mark(letter, getCurrent());
        break;

      case SET_REGISTER:
        modes.getClipboard().setRegister(letter);
        break;

      case MOVE_TO_MARK:
        Argument mark = getEquationList().getMark(letter);
        if (mark!=null) {
          setCurrent(mark);
        } // Otherwise, give up.
        break;
    }
    modes.setDocumentState(DocumentState.NORMAL);
  }

  private EquationList getEquationList() {
    return getCurrent().getEquationList();
  }

  // TODO: The delete methods that are called here should be mode
  // agnostic, ie don't add letter-mode, but don't use normal mode
  // either.
  private boolean recognizeDeleteChord(Chord chord, char character) {
    chord.setComplete(true);
    if (Character.isDigit(character) && character!='0') { // Delete e.g. the 3rd argument, 'd3'.
      Branch currentBranch = getCurrent().asBranch();
      if (currentBranch==null) return false;
      int index = (int)character - (int)'0' - 1;
      int size = currentBranch.size();
      if (index>=size) {
        //-? index = size-1;
        return false;
      }
      Argument child = currentBranch.getChild(index);
      Ket.out.println("child = " + child);
      switch(size) {
        case 0: // f() -> f()
          return false;
        case 1: // f(a) -> f
          child.remove();
          return true;
        case 2: // f(a,b) -> b
          getSelection().replace(child.getOnlySibling());
          return true;
        default: // f(a,b,c) -> f(b,c)
          child.remove();
          return true;
      }
    }
    KeyPress keyPress = chord.getLastKeyPress();
    String code = keyPress.getCode();
    switch (code) {
      case "<Left>": // d<Left> Delete left sibling.
        return getDeleteMode().deleteLeft();
      case "<Right>": // d<Right> Delete right sibling.
        return getDeleteMode().deleteRight();

      case "<Up>": // d<Up> Delete this and the next line.
        getDeleteMode().deleteUp();
        return true;
      case "<Down>": // d<Up> Delete this and the next line.
        getDeleteMode().deleteDown();
        return true;
    }
    switch(character) {
      case 's': // 'ds' Delete every match of the current selection within the current equation.
        Vector<Argument> args = new ArgumentVector(getCurrent().getVisibleRoot(), ArgumentVector.INCLUDE_ROOT);
        //- Collections.reverse(args);
        EquationList el = getEquationList();
        Argument current = getCurrent();
        Equation e = current.getEquation();
        Argument match = getCurrent().cloneArgument();
        for (Argument a : args) {
          if (a.getEquationList()!=el) continue;
          if (!match.equals(a)) continue;
          getSelection().setCurrent(a);
          getSelection().cleanlyRemove(false);
        }
        if (getSelection().isIn(current)) {
          getSelection().setCurrent(current);
        }
        return true;
        // /////////////////////////
      case 'i': // 'di' Delete leftmost child argument.
        return getDeleteMode().deleteInLeft();
      case 'o': // 'do' Delete rightmost child argument.
        return getDeleteMode().deleteInRight();
      case 'h': // 'dh' Delete left sibling.
        return getDeleteMode().deleteLeft();
      case 'l': // 'dl' Delete right sibling.
        return getDeleteMode().deleteRight();
      case ' ': // 'd ' Delete the parent function.
        return getDeleteMode().deleteOut();
      case 'k': // 'dk' Delete this and the next line.
        getDeleteMode().deleteUp();
        return true;
      case 'j': // 'dj' Delete this and the next line.
        getDeleteMode().deleteDown();
        return true;
        // /////////////////////////

      case 't': // 'dt' Remove any trace of the current argument, storing it and its parent in clipboard (without the current siblings).
        getSelection().deleteTrace();
        return true;

      case 'd': // 'dd' Delete the current equation.
      case 'e': // 'de' Delete the current equation.
        return getDeleteMode().deleteCurrentEquation();

      case 'p': // 'dp' Delete the current branch's parent while retaining the current argument.
        return getDeleteMode().deleteParent();

      case '0': // 'd0' Delete the current branch (argument and all its children).
      case '.': // 'd.' Delete the current branch (argument and all its children).
      case 'x': // 'dx' Delete the current branch (argument and all its children).
      case 'b': // 'db' Delete the current branch (argument and all its children).
        getDeleteMode().remove();
        return true;
      case 'c': // 'dc' Delete the current intermediary argument, but retain any children.
        getDeleteMode().deleteIntermediate();
        return true;
      case 'm': // 'dm' Remove-map, i.e. delete all intermediate functions of children.
        getDeleteMode().mapDeleteIntermediate();
        return false;
      case 'H':
        {
        Argument next = getCurrent().getNextSibling();
        if (next==null) return false;
        setCurrent(next);
        getSelection().deleteTrace();
        return true;
        }
      case 'L':
        {
        Argument prev = getCurrent().getPreviousSibling();
        if (prev==null) return false;
        setCurrent(prev);
        getSelection().deleteTrace();
        return true;
        }
        /*-
      default:
        */
    }
    Function function = getKnownArguments().getFunctionByChar(character);
    if (function==null) {
      Ket.out.println(" !!! Unknown command: 'd"+character+"' !!! ");
      return false;
    }
    int settings = ArgumentVector.BRANCHES_ONLY|ArgumentVector.INCLUDE_ROOT|ArgumentVector.REVERSE_ITERATOR_ORDER;
    ArgumentVector argumentVector = new ArgumentVector(getCurrent(), settings);
    for (Branch branch : argumentVector.toBranchVector()) {
      if (branch.getFunction()==function) {
        getSelection().setCurrent(branch);
        getSelection().cleanlyRemove(false);
      }
    }
    return true;
  }

  private boolean recognizePasteChord(Chord chord, char character) {
    if (Character.isDigit(character) && character!='0') { // Paste e.g. the 3rd argument, 'p3'.
      Branch currentBranch = getCurrent().asBranch();
      int index = -1;
      if (currentBranch!=null) {
        index = (int)character - (int)'0' - 2;
        int size = currentBranch.size();
        if (index>=size) {
          index = size-1;
        }
      }
      getPasteMode().addChildAfterFromClipboard(index);
    }
    KeyPress keyPress = chord.getLastKeyPress();
    String code = keyPress.getCode();
    switch (code) {
      case "<Left>": // p<Left> Paste to the left of the current argument, i.e. as the previous (left) sibling.
        return getPasteMode().prependSiblingFromClipboard();
      case "<Right>": // p<Right> Paste to the right of the current argument, i.e. as the next (right) sibling.
        return getPasteMode().appendSiblingFromClipboard();
      case "<Up>": // p<Up> Paste to a new equation above the current equation.
        return getPasteMode().prependUpFromClipboard();
      case "<Down>": // p<Up> Paste to a new equation below the current equation.
        return getPasteMode().appendDownFromClipboard();
    }
    switch(character) {
      case '"': // 'p"' Substitute for the contents of clipboard in the current argument.
        return getPasteMode().appendSubstituteFromClipboard();
      case 't': // 'pt' Paste as text.
        getPasteMode().pasteTextFromClipboard();
        return true;
      case 'p': // 'pp' Insert a new parent from clipboard.
      case ' ': // 'p<Space>' Paste 'up', i.e. insert current into the pasted sub-branch.
        //- return getPasteMode().pasteUp();
        return getPasteMode().appendParentFromClipboard();
      case '.': // 'p.' Replace the entire current branch (i.e. the current sub-tree).
      case '0': // 'p0' Replace the entire current branch (i.e. the current sub-tree).
      case 'b': // 'pb' Replace the entire current branch (i.e. the current sub-tree).
      case 'r': // 'pr' Replace the entire current branch (i.e. the current sub-tree).
        return getPasteMode().appendBranchFromClipboard();
      case 'l': // 'pl' Paste to the right of the current argument, i.e. as the next (right) sibling.
        return getPasteMode().appendSiblingFromClipboard();
      case 'm': // 'pm' Paste map, i.e. apply the clipoboard argument as an intermediate function to all of selection's children.
        Ket.out.println(" --- paste map --- ");
        return getPasteMode().appendMapFromClipboard();
      case 'h': // 'ph' Paste to the left of the current argument, i.e. as the previous (left) sibling.
        return getPasteMode().prependSiblingFromClipboard();
      case 'i': // 'pi' Prepend child from clipboard.
        return getPasteMode().prependChildFromClipboard();
      case 'o': // 'po' Append child from clipboard.
        return getPasteMode().appendChildFromClipboard();
      case '~': // 'p~' Insert the inverse of clipboard's function as a new parent.
        return getPasteMode().appendInverseParentFromClipboard();
      case 's': // 'ps' Replace the current symbol of the current function with that of the clipboard argument.
        return getPasteMode().appendPurposeFromClipboard();
      case 'c': // 'pc' While retaining the current argument's children, replace it with the root argument in clipboard.
        return getPasteMode().appendCurrentFromClipboard();
      case 'k': // 'pk' Paste to a new equation above the current equation.
        return getPasteMode().prependUpFromClipboard();
      case 'j': // 'pj' Paste to a new equation below the current equation.
        return getPasteMode().appendDownFromClipboard();
      case 'e': // 'pe' Paste to a new equation below the current equation.
        return getPasteMode().appendEquationFromClipboard();
    }
    /////////////////////////////////////////////////
    // ADD FUNCTION AND PASTE AS THE NEXT ARGUMENT //
    /////////////////////////////////////////////////
    Function function = getKnownArguments().getFunctionByChar(character);
    if (function!=null) {
      return getPasteMode().appendWithFunctionFromClipboard(function);
    } else {
      Ket.out.println(" !!! Unknown command: 'p"+character+"' !!! ");
      return false;
    }
  }
 
  @Override 
  public void respondToMouseClick(MouseButton mouseButton, boolean singleClick, Position p) {
    // Do nothing.
  }
 
  @Override
  public void respondToMouseDrag(MouseButton mouseButton, Position initial, Position release) {
    // Do nothing.
 

  private NormalMode getNormalMode() {
    return modes.getNormalMode();
  }

  private PasteMode getPasteMode() {
    return modes.getPasteMode();
  }

  private AddMode getAddMode() {
    return modes.getAddMode();
  }
 
  private DeleteMode getDeleteMode() {
    return modes.getDeleteMode();
  }

  private KnownArguments getKnownArguments() {
    return mathCollection.getKnownArguments();
  }

  private Cursor getCursor() {
    return mathCollection.getCursor();
  }

}
TOP

Related Classes of ketUI.responder.LetterResponder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.