All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.lessvoid.nifty.controls.ConsoleCommands Maven / Gradle / Ivy

There is a newer version: 1.4.3
Show newest version
package de.lessvoid.nifty.controls;

import de.lessvoid.nifty.Nifty;
import de.lessvoid.nifty.elements.Element;
import de.lessvoid.nifty.input.NiftyInputEvent;
import de.lessvoid.nifty.input.NiftyStandardInputEvent;
import de.lessvoid.nifty.screen.KeyInputHandler;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;

/**
 * This adds all the nifty command line features to the console control:
 * - command line completion (for all registered commands)
 * - command history
 * - command processing which detects commands and directly calls your registered commands (via the ConsoleCommand
 * interface)
 *
 * @author void
 */
public class ConsoleCommands implements KeyInputHandler {
  private boolean commandCompletion = false;

  @Nonnull
  private final ConsoleCommandSplitter splitter = new ConsoleCommandSplitter();

  @Nonnull
  private final Map commands = new TreeMap();
  @Nonnull
  private final List commandHistory = new ArrayList();
  private int commandHistoryLastCommand = -1;

  @Nonnull
  private final Nifty nifty;
  @Nonnull
  private final Console console;
  @Nullable
  private final TextField textfield;

  /**
   * You can implement this interface for individual commands and Nifty will call them
   * when the registered command has been detected.
   *
   * @author void
   */
  public interface ConsoleCommand {
    /**
     * Execute the command. You'll get an array of all parameters. This works the
     * same as with java main, which means the first entry in the array will be the
     * command and all other array entries are the actual parameters.
     *
     * @param args command and arguments
     */
    void execute(@Nonnull String... args);
  }

  /**
   * Create and attach this to the given console.
   */
  public ConsoleCommands(@Nonnull final Nifty nifty, @Nonnull final Console console) {
    this.nifty = nifty;
    this.console = console;
    textfield = console.getTextField();
    if (textfield != null) {
      Element textFieldElement = textfield.getElement();
      if (textFieldElement != null) {
        textFieldElement.addPreInputHandler(this);
      }
    }
  }

  /**
   * When the command completion is enabled pressing TAB will check for all known
   * commands. Use registerCommand() to register commands for this feature to work.
   * Please note that in this case you can't TAB away from the textfield.
   *
   * @param enabled true when command completion should be enabled and false if not
   */
  public void enableCommandCompletion(final boolean enabled) {
    commandCompletion = enabled;
  }

  /**
   * Register a command for the command completion feature.
   *
   * @param command the command to make known
   */
  public void registerCommand(@Nonnull final String commandText, @Nonnull final ConsoleCommand command) {
    commands.put(commandText, command);
  }

  /**
   * Get all commands that are registered.
   *
   * @return list of all commands that have been registered
   */
  @Nonnull
  public List getRegisteredCommands() {
    Set var = commands.keySet();
    return Collections.unmodifiableList(Arrays.asList(var.toArray(new String[var.size()])));
  }

  @Override
  public boolean keyEvent(@Nonnull final NiftyInputEvent inputEvent) {
    if (!commandCompletion || textfield == null) {
      return false;
    }
    if (NiftyStandardInputEvent.NextInputElement.equals(inputEvent)) {
      List matches = findMatches(textfield.getRealText());
      if (matches.size() == 1) {
        changeText(matches.get(0));
        return true;
      } else if (matches.size() > 1) {
        String shortest = findShortestMatch(matches);
        if (shortest.length() == textfield.getRealText().length()) {
          StringBuilder buffer = new StringBuilder("\n");
          for (String match : matches) {
            buffer.append("\\#cccf#").append(match).append("\n");
          }
          console.output(buffer.toString());
        }
        changeText(shortest);
      }
      return true;
    } else if (NiftyStandardInputEvent.MoveCursorUp.equals(inputEvent)) {
      if (commandHistoryLastCommand > 0) {
        commandHistoryLastCommand--;
        changeText(commandHistory.get(commandHistoryLastCommand));
      }
      return true;
    } else if (NiftyStandardInputEvent.MoveCursorDown.equals(inputEvent)) {
      if (commandHistoryLastCommand < commandHistory.size() - 1) {
        commandHistoryLastCommand++;
        changeText(commandHistory.get(commandHistoryLastCommand));
      } else {
        commandHistoryLastCommand = commandHistory.size();
        changeText("");
      }
      return true;
    } else if (NiftyStandardInputEvent.SubmitText.equals(inputEvent)) {
      String text = textfield.getRealText();
      console.output(text);
      textfield.setText("");

      // find command
      String[] split = splitter.split(text);
      if (split.length != 0) {
        String command = split[0];

        // is there a command that starts with this "command" string?
        for (Map.Entry registeredCommand : commands.entrySet()) {
          String[] s = registeredCommand.getKey().split(" ");
          if (s.length != 0) {
            String start = s[0];
            if (command.equals(start)) {
              ConsoleCommand consoleCommand = registeredCommand.getValue();
              consoleCommand.execute(split);
              addCommandToHistory(text);
              return true;
            }
          }
        }
      }

      console.outputError("Unknown command: " + text);

      // this means we have not found an appropriate command in the registered commands
      // we'll publish this now as the original console would do. this way you can still
      // subscribe for it (if you need to).
      String id = console.getId();
      if (id != null) {
        nifty.publishEvent(id, new ConsoleExecuteCommandEvent(console, text));
      }
      addCommandToHistory(text);
      return true;
    }

    return false;
  }

  /**
   * Find a ConsoleCommand with the given commandText. This will find commands that
   * begin with the given commandText as well.
   *
   * @param commandText the command to return
   * @return the ConsoleCommand or null if command does not exist
   */
  @Nullable
  public ConsoleCommand findCommand(@Nonnull final String commandText) {
    ConsoleCommand command = commands.get(commandText);
    if (command != null) {
      return command;
    }
    List commandMatches = findMatches(commandText);
    if (commandMatches.size() == 1) {
      return commands.get(commandMatches.get(0));
    }
    return null;
  }

  private void changeText(@Nonnull final String newText) {
    if (textfield != null) {
      textfield.setText(newText);
      textfield.setCursorPosition(textfield.getRealText().length());
    }
  }

  private void addCommandToHistory(@Nonnull final String text) {
    commandHistory.add(text);
    commandHistoryLastCommand = commandHistory.size();
  }

  @Nonnull
  List findMatches(@Nullable final String text) {
    if (text == null || text.length() == 0) {
      return Collections.emptyList();
    }
    List result = new ArrayList();
    for (String command : commands.keySet()) {
      if (command.equals(text)) {
        result.add(command);
      } else if (command.startsWith(text)) {
        result.add(command);
      }
    }
    return result;
  }

  /**
   * We know that all Strings in the given List start with the same string
   * that is at least one char long. This method will find the longest
   * substring that exists in all of the Strings in the List.
   */
  @Nonnull
  String findShortestMatch(@Nonnull final List matches) {
    // step 1: first longest string
    String longest = "";
    for (String match : matches) {
      if (match.length() > longest.length()) {
        longest = match;
      }
    }

    // step 2: scan the longest string char by char and check if it is
    // contained in all string of the list
    String lastCheck = longest.substring(0, 1);

    for (int i = 1; i < longest.length(); i++) {
      String check = longest.substring(0, i);
      for (String match : matches) {
        if (!match.startsWith(check)) {
          return lastCheck;
        }
      }
      // woo hoo they all match, we can update lastCheck
      lastCheck = check;
    }

    return lastCheck;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy