Webber: A Website Construction Application
HtmlEdit.java
// A HtmlPage component with editing functionality added
package library.html;
import java.awt.*;
import java.util.*;
import java.awt.event.*;
public class HtmlEdit extends HtmlPage {
private String linesep = System.getProperty("line.separator");
private int curx, cury, pos;
// Page constructors
public HtmlEdit() { this(null); }
public HtmlEdit(char[] text) {
super(text);
reader.setHtmlEnabled(false); // Disable HTML output
}
// Implement handling for text-event listeners
private TextListener textListener = null;
public void addTextListener(TextListener l) {
textListener = AWTEventMulticaster.add(textListener, l);
}
public void removeTextListener(TextListener l) {
textListener = AWTEventMulticaster.remove(textListener, l);
}
public void processEvent(AWTEvent e) {
if (e instanceof TextEvent) {
if (textListener != null)
textListener.textValueChanged((TextEvent)e);
} else super.processEvent(e);
}
// Update text with text incremented by offset at pos (dispatches a text event)
private void modifyText(char[] text, int offset) {
// resetText(text); // Reset text
updateText(text, pos, offset); // Update text
processEvent(new TextEvent(this, TextEvent.TEXT_VALUE_CHANGED));
}
// Override HtmlPage to ensure HtmlPage.text is not null (& reset page)
public void setText(char[] text, boolean isPlain) {
super.setText((text==null) ? new char[0] : text, isPlain);
curx = cury = pos = 0; // Reset cursor & pos
}
// Override HtmlPage to change the default text-formatting mode
public boolean isPlainText() { return true; }
// Override HtmlPage to change the default font settings
protected Font getNormalFont() { return getTeleFont(); }
// Override HtmlPage to add the cursor
public void paint(Graphics g) { super.paint(g); putCursor(g); }
// Return window coordinates for the edit cursor
private int getWinXpos() { return getLineStart() + curx - left; }
private int getWinYpos() { return getPageStart() + cury - top; }
// Draw/erase cursor
private void putCursor(Graphics g) {
int x = getWinXpos(), y = getWinYpos();
g.setColor(Color.black);
g.setXORMode(Color.white);
g.drawLine(x, y, x, y+lineheight-1);
g.setPaintMode();
}
private void putCursor() { putCursor(getGraphics()); }
// Returns true if there is a CR/NL sequence at pos in text
private static boolean isCrNl(char[] text, int pos) {
if ((pos<0) || (pos+1>=text.length)) return false;
return ((text[pos]!='\r') || (text[pos+1]!='\n')) ? false : true;
}
// Returns true if text at pos is an end-of-line sequence
private static boolean isEndOfLine(char[] text, int pos) {
if (text[pos]=='\n') return true; // Unix
if (isCrNl(text, pos)) return true; // DOS
return false;
}
// Return the number of characters in the sequence at pos in text (1 or 2)
private static int charSize(char[] text, int pos) {
return isCrNl(text, pos) ? 2 : 1; // Two chars for DOS CR/NL
}
// Return text with length characters from pos removed
private static char[] deleteChars(char[] text, int pos, int length) {
String s = new String(text, 0, pos)
+ new String(text, pos+length, text.length-(pos+length));
return s.toCharArray();
}
private static char[] deleteChar(char[] text, int pos) {
return deleteChars(text, pos, charSize(text, pos));
}
// Return text with s inserted at pos
private static char[] insertChars(char[] text, int pos, String s) {
s = new String(text, 0, pos) + s
+ new String(text, pos, text.length-pos);
return s.toCharArray();
}
private static char[] insertChar(char[] text, int pos, char c) {
return insertChars(text, pos, "" + c);
}
// Return the character offset of pos into it's line in text
private int getPosInLine(char[] text, int pos) {
int i;
for (i=pos; i > 0; --i) if (text[i-1]=='\n') break;
return pos - i;
}
// Return the width of a line of text (with tabs)
private int getLineWidth(String s) {
int start = getLineStart();
return textLine(s, start, 0, null) - start;
}
// Return the screen offset of pos into it's line in text
private int getLinePos(char[] text, int pos) {
int p = getPosInLine(text, pos);
return getLineWidth(new String(text, pos-p, p));
}
// Return the length of the line from pos in text
private static int getLineLength(char[] text, int pos) {
int i;
for (i=pos; i<text.length; ++i)
if (isEndOfLine(text, i)) break;
return i - pos;
}
// Set the current character position in the current line
private void setPosInLine(int p) {
pos -= getPosInLine(text, pos); // Move to start of line
int len = getLineLength(text, pos); // Get total line length
pos += (p > len) ? len : p; // Set actual position
curx = getLinePos(text, pos); // Get cursor column
}
// Adjust page line to ensure cursor visibility (true if adjusted)
private boolean findCursorLine() {
int y = getWinYpos(), h = getNumLines() * lineheight;
if (y<0) do {
top -= lineheight;
y = getWinYpos();
} while (y<0);
else if (y>=h) do {
top += lineheight;
y = getWinYpos();
} while (y>=h);
else return false;
return true;
}
// Adjust page column to ensure cursor visibility (true if adjusted)
private boolean findCursorColumn() {
int x = getWinXpos(), w = getSize().width;
if (x<0) do {
left -= tabsize;
x = getWinXpos();
} while (x<0);
else if (x>=w) do {
left += tabsize;
x = getWinXpos();
} while (x>=w);
else return false;
return true;
}
// Adjust page to ensure cursor visibility (true if adjusted)
private boolean findCursor() {
boolean y = findCursorLine(), x = findCursorColumn();
return x || y;
}
// Move current character position to the start of the next line
// Parameter p is the offset to the end of the current line
private boolean nextLine(int p) {
if (pos+p >= text.length) return false; // No next line?
pos += p + charSize(text, pos+p);
cury += lineheight; // Move cursor line
return true;
}
private boolean nextLine() { return nextLine(getLineLength(text, pos)); }
// Move current character position to the end of the previous line
// Parameter p is the offset to the start of the current line
private boolean lastLine(int p) {
if (p==pos) return false; // No prior line?
pos -= p + charSize(text, pos - p - 2);
cury -= lineheight; // Move cursor line
return true;
}
private boolean lastLine() { return lastLine(getPosInLine(text, pos)); }
// Move position up or down by offset lines
private void movePos(int offset) {
while (offset<0) { lastLine(); ++offset; }
while (offset>0) { nextLine(); --offset; }
}
// Move page up or down by offset lines (response to KeyEvent)
private void movePage(int offset, KeyEvent e) {
int p = getPosInLine(text, pos), // Get character position
q = pos; // Store current position
putCursor(); // Erase cursor
super.keyPressed(e); // Move page
if (q!=pos) putCursor(); // Erase cursor again?
movePos(offset); // Move cursor
setPosInLine(p); // Restore position
putCursor(); // Redraw cursor
}
// Move left one character
private void charLeft() {
if (getPosInLine(text, pos) > 0) --pos; // Start of line?
else lastLine(0); // Prior line
curx = getLinePos(text, pos);
}
// Move right one character
private void charRight() {
if (!isEndOfLine(text, pos)) ++pos; // End of line?
else nextLine(0); // Next line
curx = getLinePos(text, pos);
}
// Process special keys (overrides HtmlPage)
public void keyPressed(KeyEvent e) {
int p, q;
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
p = getPosInLine(text, pos); // Get character position
if (p==pos) break; // Top line?
putCursor(); // Erase cursor
lastLine(p); // Move to prior line
setPosInLine(p); // Restore position
if (!findCursor()) putCursor(); // Redraw cursor
else repaint(); // Redraw page
break;
case KeyEvent.VK_DOWN:
q = getLineLength(text, pos); // Get end of line
if (pos+q>=text.length) break; // Bottom line?
p = getPosInLine(text, pos); // Get character position
putCursor(); // Erase cursor
nextLine(q); // Move to next line
setPosInLine(p); // Restore position
if (!findCursor()) putCursor(); // Redraw cursor
else repaint(); // Redraw page
break;
case KeyEvent.VK_LEFT:
if (pos<1) break; // Start of text?
putCursor(); // Erase cursor
charLeft(); // Left one character
if (!findCursor()) putCursor(); // Redraw cursor
else repaint(); // Redraw page
break;
case KeyEvent.VK_RIGHT:
if (pos>=text.length) break; // End of text?
putCursor(); // Erase cursor
charRight(); // Right one character
if (!findCursor()) putCursor(); // Redraw cursor
else repaint(); // Redraw page
break;
case KeyEvent.VK_PAGE_UP:
movePage(-getNumLines(), e); // Move page & cursor
break;
case KeyEvent.VK_PAGE_DOWN:
movePage(getNumLines(), e); // Move page & cursor
break;
case KeyEvent.VK_HOME:
putCursor(); // Erase cursor
pos -= getPosInLine(text, pos);
curx = 0;
if (!findCursor()) putCursor(); // Redraw cursor
else repaint(); // Redraw page
break;
case KeyEvent.VK_END:
putCursor(); // Erase cursor
pos += getLineLength(text, pos);// Move to end of line
curx = getLinePos(text, pos); // Calculate screen pos
if (!findCursor()) putCursor(); // Redraw cursor
else repaint(); // Redraw page
break;
case KeyEvent.VK_BACK_SPACE:
if (pos<1) break;
charLeft(); // Proceed using DELETE
case KeyEvent.VK_DELETE:
if (pos>=text.length) break;
p = charSize(text, pos); // Get deletion count
if (isEndOfLine(text, pos)) // Decrement total height?
addHtmlHeight(-lineheight);
modifyText(deleteChar(text, pos), -p);
findCursor(); // Adjust page to cursor
repaint(); // Redraw page
break;
case KeyEvent.VK_ENTER:
p = linesep.length();
modifyText(insertChars(text, pos, linesep), p);
addHtmlHeight(lineheight); // Increment total height
pos += p;
curx = 0;
cury += lineheight;
findCursor(); // Adjust page to cursor
repaint(); // Redraw page
break;
case KeyEvent.VK_TAB: // Block focus transfer
e.setKeyCode(KeyEvent.VK_SPACE);
break;
default:
super.keyPressed(e);
}
}
// Read text characters
public void keyTyped(KeyEvent e) {
char c = e.getKeyChar();
if ((c!='\t') && Character.isISOControl(c)) return;
modifyText(insertChar(text, pos, c), 1);// Update text
curx = getLinePos(text, ++pos); // Get new cursor pos
findCursor(); // Adjust page to cursor
repaint(); // Redraw page
}
}
Go To:
Source Code