Исходный файл TextEdit.java (все изменения выделены):
import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.Enumeration;
import L2List; import ListException;
/**
* Simple text editor.
*/
public class TextEdit extends Frame {
Text text; // Text storage
EditField editField; // Client area of frame window
int cursorX = 0; // Cursor position
int cursorY = 0;
int windowX = 0; // Window position in text
int windowY = 0;
int windowWidth = 80; // Window size in characters (dx, dy)
int windowHeight = 24;
int lastCommand = 0; // The last key pressed
Font textFont = new Font("Courier", Font.PLAIN, 14); // Text font
// Font metric (we use a fixed width font!)
int dx = 0; // Maximal character advance (width)
int ascent = 0; // Character ascent
int descent = 0; // Character descent
int leading = 0; // Interline skip
int dy = 0; // Font height = ascent + descent + leading
int margin = 4; // Top and left margin
Color fgColor = Color.black; // Foreground color
Color bgColor = Color.lightGray; // Background color
String fileName = "noname.txt";
boolean fileNameSet = false;
boolean textChanged = false;
String endOfText = "[* End of text *]"; // "End of text" line
boolean inputDisabled = false;
boolean saveText = false;
public static void main(String[] args) {
TextEdit editor = new TextEdit();
if (args.length >= 1) {
editor.fileName = args[0];
editor.fileNameSet = true;
editor.text.load(editor.fileName);
editor.setTitle(editor.fileName);
}
editor.setVisible(true);
editor.editField.requestFocus();
}
public TextEdit() {
text = new Text();
editField = new EditField();
editField.setFont(textFont);
setBackground(bgColor);
setForeground(fgColor);
add("Center", editField);
WindowAdapter wa = new WindowAdapter() {
public void windowClosing(WindowEvent e) {
onQuit();
}
public void windowActivated(WindowEvent e) {
editField.requestFocus();
}
};
addWindowListener(wa);
// Add menu bar
final MenuBar mb = new MenuBar();
setMenuBar(mb);
final Menu fileMenu = new Menu("File");
//
//---------------------------------------------------------------------
final MenuItem openItem = new MenuItem("Open...");
//---------------------------------------------------------------------
//
final MenuItem saveItem = new MenuItem("Save");
final MenuItem saveAsItem = new MenuItem("Save as...");
final MenuItem quitItem = new MenuItem("Quit");
//
//---------------------------------------------------------------------
fileMenu.add(openItem);
//---------------------------------------------------------------------
//
fileMenu.add(saveItem);
fileMenu.add(saveAsItem);
fileMenu.add(quitItem);
mb.add(fileMenu);
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
//
//----------------------------------------------------------------------
if (command.equals("Open...")) {
onOpen();
//----------------------------------------------------------------------
//
} else if (command.equals("Save")) {
onSave();
} else if (command.equals("Save as...")) {
onSaveAs();
} else if (command.equals("Quit")) {
onQuit();
}
editField.requestFocus();
}
};
fileMenu.addActionListener(al);
//... pack();
setSize(
600,
400
);
setTitle(fileName);
setBackground(bgColor);
setForeground(fgColor);
KeyAdapter ka = new KeyAdapter() {
public void keyPressed(KeyEvent e) {
onKeyPressed(e);
}
};
addKeyListener(ka);
editField.addKeyListener(ka);
// Processing of resize events
ComponentAdapter ca = new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
onResize();
editField.requestFocus();
}
public void componentShown(ComponentEvent e) {
editField.requestFocus();
}
};
editField.addComponentListener(ca);
}
void onKeyPressed(KeyEvent e) {
if (inputDisabled) return;
//... System.out.println("KeyPressed: " + e);
int keyCode = e.getKeyCode();
char keyChar = e.getKeyChar();
// Remove old cursor.
drawCursor(cursorX, cursorY, false);
if (e.isActionKey()) {
// Action key
//... System.out.println("Action key: " + e.getKeyText(e.getKeyCode()));
if (keyCode == KeyEvent.VK_DOWN) {
onDown();
} else if (keyCode == KeyEvent.VK_UP) {
onUp();
} else if (keyCode == KeyEvent.VK_LEFT) {
onLeft();
} else if (keyCode == KeyEvent.VK_RIGHT) {
onRight();
} else if (keyCode == KeyEvent.VK_HOME) {
onHome();
} else if (keyCode == KeyEvent.VK_END) {
onEnd();
}
} else if ((e.getModifiers() & InputEvent.CTRL_MASK) != 0) {
// Ctrl-key
//... System.out.println("Ctrl+" + e.getKeyText(e.getKeyCode()));
if (keyCode == KeyEvent.VK_K) { // Ctrl+K
onDeleteLine();
} else if (keyCode == KeyEvent.VK_L) { // Ctrl+K
onInsertLine();
} else if (keyCode == KeyEvent.VK_S) { // Ctrl+S
onSave();
} else if (keyCode == KeyEvent.VK_Q) { // Ctrl+Q
onQuit();
}
} else if (keyCode == KeyEvent.VK_ENTER) {
//... System.out.println(e.getKeyText(e.getKeyCode()));
onEnter();
} else if (keyCode == KeyEvent.VK_BACK_SPACE) {
//... System.out.println(e.getKeyText(e.getKeyCode()));
onBackSpace();
} else if (keyChar != KeyEvent.CHAR_UNDEFINED) {
onCharTyped(keyChar);
}
if (cursorX < 0) cursorX = 0;
if (cursorY < 0) cursorY = 0;
// Scroll a window, if necessary
if (
cursorX < windowX || cursorX > windowX + windowWidth - 1 ||
cursorY < windowY || cursorY > windowY + windowHeight - 1
) {
// Cursor is removed at the moment!
// scrollToCursor() will draw it again.
scrollToCursor();
}
drawCursor(cursorX, cursorY, true);
editField.requestFocus();
}
void scrollToCursor() {
// Remove cursor for a moment
drawCursor(cursorX, cursorY, false);
if (cursorX < windowX) {
scrollLeft(windowX - cursorX);
} else if (cursorX > windowX + windowWidth - 1) {
scrollRight(cursorX - (windowX + windowWidth - 1));
}
if (cursorY < windowY) {
scrollUp(windowY - cursorY);
} else if (cursorY > windowY + windowHeight - 1) {
scrollDown(cursorY - (windowY + windowHeight - 1));
}
// Restore cursor
drawCursor(cursorX, cursorY, true);
}
void scrollLeft(int n) {
inputDisabled = true;
if (n > windowX) n = windowX;
windowX -= n;
if (n > windowWidth / 2) editField.repaint();
else {
Graphics g = editField.getGraphics();
Rectangle r = editField.getBounds();
if (g != null) {
int shift = dx * n;
g.copyArea(
margin, margin,
windowWidth * dx - shift, (windowHeight + 1) * dy,
shift, 0
);
inputDisabled = true;
editField.repaint(
0, margin,
shift + margin, (windowHeight + 1) * dy
);
} else {
editField.repaint();
}
}
}
void scrollRight(int n) {
if (n == 0) return;
inputDisabled = true;
windowX += n;
if (n > windowWidth / 2) editField.repaint();
else {
Graphics g = editField.getGraphics();
if (g != null) {
int shift = dx * n;
g.copyArea(
margin + shift, margin,
windowWidth * dx - shift, windowHeight * dy,
-shift, 0
);
inputDisabled = true;
editField.repaint(
margin + windowWidth * dx - shift, 0,
shift, windowHeight * dy
);
} else {
editField.repaint();
}
}
}
void scrollUp(int n) {
if (n == 0) return;
inputDisabled = true;
if (n > windowY) n = windowY;
windowY -= n;
if (n > windowHeight / 2) editField.repaint();
else {
Graphics g = editField.getGraphics();
Rectangle r = editField.getBounds();
if (g != null) {
int shift = dy * n;
g.copyArea(
margin, margin,
windowWidth * dx, windowHeight * dy - shift,
0, shift
);
inputDisabled = true;
editField.repaint(
margin, margin,
margin + windowWidth * dx, shift
);
} else {
editField.repaint();
}
}
}
void scrollDown(int n) {
inputDisabled = true;
windowY += n;
if (n > windowHeight / 2) editField.repaint();
else {
Graphics g = editField.getGraphics();
if (g != null) {
int shift = dy * n;
g.copyArea(
margin, margin + shift,
windowWidth * dx, windowHeight * dy - shift,
0, -shift
);
inputDisabled = true;
editField.repaint(
margin, margin + windowHeight * dy - shift,
windowWidth * dx, shift
);
} else {
editField.repaint();
}
}
}
void drawCursor(Graphics g, int cx, int cy, boolean on) {
if (cx >= windowX + windowWidth || cy >= windowY + windowHeight)
return;
if (g != null) {
if (on) g.setColor(fgColor);
else g.setColor(bgColor);
int x = margin + (cx - windowX) * dx;
int y = margin + (cy - windowY) * dy;
g.fillRect(
x, y,
dx, ascent + descent
);
if (on) g.setColor(bgColor);
else g.setColor(fgColor);
if (cy <= text.size()) {
String line;
if (cy == text.size()) {
line = endOfText;
} else {
text.setPointer(cy);
line = (String) text.elementAfter();
}
if (cx < line.length()) {
g.drawString(
line.substring(cx, cx + 1),
x, y + ascent
);
}
}
g.setColor(fgColor);
}
}
void drawCursor(int cx, int cy, boolean on) {
drawCursor(editField.getGraphics(), cx, cy, on);
}
void onDown(){
if (cursorY < text.size())
cursorY++;
}
void onUp(){
if (cursorY > 0) cursorY--;
}
void onLeft(){
if (cursorX > 0) cursorX--;
}
void onRight(){
cursorX++;
}
void onHome(){
cursorX = 0;
}
void onEnd(){
if (cursorY < text.size()) {
String line = text.getLine(cursorY);
cursorX = line.length();
} else {
cursorX = 0;
}
}
void onDeleteLine(){
if (cursorY < text.size()) {
text.setPointer(cursorY);
text.removeAfter();
redraw(0, cursorY, Integer.MAX_VALUE, Integer.MAX_VALUE);
textChanged = true;
}
}
void onInsertLine(){
text.setPointer(cursorY);
text.addAfter("");
redraw(0, cursorY, Integer.MAX_VALUE, Integer.MAX_VALUE);
textChanged = true;
}
void onEnter(){
text.setPointer(cursorY);
if (cursorY >= text.size()) {
text.addAfter("");
} else {
String line = (String) text.elementAfter();
if (cursorX >= line.length()) {
text.moveForward();
text.addAfter("");
} else {
// Cut line into 2 pieces
text.setElementAfter(line.substring(0, cursorX));
text.moveForward();
text.addAfter(line.substring(cursorX));
}
}
cursorX = 0;
cursorY++;
scrollToCursor();
redraw(0, cursorY - 1, Integer.MAX_VALUE, Integer.MAX_VALUE);
textChanged = true;
}
void onBackSpace(){
if (cursorX <= 0 || cursorY >= text.size()) return;
text.setPointer(cursorY);
String line = (String) text.elementAfter();
if (cursorX <= line.length()) {
String l = line.substring(0, cursorX - 1) +
line.substring(cursorX);
text.setElementAfter(l);
}
cursorX--;
redraw(cursorX, cursorY, Integer.MAX_VALUE, 1);
textChanged = true;
}
void onDelete(){
if (cursorX < 0 || cursorY >= text.size()) return;
text.setPointer(cursorY);
String line = (String) text.elementAfter();
if (cursorX < line.length()) {
String l = line.substring(0, cursorX) +
line.substring(cursorX + 1);
text.setElementAfter(l);
}
redraw(cursorX, cursorY, Integer.MAX_VALUE, 1);
textChanged = true;
}
void onCharTyped(char c) {
//... System.out.println("Char typed: " + c + " code: " + (int)c);
if ((int)c == 0x7f) {
onDelete(); return;
}
if (cursorY == text.size()) onInsertLine();
text.setPointer(cursorY);
String line = (String) text.elementAfter();
String l;
if (cursorX < line.length()) {
l = line.substring(0, cursorX);
l += c;
l += line.substring(cursorX);
} else if (cursorX == line.length()) {
l = line;
l += c;
} else {
int n = cursorX - line.length();
l = line;
while (n > 0) {
l += ' '; n--;
}
l += c;
}
text.setElementAfter(l);
cursorX++;
scrollToCursor();
redraw(cursorX - 1, cursorY, Integer.MAX_VALUE, 1);
textChanged = true;
}
//
//-----------------------------------------------------------------
void onOpen() {
FileDialog fd = new FileDialog((Frame) this, "Open File", FileDialog.LOAD);
if (textChanged) {
SaveDialog sd = new SaveDialog(this);
sd.setVisible(true); // Modal dialog
if (saveText) onSave();
saveText = false;
}
fd.setVisible(true);
editField.requestFocus();
String file = fd.getFile();
String directory = fd.getDirectory();
if (file == null || directory == null) return;
if (directory.length() > 0) {
char lastChar = directory.charAt(directory.length() - 1);
if (lastChar != '/' && lastChar != '\\') directory += '/';
}
fileName = directory + file;
fileNameSet = true;
textChanged = false;
text.load(fileName);
setTitle(fileName);
cursorX = 0;
cursorY = 0;
redraw(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
}
//------------------------------------------------------------------
//
// Redraw a rectangle in a text
void redraw(int x, int y, int w, int h) {
int x1 = x + w;
if (w == Integer.MAX_VALUE) x1 = Integer.MAX_VALUE;
int y1 = y + h;
if (h == Integer.MAX_VALUE) y1 = Integer.MAX_VALUE;
int x0 = x;
if (x0 < windowX) x0 = windowX;
int y0 = y;
if (y0 < windowY) y0 = windowY;
if (x1 > windowX + windowWidth) x1 = windowX + windowWidth;
if (y1 > windowY + windowHeight) y1 = windowY + windowHeight;
if (x1 <= x || y1 <= y) return;
int left = margin + (x0 - windowX) * dx;
int top = margin + (y0 - windowY) * dy;
int width = (x1 - x0) * dx;
int height = (y1 - y0) * dy;
Rectangle r = editField.getBounds();
if (left + width > r.width) width -= (left + width - r.width);
if (top + height > r.height) height -= (top + height - r.height);
editField.repaint(left, top, width, height);
}
void onSave() {
if (fileNameSet) {
if (text.save(fileName)) textChanged = false;
} else {
onSaveAs();
}
}
void onSaveAs() {
FileDialog fd = new FileDialog(
(Frame) this, "Save File", FileDialog.SAVE
);
if (fileNameSet)
fd.setFile(fileName);
fd.setVisible(true);
String file = fd.getFile();
String directory = fd.getDirectory();
if (file == null || directory == null) return;
//... System.out.println("Selected file = " + file + ", directory = " + directory);
String oldFileName = fileName;
if (directory.length() > 0) {
char lastChar = directory.charAt(directory.length() - 1);
if (lastChar != '/' && lastChar != '\\') directory += '/';
}
fileName = directory + file;
if (text.save(fileName) &&
(!fileNameSet || oldFileName.equals(fileName))) {
textChanged = false;
}
if (!fileNameSet) {
fileNameSet = true;
setTitle(fileName);
}
else fileName = oldFileName;
}
void onQuit() {
if (textChanged) {
SaveDialog sd = new SaveDialog(this);
sd.setVisible(true); // Modal dialog
if (saveText) {
onSave();
}
saveText = false;
}
setVisible(false);
dispose();
System.exit(0);
}
void initFontMetric(Graphics g) {
FontMetrics fm = g.getFontMetrics();
//... dx = fm.getMaxAdvance(); // Maximal character advance (width)
dx = fm.charWidth('W'); // Maximal character advance (width)
dy = fm.getHeight();
ascent = fm.getAscent();
descent = fm.getDescent();
leading = fm.getLeading();
}
class EditField extends Canvas {
public void paint(Graphics g) {
if (dx == 0) {
initFontMetric(g);
}
Rectangle r = getBounds();
g.setPaintMode();
// Clear window
g.setColor(bgColor);
g.fillRect(0, 0, r.width, r.height);
// Define window size
windowWidth = (r.width - 2 * margin) / dx;
windowHeight = (r.height - 2 * margin) / dy;
g.setColor(fgColor);
int numLines = (r.height - 2 * margin) / dy;
for (int screenY = 0; screenY < numLines; screenY++) {
// Draw a line
int textY = windowY + screenY;
String line = null;
if (textY <= text.size()) {
String str;
if (textY == text.size()) {
str = endOfText;
} else {
str = text.getLine(textY);
}
if (windowX < str.length()) {
int endIndex = str.length();
if (endIndex > windowX + windowWidth)
endIndex = windowX + windowWidth;
line = str.substring(windowX, endIndex);
}
}
if (line != null) {
g.drawString(
line,
margin,
margin + ascent + dy * screenY
);
}
}
drawCursor(g, cursorX, cursorY, true);
inputDisabled = false;
}
public void update(Graphics g) { paint(g); }
} // End of class EditField
void onResize() {
Rectangle r = editField.getBounds();
if (dx != 0 && dy != 0) {
// Define window size
windowWidth = (r.width - margin) / dx;
windowHeight = (r.height - margin) / dy;
scrollToCursor();
}
editField.requestFocus();
}
}
class Text extends L2List {
static final int tabWidth = 8;
public Text() {
super();
}
public boolean load(String fileName) {
removeAll();
try {
BufferedReader input = new BufferedReader(
new FileReader(fileName)
);
String line;
while ((line = input.readLine()) != null) {
String str = convertTabs(line); // Replace tabulations by spaces
addBefore(str);
}
} catch (FileNotFoundException e) {
//... System.out.println("Cannot open a file: " + e);
return false;
} catch (IOException e) {
System.out.println("Read error: " + e);
return false;
}
return true;
}
public boolean save(String fileName) {
try {
BufferedWriter output = new BufferedWriter(
new FileWriter(fileName)
);
Enumeration lines = elements();
while (lines.hasMoreElements()) {
String str = (String) lines.nextElement();
//... System.out.println(str);
output.write(str);
output.newLine();
}
output.flush();
} catch (Exception e) {
System.out.println("Cannot write to file " + fileName + " " + e);
return false;
}
return true;
}
public String getLine(int i) {
String line = null;
if (i < size()) {
setPointer(i);
line = (String) elementAfter();
}
return line;
}
private String convertTabs(String line) {
StringBuffer str = new StringBuffer(256);
int x = 0;
int len = line.length();
int i = 0;
while (i < len) {
char c = line.charAt(i);
if (c == '\t') { // Tabulation
int spacesToAdd = tabWidth - (x % tabWidth);
while (spacesToAdd > 0) {
str.append(' ');
x++;
spacesToAdd--;
}
} else { // Other character
str.append(c);
x++;
}
i++;
}
return str.toString();
}
}
class SaveDialog extends Dialog implements ActionListener {
Button yes;
Button no;
TextEdit editor;
public SaveDialog(Frame f) {
super(f, true);
editor = (TextEdit) f;
setLayout(null);
setFont(new Font("Helvetica", Font.BOLD, 14));
setTitle("Save text?");
Label l = new Label("Text was changed, save it?");
int y = 30;
l.setBounds(10, y, 240, 30);
y += 40;
add(l);
yes = new Button("Yes");
no = new Button("No");
yes.setBounds(10, y, 80, 30);
no.setBounds(100, y, 80, 30);
add(yes); add(no);
yes.addActionListener(this);
no.addActionListener(this);
yes.requestFocus();
setSize(250, 130);
}
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == yes) editor.saveText = true;
else if (source == no) editor.saveText = false;
setVisible(false);
}
}