Additional backends can be created by subclassing
jexer.backend.Backend and passing it into the TApplication
-constructor.
+constructor. See Demo5 and Demo6 for examples of other backends.
The Jexer homepage, which includes additional information and binary
release downloads, is at: https://jexer.sourceforge.io . The Jexer
- Closing a TTerminalWindow without exiting the process inside it
may result in a zombie 'script' process.
+ - TTerminalWindow cannot notify the child process of changes in
+ window size, due to Java's lack of support for forkpty() and
+ similar. Solving this requires C, and will be pursued only if
+ sufficient user requests come in.
+
- Java's InputStreamReader as used by the ECMA48 backend requires a
valid UTF-8 stream. The default X10 encoding for mouse
coordinates outside (160,94) can corrupt that stream, at best
Roadmap
-------
-BUG: TTreeView.reflow() doesn't keep the vertical dot within the
- scrollbar.
-
0.0.6
+- TSpinner
+- TComboBox
+- TCalendar
+
- TEditor
+ - Horizontal scrollbar integration
- True tokenization and syntax highlighting: Java, C, Clojure, XML
+ - Carat notation for control characters
- Tab character support
- - Cut and Paste
+ - Cut/copy/paste
+ - Performance: behave smoothly on 100MB text files
+
+0.0.7
- Finish up multiscreen support:
- cmAbort to cmScreenDisconnected
- cmScreenConnected
- Handle screen resizes
-- TSpinner
-- TComboBox
-- TCalendar
+- TEditor
+ - Word wrap
+ - Performance: behave smoothly on 1GB text files
-0.0.7
+- Additional main color themes:
+ - Dark / L33t
+ - Green / NoReallyElite
+ - Red/brown
+ - Monochrome
+ - OMGPonies
+
+0.0.8
- THelpWindow
- - TText + clickable links
+ - TEditor + clickable links
- Index
-0.0.8
+- TEditor
+ - Expose API:
+ - Cursor movement
+ - Movement within document
+ - Cut/copy/paste
-- Undo / Redo support
+0.0.9
+
+- TEditor:
+ - Undo / Redo support
0.1.0: BETA RELEASE and BUG HUNT
- Verify vttest in multiple tterminals.
+0.2.0:
+
+- Drag and drop
+ - TEditor
+ - TField
+ - TText
+ - TTerminal
+ - TComboBox
+
1.0.0
-- Maven artifact.
+- Publish to the whole wide world
1.1.0 Wishlist
--------------
- TTerminal
- - Handle resize events (pass to child process)
+ - Handle resize events (pass to child process). Will need to switch
+ to forkpty(), or ship a C wrapper process.
- Screen
- Allow complex characters in putCharXY() and detect them in putStringXY().
-- Drag and drop
- - TEditor
- - TField
- - TText
- - TTerminal
- - TComboBox
-
Regression Checklist
return;
}
+ // Whatever window might be moving/dragging, stop it now.
+ for (TWindow w: windows) {
+ if (w.inMovements()) {
+ w.stopMovements();
+ }
+ }
+
assert (windows.size() > 0);
if (window.isHidden()) {
return;
}
+ // Whatever window might be moving/dragging, stop it now.
+ for (TWindow w: windows) {
+ if (w.inMovements()) {
+ w.stopMovements();
+ }
+ }
+
assert (windows.size() > 0);
if (!window.hidden) {
return;
}
+ // Whatever window might be moving/dragging, stop it now.
+ for (TWindow w: windows) {
+ if (w.inMovements()) {
+ w.stopMovements();
+ }
+ }
+
assert (windows.size() > 0);
if (window.hidden) {
}
synchronized (windows) {
+ // Whatever window might be moving/dragging, stop it now.
+ for (TWindow w: windows) {
+ if (w.inMovements()) {
+ w.stopMovements();
+ }
+ }
+
int z = window.getZ();
window.setZ(-1);
window.onUnfocus();
assert (activeWindow != null);
synchronized (windows) {
+ // Whatever window might be moving/dragging, stop it now.
+ for (TWindow w: windows) {
+ if (w.inMovements()) {
+ w.stopMovements();
+ }
+ }
// Swap z/active between active window and the next in the list
int activeWindowI = -1;
}
synchronized (windows) {
+ // Whatever window might be moving/dragging, stop it now.
+ for (TWindow w: windows) {
+ if (w.inMovements()) {
+ w.stopMovements();
+ }
+ }
+
// Do not allow a modal window to spawn a non-modal window. If a
// modal window is active, then this window will become modal
// too.
}
}
setList(newStrings);
+
+ // Select the first entry
+ if (getMaxSelectedIndex() >= 0) {
+ setSelectedIndex(0);
+ }
}
/**
// Need to move topLine to bring document back into view.
if (topLineIsTop) {
topLine = line - (getHeight() - 1);
+ if (topLine < 0) {
+ topLine = 0;
+ }
+ assert (topLine >= 0);
} else {
topLine = line;
+ assert (topLine >= 0);
}
}
*/
// Document is in view, let's set cursorY
+ assert (line >= topLine);
setCursorY(line - topLine);
alignCursor();
}
* @param row the new editing row number. Row 1 is the first row.
*/
public void setEditingRowNumber(final int row) {
- document.setLineNumber(row - 1);
+ assert (row > 0);
+ if ((row > 0) && (row < document.getLineCount())) {
+ document.setLineNumber(row - 1);
+ alignTopLine(true);
+ }
}
/**
* column.
*/
public void setEditingColumnNumber(final int column) {
- document.setCursor(column - 1);
+ if ((column > 0) && (column < document.getLineLength())) {
+ document.setCursor(column - 1);
+ alignCursor();
+ }
}
/**
import jexer.bits.CellAttributes;
import jexer.bits.GraphicsChars;
import jexer.event.TCommandEvent;
+import jexer.event.TKeypressEvent;
import jexer.event.TMouseEvent;
import jexer.event.TResizeEvent;
import static jexer.TCommand.*;
* editor.
*
* @param mouse a mouse-based event
- * @return whether or not the mouse is on the emulator
+ * @return whether or not the mouse is on the editor
*/
private final boolean mouseOnEditor(final TMouseEvent mouse) {
if ((mouse.getAbsoluteX() >= getAbsoluteX() + 1)
// Vertical scrollbar actions
editField.setEditingRowNumber(getVerticalValue());
}
+ }
+ }
+
+ /**
+ * Handle mouse release events.
+ *
+ * @param mouse mouse button release event
+ */
+ @Override
+ public void onMouseUp(final TMouseEvent mouse) {
+ // Use TWidget's code to pass the event to the children.
+ super.onMouseUp(mouse);
+
+ if (mouse.isMouse1() && mouseOnVerticalScroller(mouse)) {
+ // Clicked on vertical scrollbar
+ editField.setEditingRowNumber(getVerticalValue());
+ }
+
+ // TODO: horizontal scrolling
+ }
+
+ /**
+ * Method that subclasses can override to handle mouse movements.
+ *
+ * @param mouse mouse motion event
+ */
+ @Override
+ public void onMouseMotion(final TMouseEvent mouse) {
+ // Use TWidget's code to pass the event to the children.
+ super.onMouseMotion(mouse);
+
+ if (mouseOnEditor(mouse) && mouse.isMouse1()) {
+ // The editor might have changed, update the scollbars.
+ setBottomValue(editField.getMaximumRowNumber());
+ setVerticalValue(editField.getEditingRowNumber());
+ setRightValue(editField.getMaximumColumnNumber());
+ setHorizontalValue(editField.getEditingColumnNumber());
+ } else {
+ if (mouse.isMouse1() && mouseOnVerticalScroller(mouse)) {
+ // Clicked/dragged on vertical scrollbar
+ editField.setEditingRowNumber(getVerticalValue());
+ }
+
// TODO: horizontal scrolling
}
+
+ }
+
+ /**
+ * Handle keystrokes.
+ *
+ * @param keypress keystroke event
+ */
+ @Override
+ public void onKeypress(final TKeypressEvent keypress) {
+ // Use TWidget's code to pass the event to the children.
+ super.onKeypress(keypress);
+
+ // The editor might have changed, update the scollbars.
+ setBottomValue(editField.getMaximumRowNumber());
+ setVerticalValue(editField.getEditingRowNumber());
+ setRightValue(editField.getMaximumColumnNumber());
+ setHorizontalValue(editField.getEditingColumnNumber());
}
/**
return null;
}
+ /**
+ * Get the maximum selection index value.
+ *
+ * @return -1 if the list is empty
+ */
+ public final int getMaxSelectedIndex() {
+ return strings.size() - 1;
+ }
+
/**
* Set the new list of strings to display.
*
*/
package jexer;
+import jexer.event.TMouseEvent;
import jexer.event.TResizeEvent;
/**
}
}
+ /**
+ * Check if a mouse press/release/motion event coordinate is over the
+ * vertical scrollbar.
+ *
+ * @param mouse a mouse-based event
+ * @return whether or not the mouse is on the scrollbar
+ */
+ protected final boolean mouseOnVerticalScroller(final TMouseEvent mouse) {
+ if (vScroller == null) {
+ return false;
+ }
+ if ((mouse.getAbsoluteX() == vScroller.getAbsoluteX())
+ && (mouse.getAbsoluteY() >= vScroller.getAbsoluteY())
+ && (mouse.getAbsoluteY() < vScroller.getAbsoluteY() +
+ vScroller.getHeight())
+ ) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if a mouse press/release/motion event coordinate is over the
+ * horizontal scrollbar.
+ *
+ * @param mouse a mouse-based event
+ * @return whether or not the mouse is on the scrollbar
+ */
+ protected final boolean mouseOnHorizontalScroller(final TMouseEvent mouse) {
+ if (hScroller == null) {
+ return false;
+ }
+ if ((mouse.getAbsoluteY() == hScroller.getAbsoluteY())
+ && (mouse.getAbsoluteX() >= hScroller.getAbsoluteX())
+ && (mouse.getAbsoluteX() < hScroller.getAbsoluteX() +
+ hScroller.getWidth())
+ ) {
+ return true;
+ }
+ return false;
+ }
+
}
*/
package jexer;
-import java.io.InputStream;
import java.io.IOException;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.List;
emulator = new ECMA48(deviceType, shell.getInputStream(),
shell.getOutputStream());
} catch (IOException e) {
- e.printStackTrace();
+ messageBox("Error", "Error launching shell: " + e.getMessage());
}
// Setup the scroll bars
}
}
- /**
- * Public constructor.
- *
- * @param application TApplication that manages this window
- * @param x column relative to parent
- * @param y row relative to parent
- * @param flags mask of CENTERED, MODAL, or RESIZABLE
- * @param input an InputStream connected to the remote side. For type ==
- * XTERM, input is converted to a Reader with UTF-8 encoding.
- * @param output an OutputStream connected to the remote user. For type
- * == XTERM, output is converted to a Writer with UTF-8 encoding.
- * @throws UnsupportedEncodingException if an exception is thrown when
- * creating the InputStreamReader
- */
- public TTerminalWindow(final TApplication application, final int x,
- final int y, final int flags, final InputStream input,
- final OutputStream output) throws UnsupportedEncodingException {
-
- super(application, "Terminal", x, y, 80 + 2, 24 + 2, flags);
-
- emulator = new ECMA48(ECMA48.DeviceType.XTERM, input, output);
-
- // Setup the scroll bars
- onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, getWidth(),
- getHeight()));
-
- // Claim the keystrokes the emulator will need.
- addShortcutKeys();
-
- // Add shortcut text
- newStatusBar("Terminal session executing...");
- }
-
/**
* Draw the display buffer.
*/
// General behavior -------------------------------------------------------
// ------------------------------------------------------------------------
+ /**
+ * See if this window is undergoing any movement/resize/etc.
+ *
+ * @return true if the window is moving
+ */
+ public boolean inMovements() {
+ if (inWindowResize || inWindowMove || inKeyboardResize) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Stop any pending movement/resize/etc.
+ */
+ public void stopMovements() {
+ inWindowResize = false;
+ inWindowMove = false;
+ inKeyboardResize = false;
+ }
+
/**
* Returns true if this window is modal.
*
public void flushPhysical() {
String result = flushString();
if ((cursorVisible)
+ && (cursorY >= 0)
+ && (cursorX >= 0)
&& (cursorY <= height - 1)
&& (cursorX <= width - 1)
) {
private void drawCursor(final Graphics gr) {
if (cursorVisible
+ && (cursorY >= 0)
+ && (cursorX >= 0)
&& (cursorY <= height - 1)
&& (cursorX <= width - 1)
&& cursorBlinkVisible
}
if (cursorVisible
+ && (cursorY >= 0)
+ && (cursorX >= 0)
&& (cursorY <= height - 1)
&& (cursorX <= width - 1)
) {
return n;
}
+ /**
+ * Get the current line length.
+ *
+ * @return the number of cells needed to display the current line
+ */
+ public int getLineLength() {
+ return lines.get(lineNumber).getDisplayLength();
+ }
+
}