TEditor 80% complete

This commit is contained in:
Kevin Lamonte 2017-08-14 14:45:33 -04:00
parent e8a11f986b
commit 71a389c981
14 changed files with 651 additions and 143 deletions

View file

@ -11,17 +11,18 @@ BUG: TTreeView.reflow() doesn't keep the vertical dot within the
0.0.5
- TEditor
- Document
- Filename
- Pick appropriate Highlighter: plain, Java, XML, ...
- TEditorWidget:
- Mouse wheel is buggy as hell
- Actual editing
- Cut and Paste
- TEditorWindow extends TScrollableWindow
- TTextArea extends TScrollableWidget
0.0.6
- TEditor
- True tokenization and syntax highlighting: Java, C, Clojure
- True tokenization and syntax highlighting: Java, C, Clojure, XML
- Tab character support
- Finish up multiscreen support:
- cmAbort to cmScreenDisconnected

View file

@ -2420,6 +2420,16 @@ public class TApplication implements Runnable {
return true;
}
if (command.equals(cmMenu)) {
if (!modalWindowActive() && (activeMenu == null)) {
if (menus.size() > 0) {
menus.get(0).setActive(true);
activeMenu = menus.get(0);
return true;
}
}
}
return false;
}

View file

@ -121,6 +121,16 @@ public class TCommand {
*/
public static final int HELP = 20;
/**
* Enter first menu.
*/
public static final int MENU = 21;
/**
* Save file.
*/
public static final int SAVE = 30;
/**
* Type of command, one of EXIT, CASCADE, etc.
*/
@ -189,5 +199,7 @@ public class TCommand {
public static final TCommand cmWindowPrevious = new TCommand(WINDOW_PREVIOUS);
public static final TCommand cmWindowClose = new TCommand(WINDOW_CLOSE);
public static final TCommand cmHelp = new TCommand(HELP);
public static final TCommand cmSave = new TCommand(SAVE);
public static final TCommand cmMenu = new TCommand(MENU);
}

View file

@ -28,6 +28,8 @@
*/
package jexer;
import java.io.IOException;
import jexer.bits.CellAttributes;
import jexer.event.TKeypressEvent;
import jexer.event.TMouseEvent;
@ -121,34 +123,16 @@ public final class TEditorWidget extends TWidget {
@Override
public void onMouseDown(final TMouseEvent mouse) {
if (mouse.isMouseWheelUp()) {
if (getCursorY() == getHeight() - 1) {
if (document.up()) {
if (topLine > 0) {
topLine--;
}
alignCursor();
}
} else {
if (topLine > 0) {
topLine--;
setCursorY(getCursorY() + 1);
}
if (topLine > 0) {
topLine--;
alignDocument(false);
}
return;
}
if (mouse.isMouseWheelDown()) {
if (getCursorY() == 0) {
if (document.down()) {
if (topLine < document.getLineNumber()) {
topLine++;
}
alignCursor();
}
} else {
if (topLine < document.getLineCount() - getHeight()) {
topLine++;
setCursorY(getCursorY() - 1);
}
if (topLine < document.getLineCount() - 1) {
topLine++;
alignDocument(true);
}
return;
}
@ -185,6 +169,63 @@ public final class TEditorWidget extends TWidget {
super.onMouseDown(mouse);
}
/**
* Align visible area with document current line.
*
* @param topLineIsTop if true, make the top visible line the document
* current line if it was off-screen. If false, make the bottom visible
* line the document current line.
*/
private void alignTopLine(final boolean topLineIsTop) {
int line = document.getLineNumber();
if ((line < topLine) || (line > topLine + getHeight() - 1)) {
// Need to move topLine to bring document back into view.
if (topLineIsTop) {
topLine = line - (getHeight() - 1);
} else {
topLine = line;
}
}
/*
System.err.println("line " + line + " topLine " + topLine);
*/
// Document is in view, let's set cursorY
setCursorY(line - topLine);
alignCursor();
}
/**
* Align document current line with visible area.
*
* @param topLineIsTop if true, make the top visible line the document
* current line if it was off-screen. If false, make the bottom visible
* line the document current line.
*/
private void alignDocument(final boolean topLineIsTop) {
int line = document.getLineNumber();
if ((line < topLine) || (line > topLine + getHeight() - 1)) {
// Need to move document to ensure it fits view.
if (topLineIsTop) {
document.setLineNumber(topLine);
} else {
document.setLineNumber(topLine + (getHeight() - 1));
}
}
/*
System.err.println("getLineNumber() " + document.getLineNumber() +
" topLine " + topLine);
*/
// Document is in view, let's set cursorY
setCursorY(document.getLineNumber() - topLine);
alignCursor();
}
/**
* Align visible cursor with document cursor.
*/
@ -224,57 +265,17 @@ public final class TEditorWidget extends TWidget {
alignCursor();
}
} else if (keypress.equals(kbUp)) {
if (document.up()) {
if (getCursorY() > 0) {
setCursorY(getCursorY() - 1);
} else {
if (topLine > 0) {
topLine--;
}
}
alignCursor();
}
document.up();
alignTopLine(false);
} else if (keypress.equals(kbDown)) {
if (document.down()) {
if (getCursorY() < getHeight() - 1) {
setCursorY(getCursorY() + 1);
} else {
if (topLine < document.getLineCount() - getHeight()) {
topLine++;
}
}
alignCursor();
}
document.down();
alignTopLine(true);
} else if (keypress.equals(kbPgUp)) {
for (int i = 0; i < getHeight() - 1; i++) {
if (document.up()) {
if (getCursorY() > 0) {
setCursorY(getCursorY() - 1);
} else {
if (topLine > 0) {
topLine--;
}
}
alignCursor();
} else {
break;
}
}
document.up(getHeight() - 1);
alignTopLine(false);
} else if (keypress.equals(kbPgDn)) {
for (int i = 0; i < getHeight() - 1; i++) {
if (document.down()) {
if (getCursorY() < getHeight() - 1) {
setCursorY(getCursorY() + 1);
} else {
if (topLine < document.getLineCount() - getHeight()) {
topLine++;
}
}
alignCursor();
} else {
break;
}
}
document.down(getHeight() - 1);
alignTopLine(true);
} else if (keypress.equals(kbHome)) {
if (document.home()) {
leftColumn = 0;
@ -297,29 +298,25 @@ public final class TEditorWidget extends TWidget {
} else if (keypress.equals(kbCtrlEnd)) {
document.setLineNumber(document.getLineCount() - 1);
document.end();
topLine = document.getLineCount() - getHeight();
if (topLine < 0) {
topLine = 0;
}
if (document.getLineCount() > getHeight()) {
setCursorY(getHeight() - 1);
} else {
setCursorY(document.getLineCount() - 1);
}
alignCursor();
alignTopLine(false);
} else if (keypress.equals(kbIns)) {
document.setOverwrite(!document.getOverwrite());
} else if (keypress.equals(kbDel)) {
// TODO: join lines
document.del();
alignCursor();
} else if (keypress.equals(kbBackspace)) {
document.backspace();
alignCursor();
} else if (keypress.equals(kbEnter)) {
// TODO: split lines
} else if (!keypress.getKey().isFnKey()
&& !keypress.getKey().isAlt()
&& !keypress.getKey().isCtrl()
) {
// Plain old keystroke, process it
document.addChar(keypress.getKey().getChar());
alignCursor();
} else {
// Pass other keys (tab etc.) on to TWidget
super.onKeypress(keypress);
@ -354,4 +351,87 @@ public final class TEditorWidget extends TWidget {
}
}
/**
* Get the number of lines in the underlying Document.
*
* @return the number of lines
*/
public int getLineCount() {
return document.getLineCount();
}
/**
* Get the current editing row number. 1-based.
*
* @return the editing row number. Row 1 is the first row.
*/
public int getEditingRowNumber() {
return document.getLineNumber() + 1;
}
/**
* Set the current editing row number. 1-based.
*
* @param row the new editing row number. Row 1 is the first row.
*/
public void setEditingRowNumber(final int row) {
document.setLineNumber(row - 1);
}
/**
* Get the current editing column number. 1-based.
*
* @return the editing column number. Column 1 is the first column.
*/
public int getEditingColumnNumber() {
return document.getCursor() + 1;
}
/**
* Set the current editing column number. 1-based.
*
* @param column the new editing column number. Column 1 is the first
* column.
*/
public void setEditingColumnNumber(final int column) {
document.setCursor(column - 1);
}
/**
* Get the maximum possible row number. 1-based.
*
* @return the maximum row number. Row 1 is the first row.
*/
public int getMaximumRowNumber() {
return document.getLineCount() + 1;
}
/**
* Get the maximum possible column number. 1-based.
*
* @return the maximum column number. Column 1 is the first column.
*/
public int getMaximumColumnNumber() {
return document.getLineLengthMax() + 1;
}
/**
* Get the dirty value.
*
* @return true if the buffer is dirty
*/
public boolean isDirty() {
return document.isDirty();
}
/**
* Save contents to file.
*
* @param filename file to save to
* @throws IOException if a java.io operation throws
*/
public void saveToFilename(final String filename) throws IOException {
document.saveToFilename(filename);
}
}

View file

@ -0,0 +1,267 @@
/*
* Jexer - Java Text User Interface
*
* The MIT License (MIT)
*
* Copyright (C) 2017 Kevin Lamonte
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* @author Kevin Lamonte [kevin.lamonte@gmail.com]
* @version 1
*/
package jexer;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
import jexer.TApplication;
import jexer.TEditorWidget;
import jexer.THScroller;
import jexer.TScrollableWindow;
import jexer.TVScroller;
import jexer.TWidget;
import jexer.bits.CellAttributes;
import jexer.bits.GraphicsChars;
import jexer.event.TCommandEvent;
import jexer.event.TMouseEvent;
import jexer.event.TResizeEvent;
import static jexer.TCommand.*;
import static jexer.TKeypress.*;
/**
* TEditorWindow is a basic text file editor.
*/
public class TEditorWindow extends TScrollableWindow {
/**
* Hang onto my TEditor so I can resize it with the window.
*/
private TEditorWidget editField;
/**
* The fully-qualified name of the file being edited.
*/
private String filename = "";
/**
* Setup other fields after the editor is created.
*/
private void setupAfterEditor() {
hScroller = new THScroller(this, 17, getHeight() - 2, getWidth() - 20);
vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
setMinimumWindowWidth(25);
setMinimumWindowHeight(10);
setTopValue(1);
setBottomValue(editField.getMaximumRowNumber());
setLeftValue(1);
setRightValue(editField.getMaximumColumnNumber());
statusBar = newStatusBar("Editor");
statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
statusBar.addShortcutKeypress(kbF2, cmSave, "Save");
statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
statusBar.addShortcutKeypress(kbF10, cmMenu, "Menu");
}
/**
* Public constructor sets window title.
*
* @param parent the main application
* @param title the window title
*/
public TEditorWindow(final TApplication parent, final String title) {
super(parent, title, 0, 0, parent.getScreen().getWidth(),
parent.getScreen().getHeight() - 2, RESIZABLE);
editField = addEditor("", 0, 0, getWidth() - 2, getHeight() - 2);
setupAfterEditor();
}
/**
* Public constructor sets window title and contents.
*
* @param parent the main application
* @param title the window title, usually a filename
* @param contents the data for the editing window, usually the file data
*/
public TEditorWindow(final TApplication parent, final String title,
final String contents) {
super(parent, title, 0, 0, parent.getScreen().getWidth(),
parent.getScreen().getHeight() - 2, RESIZABLE);
filename = title;
editField = addEditor(contents, 0, 0, getWidth() - 2, getHeight() - 2);
setupAfterEditor();
}
/**
* Public constructor.
*
* @param parent the main application
*/
public TEditorWindow(final TApplication parent) {
this(parent, "New Text Document");
}
/**
* Draw the window.
*/
@Override
public void draw() {
// Draw as normal.
super.draw();
// Add the row:col on the bottom row
CellAttributes borderColor = getBorder();
String location = String.format(" %d:%d ",
editField.getEditingRowNumber(),
editField.getEditingColumnNumber());
int colon = location.indexOf(':');
putStringXY(10 - colon, getHeight() - 1, location, borderColor);
if (editField.isDirty()) {
putCharXY(2, getHeight() - 1, GraphicsChars.OCTOSTAR, borderColor);
}
}
/**
* Check if a mouse press/release/motion event coordinate is over the
* editor.
*
* @param mouse a mouse-based event
* @return whether or not the mouse is on the emulator
*/
private final boolean mouseOnEditor(final TMouseEvent mouse) {
if ((mouse.getAbsoluteX() >= getAbsoluteX() + 1)
&& (mouse.getAbsoluteX() < getAbsoluteX() + getWidth() - 1)
&& (mouse.getAbsoluteY() >= getAbsoluteY() + 1)
&& (mouse.getAbsoluteY() < getAbsoluteY() + getHeight() - 1)
) {
return true;
}
return false;
}
/**
* Handle mouse press events.
*
* @param mouse mouse button press event
*/
@Override
public void onMouseDown(final TMouseEvent mouse) {
if (mouseOnEditor(mouse)) {
editField.onMouseDown(mouse);
setBottomValue(editField.getMaximumRowNumber());
setVerticalValue(editField.getEditingRowNumber());
setRightValue(editField.getMaximumColumnNumber());
setHorizontalValue(editField.getEditingColumnNumber());
} else {
// Let the scrollbars get the event
super.onMouseDown(mouse);
if (mouse.isMouseWheelUp() || mouse.isMouseWheelDown()) {
editField.setEditingRowNumber(getVerticalValue());
}
// TODO: horizontal scrolling
}
}
/**
* Handle window/screen resize events.
*
* @param event resize event
*/
@Override
public void onResize(final TResizeEvent event) {
if (event.getType() == TResizeEvent.Type.WIDGET) {
// Resize the text field
TResizeEvent editSize = new TResizeEvent(TResizeEvent.Type.WIDGET,
event.getWidth() - 2, event.getHeight() - 2);
editField.onResize(editSize);
// Have TScrollableWindow handle the scrollbars
super.onResize(event);
return;
}
// Pass to children instead
for (TWidget widget: getChildren()) {
widget.onResize(event);
}
}
/**
* Method that subclasses can override to handle posted command events.
*
* @param command command event
*/
@Override
public void onCommand(final TCommandEvent command) {
if (command.equals(cmOpen)) {
try {
String filename = fileOpenBox(".");
if (filename != null) {
try {
File file = new File(filename);
StringBuilder fileContents = new StringBuilder();
Scanner scanner = new Scanner(file);
String EOL = System.getProperty("line.separator");
try {
while (scanner.hasNextLine()) {
fileContents.append(scanner.nextLine() + EOL);
}
new TEditorWindow(getApplication(), filename,
fileContents.toString());
} finally {
scanner.close();
}
} catch (IOException e) {
// TODO: make this a message box
e.printStackTrace();
}
}
} catch (IOException e) {
// TODO: make this a message box
e.printStackTrace();
}
return;
}
if (command.equals(cmSave)) {
if (filename.length() > 0) {
try {
editField.saveToFilename(filename);
} catch (IOException e) {
// TODO: make this a message box
e.printStackTrace();
}
}
return;
}
// Didn't handle it, let children get it instead
super.onCommand(command);
}
}

View file

@ -53,8 +53,8 @@ public class TScrollableWindow extends TWindow implements Scrollable {
protected void placeScrollbars() {
if (hScroller != null) {
hScroller.setY(getHeight() - 2);
hScroller.setWidth(getWidth() - 3);
hScroller.setBigChange(getWidth() - 3);
hScroller.setWidth(getWidth() - hScroller.getX() - 3);
hScroller.setBigChange(getWidth() - hScroller.getX() - 3);
}
if (vScroller != null) {
vScroller.setX(getWidth() - 2);

View file

@ -284,9 +284,60 @@ public class TWindow extends TWidget {
* @param maximumWindowWidth new maximum width
*/
public final void setMaximumWindowWidth(final int maximumWindowWidth) {
if ((maximumWindowWidth != -1)
&& (maximumWindowWidth < minimumWindowWidth + 1)
) {
throw new IllegalArgumentException("Maximum window width cannot " +
"be smaller than minimum window width + 1");
}
this.maximumWindowWidth = maximumWindowWidth;
}
/**
* Set the minimum width for this window.
*
* @param minimumWindowWidth new minimum width
*/
public final void setMinimumWindowWidth(final int minimumWindowWidth) {
if ((maximumWindowWidth != -1)
&& (minimumWindowWidth > maximumWindowWidth - 1)
) {
throw new IllegalArgumentException("Minimum window width cannot " +
"be larger than maximum window width - 1");
}
this.minimumWindowWidth = minimumWindowWidth;
}
/**
* Set the maximum height for this window.
*
* @param maximumWindowHeight new maximum height
*/
public final void setMaximumWindowHeight(final int maximumWindowHeight) {
if ((maximumWindowHeight != -1)
&& (maximumWindowHeight < minimumWindowHeight + 1)
) {
throw new IllegalArgumentException("Maximum window height cannot " +
"be smaller than minimum window height + 1");
}
this.maximumWindowHeight = maximumWindowHeight;
}
/**
* Set the minimum height for this window.
*
* @param minimumWindowHeight new minimum height
*/
public final void setMinimumWindowHeight(final int minimumWindowHeight) {
if ((maximumWindowHeight != -1)
&& (minimumWindowHeight > maximumWindowHeight - 1)
) {
throw new IllegalArgumentException("Minimum window height cannot " +
"be larger than maximum window height - 1");
}
this.minimumWindowHeight = minimumWindowHeight;
}
/**
* Recenter the window on-screen.
*/

View file

@ -146,4 +146,5 @@ public final class GraphicsChars {
public static final char WINDOW_LEFT_BOTTOM_DOUBLE = CP437[0xC8];
public static final char WINDOW_RIGHT_BOTTOM_DOUBLE = CP437[0xBC];
public static final char VERTICAL_BAR = CP437[0xB3];
public static final char OCTOSTAR = CP437[0x0F];
}

View file

@ -76,7 +76,7 @@ public class DemoApplication extends TApplication {
item = subMenu.addItem(2002, "&Normal (sub)");
if (getScreen() instanceof SwingTerminal) {
TMenu swingMenu = addMenu("&Swing");
TMenu swingMenu = addMenu("Swin&g");
item = swingMenu.addItem(3000, "&Bigger +2");
item = swingMenu.addItem(3001, "&Smaller -2");
}

View file

@ -34,7 +34,7 @@ import static jexer.TCommand.*;
import static jexer.TKeypress.*;
/**
* This window demonstates the TText, THScroller, and TVScroller widgets.
* This window demonstates the TEditor widget.
*/
public class DemoEditorWindow extends TWindow {
@ -56,10 +56,9 @@ public class DemoEditorWindow extends TWindow {
super(parent, title, 0, 0, 44, 22, RESIZABLE);
editField = addEditor(text, 0, 0, 42, 20);
statusBar = newStatusBar("Editable text window");
statusBar = newStatusBar("Editable text demo window");
statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
statusBar.addShortcutKeypress(kbF2, cmShell, "Shell");
statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
statusBar.addShortcutKeypress(kbF10, cmExit, "Exit");
}

View file

@ -77,7 +77,7 @@ public class DemoMainWindow extends TWindow {
private DemoMainWindow(final TApplication parent, final int flags) {
// Construct a demo window. X and Y don't matter because it will be
// centered on screen.
super(parent, "Demo Window", 0, 0, 60, 23, flags);
super(parent, "Demo Window", 0, 0, 64, 23, flags);
int row = 1;
@ -123,13 +123,20 @@ public class DemoMainWindow extends TWindow {
row += 2;
addLabel("Editor window", 1, row);
addButton("Edito&r", 35, row,
addButton("&1 Widget", 35, row,
new TAction() {
public void DO() {
new DemoEditorWindow(getApplication());
}
}
);
addButton("&2 Window", 48, row,
new TAction() {
public void DO() {
new TEditorWindow(getApplication());
}
}
);
row += 2;
addLabel("Text areas", 1, row);

View file

@ -28,6 +28,9 @@
*/
package jexer.teditor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
@ -54,6 +57,11 @@ public class Document {
*/
private boolean overwrite = false;
/**
* If true, the document has been edited.
*/
private boolean dirty = false;
/**
* The default color for the TEditor class.
*/
@ -73,6 +81,41 @@ public class Document {
return overwrite;
}
/**
* Get the dirty value.
*
* @return true if the buffer is dirty
*/
public boolean isDirty() {
return dirty;
}
/**
* Save contents to file.
*
* @param filename file to save to
* @throws IOException if a java.io operation throws
*/
public void saveToFilename(final String filename) throws IOException {
OutputStreamWriter output = null;
try {
output = new OutputStreamWriter(new FileOutputStream(filename),
"UTF-8");
for (Line line: lines) {
output.write(line.getRawString());
output.write("\n");
}
dirty = false;
}
finally {
if (output != null) {
output.close();
}
}
}
/**
* Set the overwrite flag.
*
@ -136,6 +179,15 @@ public class Document {
return lines.get(lineNumber).getCursor();
}
/**
* Set the current cursor position of the editing line. 0-based.
*
* @param cursor the new cursor position
*/
public void setCursor(final int cursor) {
lines.get(lineNumber).setCursor(cursor);
}
/**
* Construct a new Document from an existing text string.
*
@ -280,6 +332,7 @@ public class Document {
* Delete the character under the cursor.
*/
public void del() {
dirty = true;
lines.get(lineNumber).del();
}
@ -287,6 +340,7 @@ public class Document {
* Delete the character immediately preceeding the cursor.
*/
public void backspace() {
dirty = true;
lines.get(lineNumber).backspace();
}
@ -297,6 +351,7 @@ public class Document {
* @param ch the character to replace or insert
*/
public void addChar(final char ch) {
dirty = true;
if (overwrite) {
lines.get(lineNumber).replaceChar(ch);
} else {

View file

@ -60,14 +60,19 @@ public class Line {
private int cursor = 0;
/**
* The current word that the cursor position is in.
* The raw text of this line, what is passed to Word to determine
* highlighting behavior.
*/
private Word currentWord;
private StringBuilder rawText;
/**
* We use getDisplayLength() a lot, so cache the value.
* Get a (shallow) copy of the words in this line.
*
* @return a copy of the word list
*/
private int displayLength = -1;
public List<Word> getWords() {
return new ArrayList<Word>(words);
}
/**
* Get the current cursor position.
@ -92,16 +97,6 @@ public class Line {
getDisplayLength() + ", requested position " + cursor);
}
this.cursor = cursor;
// TODO: set word
}
/**
* Get a (shallow) copy of the list of words.
*
* @return the list of words
*/
public List<Word> getWords() {
return new ArrayList<Word>(words);
}
/**
@ -110,21 +105,44 @@ public class Line {
* @return the number of cells needed to display this line
*/
public int getDisplayLength() {
if (displayLength != -1) {
return displayLength;
}
int n = 0;
for (Word word: words) {
n += word.getDisplayLength();
}
displayLength = n;
int n = rawText.length();
// If we have any visible characters, add one to the display so that
// the cursor is immediately after the data.
if (displayLength > 0) {
displayLength++;
// For now just return the raw text length.
if (n > 0) {
// If we have any visible characters, add one to the display so
// that the cursor is immediately after the data.
return n + 1;
}
return n;
}
/**
* Get the raw string that matches this line.
*
* @return the string
*/
public String getRawString() {
return rawText.toString();
}
/**
* Scan rawText and make words out of it.
*/
private void scanLine() {
words.clear();
Word word = new Word(this.defaultColor, this.highlighter);
words.add(word);
for (int i = 0; i < rawText.length(); i++) {
char ch = rawText.charAt(i);
Word newWord = word.addChar(ch);
if (newWord != word) {
words.add(newWord);
word = newWord;
}
}
for (Word w: words) {
w.applyHighlight();
}
return displayLength;
}
/**
@ -140,20 +158,9 @@ public class Line {
this.defaultColor = defaultColor;
this.highlighter = highlighter;
this.rawText = new StringBuilder(str);
currentWord = new Word(this.defaultColor, this.highlighter);
words.add(currentWord);
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
Word newWord = currentWord.addChar(ch);
if (newWord != currentWord) {
words.add(newWord);
currentWord = newWord;
}
}
for (Word word: words) {
word.applyHighlight();
}
scanLine();
}
/**
@ -175,7 +182,6 @@ public class Line {
if (cursor == 0) {
return false;
}
// TODO: switch word
cursor--;
return true;
}
@ -192,7 +198,6 @@ public class Line {
if (cursor == getDisplayLength() - 1) {
return false;
}
// TODO: switch word
cursor++;
return true;
}
@ -205,7 +210,6 @@ public class Line {
public boolean home() {
if (cursor > 0) {
cursor = 0;
currentWord = words.get(0);
return true;
}
return false;
@ -222,7 +226,6 @@ public class Line {
if (cursor < 0) {
cursor = 0;
}
currentWord = words.get(words.size() - 1);
return true;
}
return false;
@ -232,14 +235,23 @@ public class Line {
* Delete the character under the cursor.
*/
public void del() {
// TODO
assert (words.size() > 0);
if (cursor < getDisplayLength()) {
rawText.deleteCharAt(cursor);
}
// Re-scan the line to determine the new word boundaries.
scanLine();
}
/**
* Delete the character immediately preceeding the cursor.
*/
public void backspace() {
// TODO
if (left()) {
del();
}
}
/**
@ -248,7 +260,13 @@ public class Line {
* @param ch the character to insert
*/
public void addChar(final char ch) {
// TODO
if (cursor < getDisplayLength() - 1) {
rawText.insert(cursor, ch);
} else {
rawText.append(ch);
}
scanLine();
cursor++;
}
/**
@ -257,7 +275,13 @@ public class Line {
* @param ch the character to replace
*/
public void replaceChar(final char ch) {
// TODO
if (cursor < getDisplayLength() - 1) {
rawText.setCharAt(cursor, ch);
} else {
rawText.append(ch);
}
scanLine();
cursor++;
}
}

View file

@ -165,7 +165,8 @@ public class Word {
* Add a character to this word. If this is a whitespace character
* adding to a non-whitespace word, create a new word and return that;
* similarly if this a non-whitespace character adding to a whitespace
* word, create a new word and return that.
* word, create a new word and return that. Note package private access:
* this is only called by Line to figure out highlighting boundaries.
*
* @param ch the new character to add
* @return either this word (if it was added), or a new word that