0.0.3: FINISH PORTING
-- TTreeView
- - Also add keyboard navigation
- TDirectoryList
- Also add keyboard navigation
- TFileOpen
+ - Also add keyboard navigation
0.0.4: NEW STUFF
package jexer;
import java.io.InputStream;
+import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
// Draw the menu title
getScreen().hLineXY(x, 0, menu.getTitle().length() + 2, ' ',
menuColor);
- getScreen().putStrXY(x + 1, 0, menu.getTitle(), menuColor);
+ getScreen().putStringXY(x + 1, 0, menu.getTitle(), menuColor);
// Draw the highlight character
getScreen().putCharXY(x + 1 + menu.getMnemonic().getShortcutIdx(),
0, menu.getMnemonic().getShortcut(), menuMnemonicColor);
return new TTerminalWindow(this, x, y, flags);
}
+ /**
+ * Convenience function to spawn an file open box.
+ *
+ * @param path path of selected file
+ * @return the result of the new file open box
+ */
+ public final String fileOpenBox(final String path) throws IOException {
+
+ TFileOpenBox box = new TFileOpenBox(this, path, TFileOpenBox.Type.OPEN);
+ return box.getFilename();
+ }
+
+ /**
+ * Convenience function to spawn an file open box.
+ *
+ * @param path path of selected file
+ * @param type one of the Type constants
+ * @return the result of the new file open box
+ */
+ public final String fileOpenBox(final String path,
+ final TFileOpenBox.Type type) throws IOException {
+
+ TFileOpenBox box = new TFileOpenBox(this, path, type);
+ return box.getFilename();
+ }
+
}
if (inButtonPress) {
getScreen().putCharXY(1, 0, ' ', buttonColor);
- getScreen().putStrXY(2, 0, mnemonic.getRawLabel(), buttonColor);
+ getScreen().putStringXY(2, 0, mnemonic.getRawLabel(), buttonColor);
getScreen().putCharXY(getWidth() - 1, 0, ' ', buttonColor);
} else {
getScreen().putCharXY(0, 0, ' ', buttonColor);
- getScreen().putStrXY(1, 0, mnemonic.getRawLabel(), buttonColor);
+ getScreen().putStringXY(1, 0, mnemonic.getRawLabel(), buttonColor);
getScreen().putCharXY(getWidth() - 2, 0, ' ', buttonColor);
getScreen().putCharXY(getWidth() - 1, 0,
getScreen().putCharXY(1, 0, ' ', checkboxColor);
}
getScreen().putCharXY(2, 0, ']', checkboxColor);
- getScreen().putStrXY(4, 0, label, checkboxColor);
+ getScreen().putStringXY(4, 0, label, checkboxColor);
}
/**
--- /dev/null
+/**
+ * Jexer - Java Text User Interface
+ *
+ * License: LGPLv3 or later
+ *
+ * This module is licensed under the GNU Lesser General Public License
+ * Version 3. Please see the file "COPYING" in this directory for more
+ * information about the GNU Lesser General Public License Version 3.
+ *
+ * Copyright (C) 2015 Kevin Lamonte
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * http://www.gnu.org/licenses/, or write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import jexer.bits.CellAttributes;
+import jexer.event.TKeypressEvent;
+import jexer.event.TMouseEvent;
+import static jexer.TKeypress.*;
+
+/**
+ * TDirectoryList shows the files within a directory.
+ */
+public class TDirectoryList extends TWidget {
+
+ /**
+ * Files in the directory.
+ */
+ private List<File> files;
+
+ /**
+ * Selected file.
+ */
+ private int selectedFile = -1;
+
+ /**
+ * Root path containing files to display.
+ */
+ public File path;
+
+ /**
+ * Vertical scrollbar.
+ */
+ private TVScroller vScroller;
+
+ /**
+ * Horizontal scrollbar.
+ */
+ private THScroller hScroller;
+
+ /**
+ * Maximum width of a single line.
+ */
+ private int maxLineWidth;
+
+ /**
+ * The action to perform when the user selects an item.
+ */
+ private TAction action = null;
+
+ /**
+ * Perform user selection action.
+ */
+ public void dispatch() {
+ assert (selectedFile >= 0);
+ assert (selectedFile < files.size());
+ if (action != null) {
+ action.DO();
+ }
+ }
+
+ /**
+ * Format one of the entries for drawing on the screen.
+ *
+ * @param index index into files
+ * @return the line to draw
+ */
+ private String renderFile(int index) {
+ File file = files.get(index);
+ String name = file.getName();
+ if (name.length() > 20) {
+ name = name.substring(0, 17) + "...";
+ }
+ return String.format("%-20s %5dk", name, (file.length() / 1024));
+ }
+
+ /**
+ * Resize for a new width/height.
+ */
+ public void reflow() {
+
+ // Reset the lines
+ selectedFile = -1;
+ maxLineWidth = 0;
+ files.clear();
+
+ // Build a list of files in this directory
+ File [] newFiles = path.listFiles();
+ for (int i = 0; i < newFiles.length; i++) {
+ if (newFiles[i].getName().startsWith(".")) {
+ continue;
+ }
+ if (newFiles[i].isDirectory()) {
+ continue;
+ }
+ files.add(newFiles[i]);
+ }
+
+ for (int i = 0; i < files.size(); i++) {
+ String line = renderFile(i);
+ if (line.length() > maxLineWidth) {
+ maxLineWidth = line.length();
+ }
+ }
+
+ // Start at the top
+ if (vScroller == null) {
+ vScroller = new TVScroller(this, getWidth() - 1, 0,
+ getHeight() - 1);
+ } else {
+ vScroller.setX(getWidth() - 1);
+ vScroller.setHeight(getHeight() - 1);
+ }
+ vScroller.setBottomValue(files.size() - getHeight() - 1);
+ vScroller.setTopValue(0);
+ vScroller.setValue(0);
+ if (vScroller.getBottomValue() < 0) {
+ vScroller.setBottomValue(0);
+ }
+ vScroller.setBigChange(getHeight() - 1);
+
+ // Start at the left
+ if (hScroller == null) {
+ hScroller = new THScroller(this, 0, getHeight() - 1,
+ getWidth() - 1);
+ } else {
+ hScroller.setY(getHeight() - 1);
+ hScroller.setWidth(getWidth() - 1);
+ }
+ hScroller.setRightValue(maxLineWidth - getWidth() + 1);
+ hScroller.setLeftValue(0);
+ hScroller.setValue(0);
+ if (hScroller.getRightValue() < 0) {
+ hScroller.setRightValue(0);
+ }
+ hScroller.setBigChange(getWidth() - 1);
+ }
+
+ /**
+ * Public constructor.
+ *
+ * @param parent parent widget
+ * @param path directory path, must be a directory
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param width width of text area
+ * @param height height of text area
+ */
+ public TDirectoryList(final TWidget parent, final String path, final int x,
+ final int y, final int width, final int height) {
+
+ this(parent, path, x, y, width, height, null);
+ }
+
+ /**
+ * Public constructor.
+ *
+ * @param parent parent widget
+ * @param path directory path, must be a directory
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param width width of text area
+ * @param height height of text area
+ * @param action action to perform when an item is selected
+ */
+ public TDirectoryList(final TWidget parent, final String path, final int x,
+ final int y, final int width, final int height, final TAction action) {
+
+ this.path = new File(path);
+ this.action = action;
+ files = new ArrayList<File>();
+ reflow();
+ }
+
+ /**
+ * Draw the files list.
+ */
+ @Override
+ public void draw() {
+ CellAttributes color = null;
+ int begin = vScroller.getValue();
+ int topY = 0;
+ for (int i = begin; i < files.size() - 1; i++) {
+ String line = renderFile(i);
+ if (hScroller.getValue() < line.length()) {
+ line = line.substring(hScroller.getValue());
+ } else {
+ line = "";
+ }
+ if (i == selectedFile) {
+ color = getTheme().getColor("tdirectorylist.selected");
+ } else if (isAbsoluteActive()) {
+ color = getTheme().getColor("tdirectorylist");
+ } else {
+ color = getTheme().getColor("tdirectorylist.inactive");
+ }
+ String formatString = "%-" + Integer.toString(getWidth() - 1) + "s";
+ getScreen().putStringXY(0, topY, String.format(formatString, line),
+ color);
+ topY++;
+ if (topY >= getHeight() - 1) {
+ break;
+ }
+ }
+
+ // Pad the rest with blank lines
+ for (int i = topY; i < getHeight() - 1; i++) {
+ getScreen().hLineXY(0, i, getWidth() - 1, ' ', color);
+ }
+ }
+
+ /**
+ * Handle mouse press events.
+ *
+ * @param mouse mouse button press event
+ */
+ @Override
+ public void onMouseDown(final TMouseEvent mouse) {
+ if (mouse.isMouseWheelUp()) {
+ vScroller.decrement();
+ return;
+ }
+ if (mouse.isMouseWheelDown()) {
+ vScroller.increment();
+ return;
+ }
+
+ if ((mouse.getX() < getWidth() - 1)
+ && (mouse.getY() < getHeight() - 1)) {
+ if (vScroller.getValue() + mouse.getY() < files.size()) {
+ selectedFile = vScroller.getValue() + mouse.getY();
+ }
+ path = files.get(selectedFile);
+ dispatch();
+ return;
+ }
+
+ // Pass to children
+ super.onMouseDown(mouse);
+ }
+
+ /**
+ * Handle mouse release events.
+ *
+ * @param mouse mouse button release event
+ */
+ @Override
+ public void onMouseUp(final TMouseEvent mouse) {
+ // Pass to children
+ super.onMouseDown(mouse);
+ }
+
+ /**
+ * Handle keystrokes.
+ *
+ * @param keypress keystroke event
+ */
+ @Override
+ public void onKeypress(final TKeypressEvent keypress) {
+ if (keypress.equals(kbLeft)) {
+ hScroller.decrement();
+ } else if (keypress.equals(kbRight)) {
+ hScroller.increment();
+ } else if (keypress.equals(kbUp)) {
+ vScroller.decrement();
+ } else if (keypress.equals(kbDown)) {
+ vScroller.increment();
+ } else if (keypress.equals(kbPgUp)) {
+ vScroller.bigDecrement();
+ } else if (keypress.equals(kbPgDn)) {
+ vScroller.bigIncrement();
+ } else if (keypress.equals(kbHome)) {
+ vScroller.toTop();
+ } else if (keypress.equals(kbEnd)) {
+ vScroller.toBottom();
+ } else {
+ // Pass other keys (tab etc.) on
+ super.onKeypress(keypress);
+ }
+ }
+
+}
package jexer;
import java.io.File;
+import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.LinkedList;
*/
@Override
public void onExpand() {
+ // System.err.printf("onExpand() %s\n", dir);
+
if (dir == null) {
return;
}
return;
}
- // Refresh my child list
for (File file: dir.listFiles()) {
- if (file.getName().equals(".")) {
+ // System.err.printf(" -> file %s %s\n", file, file.getName());
+
+ if (file.getName().startsWith(".")) {
+ // Hide dot-files
continue;
}
if (!file.isDirectory()) {
continue;
}
- TDirectoryTreeItem item = new TDirectoryTreeItem(getTreeView(),
- file.getName(), false, false);
+ try {
+ TDirectoryTreeItem item = new TDirectoryTreeItem(getTreeView(),
+ file.getCanonicalPath(), false, false);
- item.level = this.level + 1;
- getChildren().add(item);
+ item.level = this.level + 1;
+ getChildren().add(item);
+ } catch (IOException e) {
+ continue;
+ }
}
Collections.sort(getChildren());
* @param view root TTreeView
* @param text text for this item
*/
- public TDirectoryTreeItem(final TTreeView view, final String text) {
+ public TDirectoryTreeItem(final TTreeView view,
+ final String text) throws IOException {
+
this(view, text, false, true);
}
* @param expanded if true, have it expanded immediately
*/
public TDirectoryTreeItem(final TTreeView view, final String text,
- final boolean expanded) {
+ final boolean expanded) throws IOException {
this(view, text, expanded, true);
}
* return the root path entry
*/
public TDirectoryTreeItem(final TTreeView view, final String text,
- final boolean expanded, final boolean openParents) {
+ final boolean expanded, final boolean openParents) throws IOException {
super(view, text, false);
- List<TDirectoryTreeItem> parentItems = new LinkedList<TDirectoryTreeItem>();
List<String> parentPaths = new LinkedList<String>();
boolean oldExpanded = expanded;
+ // Convert to canonical path
+ File rootPath = new File(text);
+ rootPath = rootPath.getCanonicalFile();
+
if (openParents == true) {
setExpanded(true);
// Go up the directory tree
- File rootPath = new File(text);
File parent = rootPath.getParentFile();
while (parent != null) {
parentPaths.add(rootPath.getName());
rootPath = rootPath.getParentFile();
parent = rootPath.getParentFile();
}
- setText(rootPath.getName());
+ }
+ dir = rootPath;
+ if (rootPath.getParentFile() == null) {
+ // This is a filesystem root, use its full name
+ setText(rootPath.getCanonicalPath());
} else {
- setText(text);
+ // This is a relative path. We got here because openParents was
+ // false.
+ assert (openParents == false);
+ setText(rootPath.getName());
}
-
- dir = new File(getText());
onExpand();
if (openParents == true) {
end = text.length();
}
getScreen().hLineXY(0, 0, getWidth(), GraphicsChars.HATCH, fieldColor);
- getScreen().putStrXY(0, 0, text.substring(windowStart, end),
+ getScreen().putStringXY(0, 0, text.substring(windowStart, end),
fieldColor);
// Fix the cursor, it will be rendered by TApplication.drawAll().
--- /dev/null
+/**
+ * Jexer - Java Text User Interface
+ *
+ * License: LGPLv3 or later
+ *
+ * This module is licensed under the GNU Lesser General Public License
+ * Version 3. Please see the file "COPYING" in this directory for more
+ * information about the GNU Lesser General Public License Version 3.
+ *
+ * Copyright (C) 2015 Kevin Lamonte
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * http://www.gnu.org/licenses/, or write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer;
+
+import java.io.File;
+import java.io.IOException;
+
+import jexer.bits.GraphicsChars;
+import jexer.event.TKeypressEvent;
+import static jexer.TKeypress.*;
+
+/**
+ * TFileOpenBox is a system-modal dialog for selecting a file to open. Call
+ * it like:
+ *
+ * <p>
+ * <pre>
+ * {@code
+ * filename = application.fileOpenBox("/path/to/file.ext",
+ * TFileOpenBox.Type.OPEN);
+ * if (filename != null) {
+ * ... the user selected a file, go open it ...
+ * }
+ * }
+ * </pre>
+ *
+ */
+public final class TFileOpenBox extends TWindow {
+
+ /**
+ * TFileOpenBox can be called for either Open or Save actions.
+ */
+ public enum Type {
+ OPEN,
+ SAVE
+ }
+
+ /**
+ * String to return, or null if the user canceled.
+ */
+ private String filename = null;
+
+ /**
+ * Get the return string.
+ *
+ * @return the filename the user selected, or null if they canceled.
+ */
+ public String getFilename() {
+ return filename;
+ }
+
+ /**
+ * The left-side tree view pane.
+ */
+ private TTreeView treeView;
+
+ /**
+ * The data behind treeView.
+ */
+ private TDirectoryTreeItem treeViewRoot;
+
+ /**
+ * The right-side directory list pane.
+ */
+ @SuppressWarnings("unused")
+ private TDirectoryList directoryList;
+
+ /**
+ * The top row text field.
+ */
+ private TField entryField;
+
+ /**
+ * The Open or Save button.
+ */
+ private TButton openButton;
+
+ /**
+ * Update the fields in response to other field updates.
+ *
+ * @param enter if true, the user manually entered a filename
+ */
+ @SuppressWarnings("unused")
+ private void onUpdate(boolean enter) throws IOException {
+ String newFilename = entryField.getText();
+ File newFile = new File(newFilename);
+ if (newFile.exists()) {
+ if (enter) {
+ if (newFile.isFile()) {
+ filename = entryField.getText();
+ getApplication().closeWindow(this);
+ }
+ if (newFile.isDirectory()) {
+ treeViewRoot = new TDirectoryTreeItem(treeView,
+ entryField.getText(), true);
+ treeView.setTreeRoot(treeViewRoot, true);
+ treeView.reflow();
+ }
+ openButton.setEnabled(false);
+ } else {
+ if (newFile.isFile()) {
+ openButton.setEnabled(true);
+ } else {
+ openButton.setEnabled(false);
+ }
+ }
+ } else {
+ openButton.setEnabled(false);
+ }
+ }
+
+ /**
+ * Public constructor. The file open box will be centered on screen.
+ *
+ * @param application the TApplication that manages this window
+ * @param path path of selected file
+ * @param type one of the Type constants
+ */
+ public TFileOpenBox(final TApplication application, final String path,
+ final Type type) throws IOException {
+
+ // Register with the TApplication
+ super(application, "", 0, 0, 76, 22, MODAL);
+
+ // Add text field
+ entryField = addField(1, 1, getWidth() - 4, false,
+ (new File(path)).getCanonicalPath(),
+ new TAction() {
+ public void DO() {}
+ }, null);
+
+ // Add directory treeView
+ treeView = addTreeView(1, 3, 30, getHeight() - 6,
+ new TAction() {
+ public void DO() {}
+ }
+ );
+ treeViewRoot = new TDirectoryTreeItem(treeView, path, true);
+
+ // Add directory files list
+ directoryList = addDirectoryList(path, 34, 3, 28, getHeight() - 6,
+ new TAction() {
+ public void DO() {}
+ }
+ );
+
+ String openLabel = "";
+ switch (type) {
+ case OPEN:
+ openLabel = " &Open ";
+ setTitle("Open File...");
+ break;
+ case SAVE:
+ openLabel = " &Save ";
+ setTitle("Save File...");
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid type: " + type);
+ }
+
+ // Setup button actions
+ openButton = addButton(openLabel, this.getWidth() - 12, 3,
+ new TAction() {
+ public void DO() {}
+ }
+ );
+ openButton.setEnabled(false);
+
+ addButton("&Cancel", getWidth() - 12, 5,
+ new TAction() {
+ public void DO() {
+ filename = null;
+ getApplication().closeWindow(TFileOpenBox.this);
+ }
+ }
+ );
+
+ // Set the secondaryFiber to run me
+ getApplication().enableSecondaryEventReceiver(this);
+
+ // Yield to the secondary thread. When I come back from the
+ // constructor response will already be set.
+ getApplication().yield();
+ }
+
+ /**
+ * Draw me on screen.
+ */
+ @Override
+ public void draw() {
+ super.draw();
+ getScreen().vLineXY(33, 4, getHeight() - 6, GraphicsChars.WINDOW_SIDE,
+ getBackground());
+ }
+
+ /**
+ * Handle keystrokes.
+ *
+ * @param keypress keystroke event
+ */
+ @Override
+ public void onKeypress(final TKeypressEvent keypress) {
+ // Escape - behave like cancel
+ if (keypress.equals(kbEsc)) {
+ // Close window
+ filename = null;
+ getApplication().closeWindow(this);
+ return;
+ }
+
+ // Pass to my parent
+ super.onKeypress(keypress);
+ }
+
+}
CellAttributes background = getWindow().getBackground();
color.setBackColor(background.getBackColor());
- getScreen().putStrXY(0, 0, label, color);
+ getScreen().putStringXY(0, 0, label, color);
}
}
getScreen().hLineXY(0, 0, getWidth() - 2, '*',
fieldColor);
} else {
- getScreen().putStrXY(0, 0, text.substring(windowStart, end),
+ getScreen().putStringXY(0, 0, text.substring(windowStart, end),
fieldColor);
}
getScreen().putCharXY(1, 0, ' ', radioButtonColor);
}
getScreen().putCharXY(2, 0, ')', radioButtonColor);
- getScreen().putStrXY(4, 0, label, radioButtonColor);
+ getScreen().putStringXY(4, 0, label, radioButtonColor);
}
/**
radioGroupColor, radioGroupColor, 3, false);
getScreen().hLineXY(1, 0, label.length() + 2, ' ', radioGroupColor);
- getScreen().putStrXY(2, 0, label, radioGroupColor);
+ getScreen().putStringXY(2, 0, label, radioGroupColor);
}
/**
int rc = shell.exitValue();
// The emulator exited on its own, all is fine
setTitle(String.format("%s [Completed - %d]",
- getTitle(), shell.exitValue()));
+ getTitle(), rc));
shell = null;
emulator.close();
} catch (IllegalThreadStateException e) {
line = "";
}
String formatString = "%-" + Integer.toString(getWidth() - 1) + "s";
- getScreen().putStrXY(0, topY, String.format(formatString, line),
+ getScreen().putStringXY(0, topY, String.format(formatString, line),
color);
topY++;
this.selectable = selectable;
}
+ /**
+ * Pointer to the previous keyboard-navigable item (kbUp). Note package
+ * private access.
+ */
+ TTreeItem keyboardPrevious = null;
+
+ /**
+ * Pointer to the next keyboard-navigable item (kbDown). Note package
+ * private access.
+ */
+ TTreeItem keyboardNext = null;
+
/**
* Public constructor.
*
*/
@Override
public void onMouseUp(final TMouseEvent mouse) {
- if ((mouse.getX() == (getExpanderX() - view.hScroller.getValue()))
+ if ((mouse.getX() == (getExpanderX() - view.getHScroller().getValue()))
&& (mouse.getY() == 0)
) {
if (selectable) {
}
}
+ /**
+ * Handle keystrokes.
+ *
+ * @param keypress keystroke event
+ */
+ @Override
+ public void onKeypress(final TKeypressEvent keypress) {
+ if (keypress.equals(kbLeft)
+ || keypress.equals(kbRight)
+ || keypress.equals(kbSpace)
+ ) {
+ if (selectable) {
+ // Flip expanded flag
+ expanded = !expanded;
+ if (expanded == false) {
+ // Unselect children that became invisible
+ unselect();
+ }
+ view.setSelected(this);
+ }
+ // Let subclasses do something with this
+ onExpand();
+ } else {
+ // Pass other keys (tab etc.) on to TWidget's handler.
+ super.onKeypress(keypress);
+ }
+ }
+
/**
* Draw this item to a window.
*/
return;
}
- int offset = -view.hScroller.getValue();
+ int offset = -view.getHScroller().getValue();
CellAttributes color = getTheme().getColor("ttreeview");
CellAttributes textColor = getTheme().getColor("ttreeview");
// Blank out the background
getScreen().hLineXY(0, 0, getWidth(), ' ', color);
- int expandX = 0;
String line = prefix;
if (level > 0) {
if (last) {
line += "[ ] ";
}
}
- getScreen().putStrXY(offset, 0, line, color);
+ getScreen().putStringXY(offset, 0, line, color);
if (selected) {
- getScreen().putStrXY(offset + line.length(), 0, text,
+ getScreen().putStringXY(offset + line.length(), 0, text,
selectedColor);
} else {
- getScreen().putStrXY(offset + line.length(), 0, text, textColor);
+ getScreen().putStringXY(offset + line.length(), 0, text, textColor);
}
if ((level > 0) && (expandable)) {
if (expanded) {
*/
package jexer;
-import jexer.bits.CellAttributes;
-import jexer.bits.GraphicsChars;
import jexer.event.TKeypressEvent;
import jexer.event.TMouseEvent;
import static jexer.TKeypress.*;
private TVScroller vScroller;
/**
- * Horizontal scrollbar. Note package private access.
+ * Horizontal scrollbar.
*/
- THScroller hScroller;
+ private THScroller hScroller;
+
+ /**
+ * Get the horizontal scrollbar. This is used by TTreeItem.draw(), and
+ * potentially subclasses.
+ */
+ public final THScroller getHScroller() {
+ return hScroller;
+ }
/**
* Root of the tree.
}
/**
- * Set the new selected tree view item. Note package private access.
+ * Set the new selected tree view item.
*
* @param item new item that became selected
*/
- void setSelected(final TTreeItem item) {
+ public void setSelected(final TTreeItem item) {
if (item != null) {
item.setSelected(true);
}
}
/**
- * Perform user selection action. Note package private access.
+ * Perform user selection action.
*/
- void dispatch() {
+ public void dispatch() {
if (action != null) {
action.DO();
}
TTreeItem item = (TTreeItem) widget;
item.setInvisible(true);
item.setEnabled(false);
+ item.keyboardPrevious = null;
+ item.keyboardNext = null;
}
}
// Expand the tree into a linear list
getChildren().clear();
getChildren().addAll(treeRoot.expandTree("", true));
+
+ // Locate the selected row and maximum line width
for (TWidget widget: getChildren()) {
TTreeItem item = (TTreeItem) widget;
maxLineWidth = lineWidth;
}
}
+
if ((centerWindow) && (foundSelectedRow)) {
if ((selectedRow < vScroller.getValue())
|| (selectedRow > vScroller.getValue() + getHeight() - 2)
int begin = vScroller.getValue();
int topY = 0;
+
+ // As we walk the list we also adjust next/previous pointers,
+ // resulting in a doubly-linked list but only of the expanded items.
+ TTreeItem p = null;
+
for (int i = 0; i < getChildren().size(); i++) {
if (!(getChildren().get(i) instanceof TTreeItem)) {
- // Skip
+ // Skip the scrollbars
continue;
}
TTreeItem item = (TTreeItem) getChildren().get(i);
+ if (p != null) {
+ item.keyboardPrevious = p;
+ p.keyboardNext = item;
+ }
+ p = item;
+
if (i < begin) {
// Render invisible
item.setEnabled(false);
item.setWidth(getWidth() - 1);
topY++;
}
+
}
/**
*/
@Override
public void onKeypress(final TKeypressEvent keypress) {
- if (keypress.equals(kbLeft)) {
+ if (keypress.equals(kbShiftLeft)
+ || keypress.equals(kbCtrlLeft)
+ || keypress.equals(kbAltLeft)
+ ) {
hScroller.decrement();
- } else if (keypress.equals(kbRight)) {
+ } else if (keypress.equals(kbShiftRight)
+ || keypress.equals(kbCtrlRight)
+ || keypress.equals(kbAltRight)
+ ) {
hScroller.increment();
- } else if (keypress.equals(kbUp)) {
+ } else if (keypress.equals(kbShiftUp)
+ || keypress.equals(kbCtrlUp)
+ || keypress.equals(kbAltUp)
+ ) {
vScroller.decrement();
- } else if (keypress.equals(kbDown)) {
+ } else if (keypress.equals(kbShiftDown)
+ || keypress.equals(kbCtrlDown)
+ || keypress.equals(kbAltDown)
+ ) {
vScroller.increment();
- } else if (keypress.equals(kbPgUp)) {
+ } else if (keypress.equals(kbShiftPgUp)
+ || keypress.equals(kbCtrlPgUp)
+ || keypress.equals(kbAltPgUp)
+ ) {
vScroller.bigDecrement();
- } else if (keypress.equals(kbPgDn)) {
+ } else if (keypress.equals(kbShiftPgDn)
+ || keypress.equals(kbCtrlPgDn)
+ || keypress.equals(kbAltPgDn)
+ ) {
vScroller.bigIncrement();
} else if (keypress.equals(kbHome)) {
vScroller.toTop();
if (selectedItem != null) {
dispatch();
}
+ } else if (keypress.equals(kbUp)) {
+ // Select the previous item
+ if (selectedItem != null) {
+ TTreeItem oldItem = selectedItem;
+ if (selectedItem.keyboardPrevious != null) {
+ setSelected(selectedItem.keyboardPrevious);
+ if (oldItem.getY() == 0) {
+ vScroller.decrement();
+ }
+ }
+ }
+ } else if (keypress.equals(kbDown)) {
+ // Select the next item
+ if (selectedItem != null) {
+ TTreeItem oldItem = selectedItem;
+ if (selectedItem.keyboardNext != null) {
+ setSelected(selectedItem.keyboardNext);
+ if (oldItem.getY() == getHeight() - 2) {
+ vScroller.increment();
+ }
+ }
+ }
+ } else if (selectedItem != null) {
+ // Give the TTreeItem a chance to handle arrow keys
+ selectedItem.onKeypress(keypress);
} else {
- // Pass other keys (tab etc.) on
+ // Pass other keys (tab etc.) on to TWidget's handler.
super.onKeypress(keypress);
}
*/
package jexer;
+import java.io.IOException;
import java.util.List;
import java.util.LinkedList;
* @return difference between this.tabOrder and that.tabOrder, or
* difference between this.z and that.z, or String.compareTo(text)
*/
- @Override
public final int compareTo(final TWidget that) {
if ((this instanceof TWindow)
&& (that instanceof TWindow)
return new TTreeView(this, x, y, width, height, action);
}
+ /**
+ * Convenience function to spawn a file open box.
+ *
+ * @param path path of selected file
+ * @return the result of the new file open box
+ */
+ public final String fileOpenBox(final String path) throws IOException {
+ return getApplication().fileOpenBox(path);
+ }
+
+ /**
+ * Convenience function to spawn a file open box.
+ *
+ * @param path path of selected file
+ * @param type one of the Type constants
+ * @return the result of the new file open box
+ */
+ public final String fileOpenBox(final String path,
+ final TFileOpenBox.Type type) throws IOException {
+
+ return getApplication().fileOpenBox(path, type);
+ }
+ /**
+ * Convenience function to add a directory list to this container/window.
+ *
+ * @param path directory path, must be a directory
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param width width of text area
+ * @param height height of text area
+ */
+ public final TDirectoryList addDirectoryList(final String path, final int x,
+ final int y, final int width, final int height) {
+
+ return new TDirectoryList(this, path, x, y, width, height, null);
+ }
+
+ /**
+ * Convenience function to add a directory list to this container/window.
+ *
+ * @param path directory path, must be a directory
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param width width of text area
+ * @param height height of text area
+ * @param action action to perform when an item is selected
+ */
+ public final TDirectoryList addDirectoryList(final String path, final int x,
+ final int y, final int width, final int height, final TAction action) {
+
+ return new TDirectoryList(this, path, x, y, width, height, action);
+ }
}
// Draw the title
int titleLeft = (getWidth() - title.length() - 2) / 2;
putCharXY(titleLeft, 0, ' ', border);
- putStrXY(titleLeft + 1, 0, title);
+ putStringXY(titleLeft + 1, 0, title);
putCharXY(titleLeft + title.length() + 1, 0, ' ', border);
if (isActive()) {
}
}
+ // Pass a resize event to my children
+ onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
+ getWidth(), getHeight()));
+
return;
}
* @param str string to draw
* @param attr attributes to use (bold, foreColor, backColor)
*/
- public final void putStrXY(final int x, final int y, final String str,
+ public final void putStringXY(final int x, final int y, final String str,
final CellAttributes attr) {
- getScreen().putStrXY(x, y, str, attr);
+ getScreen().putStringXY(x, y, str, attr);
}
/**
* @param y row coordinate. 0 is the top-most row.
* @param str string to draw
*/
- public final void putStrXY(final int x, final int y, final String str) {
- getScreen().putStrXY(x, y, str);
+ public final void putStringXY(final int x, final int y, final String str) {
+ getScreen().putStringXY(x, y, str);
}
/**
* @return Color.RED, Color.BLUE, etc.
*/
static Color getColor(final String colorName) {
- switch (colorName.toLowerCase()) {
- case "black":
+ String str = colorName.toLowerCase();
+
+ if (str.equals("black")) {
return Color.BLACK;
- case "white":
+ } else if (str.equals("white")) {
return Color.WHITE;
- case "red":
+ } else if (str.equals("red")) {
return Color.RED;
- case "cyan":
+ } else if (str.equals("cyan")) {
return Color.CYAN;
- case "green":
+ } else if (str.equals("green")) {
return Color.GREEN;
- case "magenta":
+ } else if (str.equals("magenta")) {
return Color.MAGENTA;
- case "blue":
+ } else if (str.equals("blue")) {
return Color.BLUE;
- case "yellow":
+ } else if (str.equals("yellow")) {
return Color.YELLOW;
- case "brown":
+ } else if (str.equals("brown")) {
return Color.YELLOW;
- default:
+ } else {
// Let unknown strings become white
return Color.WHITE;
}
*/
package jexer.demos;
-import jexer.*;
-import jexer.event.*;
-import jexer.menu.*;
+import jexer.TApplication;
/**
* This class is the main driver for a simple demonstration of Jexer's
import java.io.*;
import jexer.*;
-import jexer.event.*;
import jexer.menu.*;
/**
package jexer.demos;
import jexer.*;
-import jexer.event.*;
-import jexer.menu.*;
/**
* This window demonstates the TRadioGroup, TRadioButton, and TCheckbox
package jexer.demos;
import jexer.*;
-import jexer.event.*;
-import jexer.menu.*;
/**
* This is the main "demo" application window. It makes use of the TTimer,
addButton("Tree&View", 35, row,
new TAction() {
public void DO() {
- new DemoTreeViewWindow(getApplication());
+ try {
+ new DemoTreeViewWindow(getApplication());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
}
}
);
package jexer.demos;
import jexer.*;
-import jexer.event.*;
-import jexer.menu.*;
/**
* This window demonstates the TMessageBox and TInputBox widgets.
import jexer.*;
import jexer.event.*;
-import jexer.menu.*;
/**
* This window demonstates the TText, THScroller, and TVScroller widgets.
*/
package jexer.demos;
+import java.io.IOException;
+
import jexer.*;
import jexer.event.*;
-import jexer.menu.*;
/**
* This window demonstates the TTreeView widget.
*
* @param parent the main application
*/
- public DemoTreeViewWindow(TApplication parent) {
+ public DemoTreeViewWindow(TApplication parent) throws IOException {
super(parent, "Tree View", 0, 0, 44, 16, TWindow.RESIZABLE);
// Load the treeview with "stuff"
treeView = addTreeView(1, 1, 40, 12);
- TDirectoryTreeItem root = new TDirectoryTreeItem(treeView, ".", true);
+ new TDirectoryTreeItem(treeView, ".", true);
}
/**
return "\033[?1036l";
}
- /**
- * Convert a list of SGR parameters into a full escape sequence. This
- * also eliminates a trailing ';' which would otherwise reset everything
- * to white-on-black not-bold.
- *
- * @param str string of parameters, e.g. "31;1;"
- * @return the string to emit to an ANSI / ECMA-style terminal,
- * e.g. "\033[31;1m"
- */
- private String addHeaderSGR(String str) {
- if (str.length() > 0) {
- // Nix any trailing ';' because that resets all attributes
- while (str.endsWith(":")) {
- str = str.substring(0, str.length() - 1);
- }
- }
- return "\033[" + str + "m";
- }
-
/**
* Create a SGR parameter sequence for a single color change. Note
* package private access.
return sb.toString();
}
- /**
- * Create a SGR parameter sequence for enabling reverse color.
- *
- * @param on if true, turn on reverse
- * @return the string to emit to an ANSI / ECMA-style terminal,
- * e.g. "\033[7m"
- */
- private String reverse(final boolean on) {
- if (on) {
- return "\033[7m";
- }
- return "\033[27m";
- }
-
/**
* Create a SGR parameter sequence to reset to defaults. Note package
* private access.
return "0;37;40";
}
- /**
- * Create a SGR parameter sequence for enabling boldface.
- *
- * @param on if true, turn on bold
- * @return the string to emit to an ANSI / ECMA-style terminal,
- * e.g. "\033[1m"
- */
- private String bold(final boolean on) {
- return bold(on, true);
- }
-
- /**
- * Create a SGR parameter sequence for enabling boldface.
- *
- * @param on if true, turn on bold
- * @param header if true, make the full header, otherwise just emit the
- * bare parameter e.g. "1;"
- * @return the string to emit to an ANSI / ECMA-style terminal,
- * e.g. "\033[1m"
- */
- private String bold(final boolean on, final boolean header) {
- if (header) {
- if (on) {
- return "\033[1m";
- }
- return "\033[22m";
- }
- if (on) {
- return "1;";
- }
- return "22;";
- }
-
- /**
- * Create a SGR parameter sequence for enabling blinking text.
- *
- * @param on if true, turn on blink
- * @return the string to emit to an ANSI / ECMA-style terminal,
- * e.g. "\033[5m"
- */
- private String blink(final boolean on) {
- return blink(on, true);
- }
-
- /**
- * Create a SGR parameter sequence for enabling blinking text.
- *
- * @param on if true, turn on blink
- * @param header if true, make the full header, otherwise just emit the
- * bare parameter e.g. "5;"
- * @return the string to emit to an ANSI / ECMA-style terminal,
- * e.g. "\033[5m"
- */
- private String blink(final boolean on, final boolean header) {
- if (header) {
- if (on) {
- return "\033[5m";
- }
- return "\033[25m";
- }
- if (on) {
- return "5;";
- }
- return "25;";
- }
-
- /**
- * Create a SGR parameter sequence for enabling underline / underscored
- * text.
- *
- * @param on if true, turn on underline
- * @return the string to emit to an ANSI / ECMA-style terminal,
- * e.g. "\033[4m"
- */
- private String underline(final boolean on) {
- if (on) {
- return "\033[4m";
- }
- return "\033[24m";
- }
-
/**
* Create a SGR parameter sequence for enabling the visible cursor. Note
* package private access.
return "\033[0;37;40m\033[K";
}
- /**
- * Clear the line up the cursor (inclusive). Because some terminals use
- * back-color-erase, set the color to white-on-black beforehand.
- *
- * @return the string to emit to an ANSI / ECMA-style terminal
- */
- private String clearPreceedingLine() {
- return "\033[0;37;40m\033[1K";
- }
-
- /**
- * Clear the line. Because some terminals use back-color-erase, set the
- * color to white-on-black beforehand.
- *
- * @return the string to emit to an ANSI / ECMA-style terminal
- */
- private String clearLine() {
- return "\033[0;37;40m\033[2K";
- }
-
- /**
- * Move the cursor to the top-left corner.
- *
- * @return the string to emit to an ANSI / ECMA-style terminal
- */
- private String home() {
- return "\033[H";
- }
-
/**
* Move the cursor to (x, y). Note package private access.
*
* @param str string to draw
* @param attr attributes to use (bold, foreColor, backColor)
*/
- public final void putStrXY(final int x, final int y, final String str,
+ public final void putStringXY(final int x, final int y, final String str,
final CellAttributes attr) {
int i = x;
* @param y row coordinate. 0 is the top-most row.
* @param str string to draw
*/
- public final void putStrXY(final int x, final int y, final String str) {
+ public final void putStringXY(final int x, final int y, final String str) {
int i = x;
for (int j = 0; j < str.length(); j++) {
getFontDimensions();
// Setup double-buffering
- if (screen.doubleBuffer) {
+ if (SwingScreen.doubleBuffer) {
setIgnoreRepaint(true);
createBufferStrategy(2);
bufferStrategy = getBufferStrategy();
if (reallyCleared) {
// Really refreshed, do it all
- if (doubleBuffer) {
+ if (SwingScreen.doubleBuffer) {
Graphics gr = frame.bufferStrategy.getDrawGraphics();
frame.paint(gr);
gr.dispose();
// Repaint the desired area
// System.err.printf("REPAINT X %d %d Y %d %d\n", xMin, xMax,
// yMin, yMax);
- if (doubleBuffer) {
+ if (SwingScreen.doubleBuffer) {
Graphics gr = frame.bufferStrategy.getDrawGraphics();
Rectangle bounds = new Rectangle(xMin, yMin, xMax - xMin,
yMax - yMin);
*
* @param key keystroke received
*/
- @Override
public void keyReleased(final KeyEvent key) {
// Ignore release events
}
*
* @param key keystroke received
*/
- @Override
public void keyTyped(final KeyEvent key) {
// Ignore typed events
}
*
* @param key keystroke received
*/
- @Override
public void keyPressed(final KeyEvent key) {
boolean alt = false;
boolean shift = false;
*
* @param event window event received
*/
- @Override
public void windowActivated(final WindowEvent event) {
// Force a total repaint
synchronized (screen) {
*
* @param event window event received
*/
- @Override
public void windowClosed(final WindowEvent event) {
// Ignore
}
*
* @param event window event received
*/
- @Override
public void windowClosing(final WindowEvent event) {
// Drop a cmAbort and walk away
synchronized (eventQueue) {
*
* @param event window event received
*/
- @Override
public void windowDeactivated(final WindowEvent event) {
// Ignore
}
*
* @param event window event received
*/
- @Override
public void windowDeiconified(final WindowEvent event) {
// Ignore
}
*
* @param event window event received
*/
- @Override
public void windowIconified(final WindowEvent event) {
// Ignore
}
*
* @param event window event received
*/
- @Override
public void windowOpened(final WindowEvent event) {
// Ignore
}
*
* @param event component event received
*/
- @Override
public void componentHidden(final ComponentEvent event) {
// Ignore
}
*
* @param event component event received
*/
- @Override
public void componentShown(final ComponentEvent event) {
// Ignore
}
*
* @param event component event received
*/
- @Override
public void componentMoved(final ComponentEvent event) {
// Ignore
}
*
* @param event component event received
*/
- @Override
public void componentResized(final ComponentEvent event) {
// Drop a new TResizeEvent into the queue
sessionInfo.queryWindowSize();
*
* @param mouse mouse event received
*/
- @Override
public void mouseDragged(final MouseEvent mouse) {
int modifiers = mouse.getModifiersEx();
boolean eventMouse1 = false;
*
* @param mouse mouse event received
*/
- @Override
public void mouseMoved(final MouseEvent mouse) {
int x = screen.textColumn(mouse.getX());
int y = screen.textRow(mouse.getY());
*
* @param mouse mouse event received
*/
- @Override
public void mouseClicked(final MouseEvent mouse) {
// Ignore
}
*
* @param mouse mouse event received
*/
- @Override
public void mouseEntered(final MouseEvent mouse) {
// Ignore
}
*
* @param mouse mouse event received
*/
- @Override
public void mouseExited(final MouseEvent mouse) {
// Ignore
}
*
* @param mouse mouse event received
*/
- @Override
public void mousePressed(final MouseEvent mouse) {
int modifiers = mouse.getModifiersEx();
boolean eventMouse1 = false;
*
* @param mouse mouse event received
*/
- @Override
public void mouseReleased(final MouseEvent mouse) {
int modifiers = mouse.getModifiersEx();
boolean eventMouse1 = false;
*
* @param mouse mouse event received
*/
- @Override
public void mouseWheelMoved(final MouseWheelEvent mouse) {
int modifiers = mouse.getModifiersEx();
boolean eventMouse1 = false;
getScreen().vLineXY(getWidth() - 1, 0, 1, cVSide, background);
getScreen().hLineXY(1, 0, getWidth() - 2, ' ', menuColor);
- getScreen().putStrXY(2, 0, mnemonic.getRawLabel(), menuColor);
+ getScreen().putStringXY(2, 0, mnemonic.getRawLabel(), menuColor);
if (hasKey) {
String keyLabel = key.toString();
- getScreen().putStrXY((getWidth() - keyLabel.length() - 2), 0,
+ getScreen().putStringXY((getWidth() - keyLabel.length() - 2), 0,
keyLabel, menuColor);
}
if (mnemonic.getShortcutIdx() >= 0) {
* @param option the telnet option byte
* @return a string describing the telnet option code
*/
+ @SuppressWarnings("unused")
private String optionString(final int option) {
switch (option) {
case 0: return "Binary Transmission";
for (int i = 2; i < subnegBuffer.size(); i++) {
speedString.append((char)subnegBuffer.get(i).byteValue());
}
- String termSpeed = speedString.toString();
master.terminalSpeed = speedString.toString();
}
break;
import java.io.OutputStream;
import java.io.IOException;
+import static jexer.net.TelnetSocket.*;
+
/**
* TelnetOutputStream works with TelnetSocket to perform the telnet protocol.
*/
// The last byte sent to this.write() was a CR, which was never
// actually sent. So send the CR in ascii mode, then flush.
// CR <anything> -> CR NULL
- output.write(master.C_CR);
- output.write(master.C_NUL);
+ output.write(C_CR);
+ output.write(C_NUL);
writeCR = false;
}
output.flush();
if (master.binaryMode == true) {
- if (ch == master.TELNET_IAC) {
+ if (ch == TELNET_IAC) {
// IAC -> IAC IAC
- writeBuffer[writeBufferI++] = (byte)master.TELNET_IAC;
- writeBuffer[writeBufferI++] = (byte)master.TELNET_IAC;
+ writeBuffer[writeBufferI++] = (byte)TELNET_IAC;
+ writeBuffer[writeBufferI++] = (byte)TELNET_IAC;
} else {
// Anything else -> just send
writeBuffer[writeBufferI++] = ch;
// the case that the last byte of b was a CR.
// Bare carriage return -> CR NUL
- if (ch == master.C_CR) {
+ if (ch == C_CR) {
if (writeCR == true) {
// Flush the previous CR to the stream.
// CR <anything> -> CR NULL
- writeBuffer[writeBufferI++] = (byte)master.C_CR;
- writeBuffer[writeBufferI++] = (byte)master.C_NUL;
+ writeBuffer[writeBufferI++] = (byte)C_CR;
+ writeBuffer[writeBufferI++] = (byte)C_NUL;
}
writeCR = true;
- } else if (ch == master.C_LF) {
+ } else if (ch == C_LF) {
if (writeCR == true) {
// CR LF -> CR LF
- writeBuffer[writeBufferI++] = (byte)master.C_CR;
- writeBuffer[writeBufferI++] = (byte)master.C_LF;
+ writeBuffer[writeBufferI++] = (byte)C_CR;
+ writeBuffer[writeBufferI++] = (byte)C_LF;
writeCR = false;
} else {
// Bare LF -> LF
writeBuffer[writeBufferI++] = ch;
}
- } else if (ch == master.TELNET_IAC) {
+ } else if (ch == TELNET_IAC) {
if (writeCR == true) {
// CR <anything> -> CR NULL
- writeBuffer[writeBufferI++] = (byte)master.C_CR;
- writeBuffer[writeBufferI++] = (byte)master.C_NUL;
+ writeBuffer[writeBufferI++] = (byte)C_CR;
+ writeBuffer[writeBufferI++] = (byte)C_NUL;
writeCR = false;
}
// IAC -> IAC IAC
- writeBuffer[writeBufferI++] = (byte)master.TELNET_IAC;
- writeBuffer[writeBufferI++] = (byte)master.TELNET_IAC;
+ writeBuffer[writeBufferI++] = (byte)TELNET_IAC;
+ writeBuffer[writeBufferI++] = (byte)TELNET_IAC;
} else {
if (writeCR == true) {
// CR <anything> -> CR NULL
- writeBuffer[writeBufferI++] = (byte)master.C_CR;
- writeBuffer[writeBufferI++] = (byte)master.C_NUL;
+ writeBuffer[writeBufferI++] = (byte)C_CR;
+ writeBuffer[writeBufferI++] = (byte)C_NUL;
writeCR = false;
} else {
// Normal character */
* Whether number pad keys send VT100 or VT52, application or numeric
* sequences.
*/
+ @SuppressWarnings("unused")
private KeypadMode keypadMode;
/**