/* * This file is part of the Haven & Hearth game client. * Copyright (C) 2009 Fredrik Tolf <fredrik@dolda2000.com>, and * Björn Johannessen <johannessen.bjorn@gmail.com> * * Redistribution and/or modification of this file is subject to the * terms of the GNU Lesser General Public License, version 3, as * published by the Free Software Foundation. * * 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. * * Other parts of this source tree adhere to other copying * rights. Please see the file `COPYING' in the root directory of the * source tree for details. * * A copy the GNU Lesser General Public License is distributed along * with the source tree of which this file is a part in the file * `doc/LPGL-3'. If it is missing for any reason, please see the Free * Software Foundation's website at <http://www.fsf.org/>, or write * to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA */ package haven; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.List; public class LineEdit { public String line = ""; public int point = 0; private static Text tcache = null; private static final int C = 1; private static final int M = 2; public KeyHandler mode; public abstract class KeyHandler { public abstract boolean key(char c, int code, int mod); } public class PCMode extends KeyHandler { public boolean key(char c, int code, int mod) { if((c == 8) && (mod == 0)) { if(point > 0) { line = line.substring(0, point - 1) + line.substring(point); point--; } } else if((c == 8) && (mod == C)) { int b = wordstart(point); line = line.substring(0, b) + line.substring(point); point = b; } else if(c == 10) { done(line); } else if((c == 127) && (mod == 0)) { if(point < line.length()) line = line.substring(0, point) + line.substring(point + 1); } else if((c == 127) && (mod == C)) { int b = wordend(point); line = line.substring(0, point) + line.substring(b); } else if((c >= 32) && (mod == 0)) { line = line.substring(0, point) + c + line.substring(point); point++; } else if(((c == 'v')||(c == 'V')) && (mod == C)) { String str = Utils.getClipboard(); line = line.substring(0, point) + str + line.substring(point); point += str.length(); } else if((code == KeyEvent.VK_LEFT) && (mod == 0)) { if(point > 0) point--; } else if((code == KeyEvent.VK_LEFT) && (mod == C)) { point = wordstart(point); } else if((code == KeyEvent.VK_RIGHT) && (mod == 0)) { if(point < line.length()) point++; } else if((code == KeyEvent.VK_RIGHT) && (mod == C)) { point = wordend(point); } else if((code == KeyEvent.VK_HOME) && (mod == 0)) { point = 0; } else if((code == KeyEvent.VK_END) && (mod == 0)) { point = line.length(); } else { return(false); } return(true); } } public class EmacsMode extends KeyHandler { private int mark, yankpos, undopos; private String last = ""; private List<String> yanklist = new ArrayList<String>(); private List<UndoState> undolist = new ArrayList<UndoState>(); {undolist.add(new UndoState());} private class UndoState { private String line; private int point; private UndoState() { this.line = LineEdit.this.line; this.point = LineEdit.this.point; } } private void save() { if(!undolist.get(undolist.size() - 1).line.equals(line)) undolist.add(new UndoState()); } private void mode(String mode) { if((mode == "") || (last != mode)) save(); last = mode; } private void kill(String text) { yanklist.add(text); } public boolean key(char c, int code, int mod) { if(mark > line.length()) mark = line.length(); String last = this.last; if((c == 8) && (mod == 0)) { mode("erase"); if(point > 0) { line = line.substring(0, point - 1) + line.substring(point); point--; } } else if((c == 8) && ((mod == C) || (mod == M))) { mode("backward-kill-word"); save(); int b = wordstart(point); if(last == "backward-kill-word") yanklist.set(yanklist.size() - 1, line.substring(b, point) + yanklist.get(yanklist.size() - 1)); else kill(line.substring(b, point)); line = line.substring(0, b) + line.substring(point); point = b; } else if(c == 10) { done(line); } else if((c == 'd') && (mod == C)) { mode("erase"); if(point < line.length()) line = line.substring(0, point) + line.substring(point + 1); } else if((c == 'd') && (mod == M)) { mode("kill-word"); save(); int b = wordend(point); if(last == "kill-word") yanklist.set(yanklist.size() - 1, yanklist.get(yanklist.size() - 1) + line.substring(point, b)); else kill(line.substring(point, b)); line = line.substring(0, point) + line.substring(b); } else if((c == 'b') && (mod == C)) { mode("move"); if(point > 0) point--; } else if((c == 'b') && (mod == M)) { mode("move"); point = wordstart(point); } else if((c == 'f') && (mod == C)) { mode("move"); if(point < line.length()) point++; } else if((c == 'f') && (mod == M)) { mode("move"); point = wordend(point); } else if((c == 'a') && (mod == C)) { mode("move"); point = 0; } else if((c == 'e') && (mod == C)) { mode("move"); point = line.length(); } else if((c == 't') && (mod == C)) { mode("transpose"); if((line.length() >= 2) && (point > 0)) { if(point < line.length()) { line = line.substring(0, point - 1) + line.charAt(point) + line.charAt(point - 1) + line.substring(point + 1); point++; } else { line = line.substring(0, point - 2) + line.charAt(point - 1) + line.charAt(point - 2); } } } else if((c == 'k') && (mod == C)) { mode(""); kill(line.substring(point)); line = line.substring(0, point); } else if((c == 'w') && (mod == M)) { mode(""); if(mark < point) { kill(line.substring(mark, point)); } else { kill(line.substring(point, mark)); } } else if((c == 'w') && (mod == C)) { mode(""); if(mark < point) { kill(line.substring(mark, point)); line = line.substring(0, mark) + line.substring(point); } else { kill(line.substring(point, mark)); line = line.substring(0, point) + line.substring(mark); } } else if((c == 'y') && (mod == C)) { mode("yank"); save(); yankpos = yanklist.size(); if(yankpos > 0) { String yank = yanklist.get(--yankpos); mark = point; line = line.substring(0, point) + yank + line.substring(point); point = mark + yank.length(); } } else if((c == 'y') && (mod == M)) { mode("yank"); save(); if((last == "yank") && (yankpos > 0)) { String yank = yanklist.get(--yankpos); line = line.substring(0, mark) + yank + line.substring(point); point = mark + yank.length(); } } else if((c == ' ') && (mod == C)) { mode(""); mark = point; } else if((c == '_') && (mod == C)) { mode("undo"); save(); if(last != "undo") undopos = undolist.size() - 1; if(undopos > 0) { UndoState s = undolist.get(--undopos); line = s.line; point = s.point; } } else if((c >= 32) && (mod == 0)) { mode("type"); line = line.substring(0, point) + c + line.substring(point); point++; } else { return(false); } return(true); } } public LineEdit() { String mode = Utils.getpref("editmode", "pc"); if(mode.equals("emacs")) { this.mode = new EmacsMode(); } else { this.mode = new PCMode(); } } public LineEdit(String line) { this(); this.line = line; this.point = line.length(); } public void setline(String line) { String prev = this.line; this.line = line; if(point > line.length()) point = line.length(); if(!prev.equals(line)) changed(); } public boolean key(char c, int code, int mod) { String prev = line; boolean ret = mode.key(c, code, mod); if(!prev.equals(line)) changed(); return(ret); } public boolean key(KeyEvent ev) { int mod = 0; if((ev.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0) mod |= C; if((ev.getModifiersEx() & (InputEvent.META_DOWN_MASK | InputEvent.ALT_DOWN_MASK)) != 0) mod |= M; if(ev.getID() == KeyEvent.KEY_TYPED) { char c = ev.getKeyChar(); if(((mod & C) != 0) && (c < 32)) { /* Undo Java's TTY Control-code mangling */ if(ev.getKeyCode() == KeyEvent.VK_BACK_SPACE) { } else if(ev.getKeyCode() == KeyEvent.VK_ENTER) { } else if(ev.getKeyCode() == KeyEvent.VK_TAB) { } else if(ev.getKeyCode() == KeyEvent.VK_ESCAPE) { } else { if((ev.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0) c = (char)(c + 'A' - 1); else c = (char)(c + 'a' - 1); } } return(key(c, ev.getKeyCode(), mod)); } else if(ev.getID() == KeyEvent.KEY_PRESSED) { if(ev.getKeyChar() == KeyEvent.CHAR_UNDEFINED) return(key('\0', ev.getKeyCode(), mod)); } return(false); } private static boolean wordchar(char c) { return(Character.isLetterOrDigit(c)); } private int wordstart(int from) { while((from > 0) && !wordchar(line.charAt(from - 1))) from--; while((from > 0) && wordchar(line.charAt(from - 1))) from--; return(from); } private int wordend(int from) { while((from < line.length()) && !wordchar(line.charAt(from))) from++; while((from < line.length()) && wordchar(line.charAt(from))) from++; return(from); } protected void done(String line) {} protected void changed() {} public Text render(Text.Foundry f) { if((tcache == null) || (tcache.text != line)) tcache = f.render(line); return(tcache); } static { Console.setscmd("editmode", new Console.Command() { public void run(Console cons, String[] args) { Utils.setpref("editmode", args[1]); } }); } }