/* * $Id: CommandHistory.java 536 2008-02-19 06:03:27Z weiju $ * * Created on 2006/03/10 * Copyright 2005-2008 by Wei-ju Wu * This file is part of The Z-machine Preservation Project (ZMPP). * * ZMPP 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. * * ZMPP 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 ZMPP. If not, see <http://www.gnu.org/licenses/>. */ package org.zmpp.vm; import java.util.ArrayList; import java.util.List; import org.zmpp.encoding.ZsciiEncoding; import org.zmpp.encoding.ZsciiString; import org.zmpp.vmutil.RingBuffer; /** * This class implements a store for command lines. The history is a ring buffer * stored in an array. This is done to prevent that the history is too big, * resulting in a big inefficient thing just to maintain the list of entries. * * @author Wei-ju Wu * @version 1.0 */ public class CommandHistory { private static final int NUM_ENTRIES = 5; /** * The class HistoryEntry maintains input entries, there is both an original * line and a */ private static class HistoryEntry { public List<Character> original; public List<Character> edited; public HistoryEntry() { original = new ArrayList<Character>(); edited = new ArrayList<Character>(); } public String toString() { char[] orig = new char[original.size()]; char[] edit = new char[edited.size()]; for (int i = 0; i < original.size(); i++) { orig[i] = original.get(i); } for (int i = 0; i < edited.size(); i++) { edit[i] = edited.get(i); } final StringBuilder buffer = new StringBuilder(); buffer.append(" (" + new ZsciiString(orig)); buffer.append(", "); buffer.append(new ZsciiString(edit) + " )"); return buffer.toString(); } } private RingBuffer<HistoryEntry> history = new RingBuffer<HistoryEntry>(NUM_ENTRIES); private int historyIndex; private int historySizeAtReset; private InputLine inputline; /** * Constructor. * * @param inputline the input line object */ public CommandHistory(InputLine inputline) { this.inputline = inputline; } /** * Returns the current history index. * * @return the history index */ public int getCurrentIndex() { return historyIndex; } /** * Returns true if history char, false otherwise. * * @param zsciiChar the character * @return true if history character */ public boolean isHistoryChar(final char zsciiChar) { return zsciiChar == ZsciiEncoding.CURSOR_UP || zsciiChar == ZsciiEncoding.CURSOR_DOWN; } /** * Resets the index of the history to the last entry. */ public void reset() { historySizeAtReset = historyIndex = history.size(); for (int i = 0; i < history.size(); i++) { final HistoryEntry entry = history.get(i); entry.edited.clear(); entry.edited.addAll(entry.original); } //System.out.println("reset(), History: " + history.toString()); } /** * Adds an input line to the history. * * @param inputbuffer the input buffer */ public void addInputLine(final List<Character> inputbuffer) { final HistoryEntry entry = new HistoryEntry(); entry.original.addAll(inputbuffer); entry.edited.addAll(inputbuffer); if (history.size() > historySizeAtReset) { // If the history was invoked, the last edit line is also included // in the input, in this case, replace it with the final input line history.set(history.size() - 1, entry); } else { // If the history was not invoked, simply add the input to the end of // the history list history.add(entry); } //System.out.println("addInputLine(), History: " + history.toString()); } /** * Deletes the current line and replaces it with a history entry, which is * determined depending on the input character. * * @param inputbuffer the input buffer * @param textbuffer the text buffer address * @param pointer the pointer in the text buffer * @param zsciiChar the character * @return the new pointer in the text buffer */ public int switchHistoryEntry(final List<Character> inputbuffer, final int textbuffer, final int pointer, final char zsciiChar) { if (zsciiChar == ZsciiEncoding.CURSOR_UP) { return processHistoryUp(inputbuffer, textbuffer, pointer); } else { return processHistoryDown(inputbuffer, textbuffer, pointer); } } private int processHistoryUp(final List<Character> inputbuffer, final int textbuffer, final int pointer) { int newpointer = pointer; if (historyIndex > 0) { storeCurrentInput(inputbuffer); //System.out.println("historyUp(), History: " + history.toString()); historyIndex--; newpointer = fillInputLineFromHistory(inputbuffer, textbuffer, pointer); } return newpointer; } private int processHistoryDown(final List<Character> inputbuffer, final int textbuffer, final int pointer) { int newpointer = pointer; if (historyIndex < history.size() - 1) { storeCurrentInput(inputbuffer); //System.out.println("historyDown(), History: " + history.toString()); historyIndex++; newpointer = fillInputLineFromHistory(inputbuffer, textbuffer, pointer); } return newpointer; } private int fillInputLineFromHistory(final List<Character> inputbuffer, final int textbuffer, final int pointer) { int newpointer = deleteInputLine(inputbuffer, pointer); if (history.size() > historyIndex) { final List<Character> input = history.get(historyIndex).edited; for (int i = 0; i < input.size(); i++) { newpointer = inputline.addChar(inputbuffer, textbuffer, newpointer, input.get(i)); } } return newpointer; } /** * Replaces the current history entry with the content of the input buffer. * * @param inputbuffer the input buffer */ private void storeCurrentInput(final List<Character> inputbuffer) { if (historyIndex < history.size()) { // Replace the edited thang history.get(historyIndex).edited.clear(); history.get(historyIndex).edited.addAll(inputbuffer); } else { final HistoryEntry entry = new HistoryEntry(); entry.original.addAll(inputbuffer); entry.edited.addAll(inputbuffer); history.add(entry); } } /** * Removes the text from the current input line. * * @param inputbuffer the input buffer * @param pointer the pointer in the text buffer * @return the new pointer in the text buffer */ private int deleteInputLine(final List<Character> inputbuffer, final int pointer) { final int n = inputbuffer.size(); int newpointer = pointer; for (int i = 0; i < n; i++) { newpointer = inputline.deletePreviousChar(inputbuffer, newpointer); } return newpointer; } }