/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.apps.editor; import java.awt.event.KeyEvent; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.List; import org.jnode.driver.console.ConsoleManager; import org.jnode.driver.console.TextConsole; import org.jnode.driver.console.textscreen.TextScreenConsoleManager; import org.jnode.driver.input.KeyboardEvent; import org.jnode.driver.input.KeyboardListener; import org.jnode.naming.InitialNaming; import org.jnode.shell.ShellManager; /** * @author Levente S\u00e1ntha */ public class TextEditor implements KeyboardListener { private int cx, cy, sh, sw, cxm, cym, fx, fy; private List<StringBuilder> ls; private TextConsole console; private File file; private boolean ro = false; public static void main(String[] argv) throws Exception { if (argv.length == 0) { System.out.println("No file specified"); return; } ShellManager sm = InitialNaming.lookup(ShellManager.NAME); TextScreenConsoleManager manager = (TextScreenConsoleManager) sm.getCurrentShell().getConsole().getManager(); TextConsole console = manager.createConsole( "editor", (ConsoleManager.CreateOptions.TEXT | ConsoleManager.CreateOptions.STACKED | ConsoleManager.CreateOptions.NO_LINE_EDITTING | ConsoleManager.CreateOptions.NO_SYSTEM_OUT_ERR)); try { manager.focus(console); TextEditor te = new TextEditor(console); File f = new File(argv[0]); te.ro = argv.length == 2 && "ro".equals(argv[1]); te.loadFile(f); } catch (Exception e) { manager.unregisterConsole(console); throw e; } } public TextEditor(TextConsole console) { this.console = console; console.addKeyboardListener(this); this.console.setCursorVisible(true); cx = 0; cy = 0; sh = console.getHeight(); sw = console.getWidth(); cxm = sw - 1; cym = sh - 1 - 1; fx = 0; fy = 0; ls = new ArrayList<StringBuilder>(); } public void loadFile(File f) throws IOException { ls.clear(); if (f.exists()) { BufferedReader br = new BufferedReader(new FileReader(f)); int c; StringBuilder sb = new StringBuilder(); while ((c = br.read()) != -1) { if (c == '\n') { sb.append((char) c); ls.add(sb); sb = new StringBuilder(); } else { sb.append((char) c); } } if (sb.length() > 0) ls.add(sb); } file = f; updateScreen(); } private String message; private void saveFile() { if (file == null) { System.out.println("No file."); return; } AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { try { StringBuilder buf = new StringBuilder(); FileWriter fw = new FileWriter(file); for (StringBuilder sb : ls) buf.append(sb); fw.write(buf.toString()); fw.flush(); fw.close(); message = "Saved " + file; } catch (Exception e) { e.printStackTrace(); } return null; } }); } private void updateScreen() { char[] data = new char[sw * (cym + 1 + 1)]; int k = 0; StringBuilder e = new StringBuilder(); List<StringBuilder> rl = new ArrayList<StringBuilder>(); for (int i = 0; i < cym + 1; i++) { StringBuilder l = (fy + i < ls.size()) ? ls.get(fy + i) : e; StringBuilder sb = new StringBuilder(); for (int j = 0; j < l.length(); j++) { char c = l.charAt(j); if (c == '\t') { sb.append(' '); sb.append(' '); sb.append(' '); sb.append(' '); } else if (c == '\n') { sb.append(' '); } else { sb.append(c); } } rl.add(sb); } int tab_count = 0; if (ls.size() > 0) { CharSequence l = ls.get(oy()).subSequence(0, fx + cx); for (int i = 0; i < l.length(); i++) { if (l.charAt(i) == '\t') tab_count++; } } int tx = fx + cx + 3 * tab_count; int cx2 = cx + 3 * tab_count; int fx2 = fx; if (tx >= sw) { cx2 = sw - 1; fx2 = tx - cx2; } for (int i = 0; i < cym + 1; i++) { StringBuilder l = rl.get(i); for (int j = 0; j < sw; j++) { char c = (fx2 + j < l.length()) ? l.charAt(fx2 + j) : ' '; data[k++] = c; } } if (message != null) { e.append(message); message = null; } else { e.append(ro ? "VIEW: " : "EDIT: ").append(file.getName()). append(" [").append(fx2 + cx2 + 1).append(",").append(oy() + 1).append("]"); } for (int j = 0; j < sw; j++) { data[k++] = (j < e.length()) ? e.charAt(j) : ' '; } console.setChar(0, 0, data, 7); console.setCursor(cx2, cy); } public void keyPressed(KeyboardEvent e) { int k = e.getKeyCode(); if (ro ^ e.isControlDown()) { switch (k) { case KeyEvent.VK_UP: { if (fy > 0) fy--; break; } case KeyEvent.VK_DOWN: { if (fy < my() - cym) fy++; break; } case KeyEvent.VK_LEFT: { if (fx > 0) fx--; break; } case KeyEvent.VK_RIGHT: { if (fx < mx()) fx++; break; } case KeyEvent.VK_HOME: { cx = fx = cy = fy = 0; break; } case KeyEvent.VK_END: { if (my() < cym) cy = my(); else { cy = cym; fy = my() - cy; } end(); break; } case KeyEvent.VK_Y: { if (ro) break; if (oy() >= 0 && oy() <= my()) { ls.remove(oy()); if (cy > 0 && oy() == my() + 1) cy--; else if (fy > 0) fy--; } break; } case KeyEvent.VK_S: case KeyEvent.VK_F2: { if (ro) break; saveFile(); break; } case KeyEvent.VK_Q: case KeyEvent.VK_F10: { console.getManager().unregisterConsole(console); break; } //same as without control key down case KeyEvent.VK_PAGE_UP: { if (fy > cym) fy -= cym; else fy = 0; break; } case KeyEvent.VK_PAGE_DOWN: { if (my() - fy > cym) fy += cym; break; } } } else { out: switch (k) { case KeyEvent.VK_UP: { cursorUp(); break; } case KeyEvent.VK_DOWN: { cursorDown(); break; } case KeyEvent.VK_LEFT: { if (cx > 0) cx--; else if (fx > 0) fx--; else if (cursorUp()) end(); break; } case KeyEvent.VK_RIGHT: { if (ox() < mx()) { if (cx < cxm) cx++; else fx++; } else if (cursorDown()) { cx = 0; fx = 0; } break; } case KeyEvent.VK_HOME: { cx = fx = 0; break; } case KeyEvent.VK_END: { end(); break; } //same as with control key down case KeyEvent.VK_PAGE_UP: { if (fy > cym) fy -= cym; else fy = 0; break; } case KeyEvent.VK_PAGE_DOWN: { if (my() - fy > cym) fy += cym; break; } default: if (ro) break; char c = e.getKeyChar(); if (c == KeyEvent.CHAR_UNDEFINED && k != KeyEvent.VK_DELETE) return; if (cy == ls.size()) ls.add(new StringBuilder()); StringBuilder l = ls.get(oy()); switch (k) { case KeyEvent.VK_ENTER: { c = '\n'; ls.add(oy() + 1, new StringBuilder()); break; } case KeyEvent.VK_DELETE: { if (cx == mx()) { if (cy < my()) { l.deleteCharAt(l.length() - 1).append(ls.remove(oy() + 1)); } } else if (cx < mx()) l.deleteCharAt(ox()); break out; } case KeyEvent.VK_BACK_SPACE: { if (ox() == 0) { if (cy > 0) { StringBuilder l2 = ls.get(oy() - 1); cx = l2.length() - 1; l2.deleteCharAt(l2.length() - 1).append(ls.remove(oy())); cy--; } else if (fy > 0) { StringBuilder l2 = ls.get(oy() - 1); cx = l2.length() - 1; l2.deleteCharAt(l2.length() - 1).append(ls.remove(oy())); fy--; } } else if (cx > 0) { cx--; l.deleteCharAt(ox()); } else { fx--; l.deleteCharAt(ox()); } break out; } } if (cx == l.length()) l.append(c); else l.insert(ox(), c); if (c == '\n') { if (cy < cym) cy++; else fy++; if (cx < l.length() + 1) { StringBuilder l2 = ls.get(oy()); l2.append(l, ox() + 1, l.length()); l.delete(ox() + 1, l.length()); } cx = fx = 0; } else { if (cx == cxm) fx++; else cx++; } } } if (oy() > cym && cy < 0) { cy = cym; fy = my() - cy; } if (oy() > my()) { fy -= oy() - my(); fy = fy < 0 ? 0 : fy; } if (ox() > mx()) { if (mx() > cxm) { cx = cxm; fx = mx() - cx; } else { cx = mx(); fx = 0; } } e.consume(); updateScreen(); } private void end() { if (mx() < cxm) cx = mx(); else { cx = cxm; fx = mx() - cx; } } private boolean cursorUp() { boolean ret; if (ret = (cy > 0)) cy--; else if (ret = (fy > 0)) fy--; return ret; } private boolean cursorDown() { boolean ret; if (ret = (oy() < my())) if (cy < cym) cy++; else fy++; return ret; } private int oy() { return fy + cy; } private int ox() { return fx + cx; } private int my() { return ls.size() - 1; } private int mx() { return ls.size() == 0 ? 0 : ls.get(oy()).length() - (oy() == my() ? 0 : 1); } public void keyReleased(KeyboardEvent event) { } }