mirror of
https://gitlab.com/AutumnMeowMeow/jexer
synced 2024-09-19 11:50:19 -06:00
TEditor 80% complete
This commit is contained in:
parent
e8a11f986b
commit
71a389c981
14 changed files with 651 additions and 143 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
267
src/jexer/TEditorWindow.java
Normal file
267
src/jexer/TEditorWindow.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue