TTreeView compiles
authorKevin Lamonte <kevin.lamonte@gmail.com>
Fri, 27 Mar 2015 20:18:05 +0000 (16:18 -0400)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Fri, 27 Mar 2015 20:18:05 +0000 (16:18 -0400)
src/jexer/TDirectoryTreeItem.java [new file with mode: 0644]
src/jexer/TTreeItem.java [new file with mode: 0644]
src/jexer/TTreeView.java [new file with mode: 0644]
src/jexer/TWidget.java
src/jexer/demos/DemoApplication.java
src/jexer/demos/DemoCheckboxWindow.java
src/jexer/demos/DemoMainWindow.java
src/jexer/demos/DemoMsgBoxWindow.java
src/jexer/demos/DemoTextWindow.java
src/jexer/demos/DemoTreeViewWindow.java [new file with mode: 0644]

diff --git a/src/jexer/TDirectoryTreeItem.java b/src/jexer/TDirectoryTreeItem.java
new file mode 100644 (file)
index 0000000..25a33df
--- /dev/null
@@ -0,0 +1,187 @@
+/**
+ * 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.Collections;
+import java.util.List;
+import java.util.LinkedList;
+
+/**
+ * TDirectoryTreeItem is a single item in a disk directory tree view.
+ */
+public class TDirectoryTreeItem extends TTreeItem {
+
+    /**
+     * Directory entry corresponding to this list item.
+     */
+    File dir;
+
+    /**
+     * Called when this item is expanded or collapsed.  this.expanded will be
+     * true if this item was just expanded from a mouse click or keypress.
+     */
+    @Override
+    public void onExpand() {
+        if (dir == null) {
+            return;
+        }
+        getChildren().clear();
+
+        // Make sure we can read it before trying to.
+        if (dir.canRead()) {
+            setSelectable(true);
+        } else {
+            setSelectable(false);
+        }
+        assert (dir.isDirectory());
+        setExpandable(true);
+
+        if ((isExpanded() == false) || (isExpandable() == false)) {
+            getTreeView().reflow();
+            return;
+        }
+
+        // Refresh my child list
+        for (File file: dir.listFiles()) {
+            if (file.getName().equals(".")) {
+                continue;
+            }
+            if (!file.isDirectory()) {
+                continue;
+            }
+
+            TDirectoryTreeItem item = new TDirectoryTreeItem(getTreeView(),
+                file.getName(), false, false);
+
+            item.level = this.level + 1;
+            getChildren().add(item);
+        }
+        Collections.sort(getChildren());
+
+        getTreeView().reflow();
+    }
+
+    /**
+     * Add a child item.  This method should never be used, it will throw an
+     * IllegalArgumentException every time.
+     *
+     * @param text text for this item
+     * @param expanded if true, have it expanded immediately
+     * @return the new item
+     * @throws IllegalArgumentException if this function is called
+     */
+    @Override
+    public final TTreeItem addChild(final String text, final boolean expanded) {
+        throw new IllegalArgumentException("Do not call addChild(), use onExpand() instead");
+    }
+
+    /**
+     * Public constructor.
+     *
+     * @param view root TTreeView
+     * @param text text for this item
+     */
+    public TDirectoryTreeItem(final TTreeView view, final String text) {
+        this(view, text, false, true);
+    }
+
+    /**
+     * Public constructor.
+     *
+     * @param view root TTreeView
+     * @param text text for this item
+     * @param expanded if true, have it expanded immediately
+     */
+    public TDirectoryTreeItem(final TTreeView view, final String text,
+        final boolean expanded) {
+
+        this(view, text, expanded, true);
+    }
+
+    /**
+     * Public constructor.
+     *
+     * @param view root TTreeView
+     * @param text text for this item
+     * @param expanded if true, have it expanded immediately
+     * @param openParents if true, expand all paths up the root path and
+     * return the root path entry
+     */
+    public TDirectoryTreeItem(final TTreeView view, final String text,
+        final boolean expanded, final boolean openParents) {
+
+        super(view, text, false);
+
+        List<TDirectoryTreeItem> parentItems = new LinkedList<TDirectoryTreeItem>();
+        List<String> parentPaths = new LinkedList<String>();
+        boolean oldExpanded = expanded;
+
+        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());
+        } else {
+            setText(text);
+        }
+
+        dir = new File(getText());
+        onExpand();
+
+        if (openParents == true) {
+            TDirectoryTreeItem childPath = this;
+            Collections.reverse(parentPaths);
+            for (String p: parentPaths) {
+                for (TWidget widget: childPath.getChildren()) {
+                    TDirectoryTreeItem child = (TDirectoryTreeItem) widget;
+                    if (child.getText().equals(p)) {
+                        childPath = child;
+                        childPath.setExpanded(true);
+                        childPath.onExpand();
+                        break;
+                    }
+                }
+            }
+            unselect();
+            getTreeView().setSelected(childPath);
+            setExpanded(oldExpanded);
+        }
+        getTreeView().reflow();
+    }
+}
diff --git a/src/jexer/TTreeItem.java b/src/jexer/TTreeItem.java
new file mode 100644 (file)
index 0000000..e97ea0e
--- /dev/null
@@ -0,0 +1,418 @@
+/**
+ * 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.util.ArrayList;
+import java.util.List;
+
+import jexer.bits.CellAttributes;
+import jexer.bits.GraphicsChars;
+import jexer.event.TKeypressEvent;
+import jexer.event.TMouseEvent;
+import static jexer.TKeypress.*;
+
+/**
+ * TTreeItem is a single item in a tree view.
+ */
+public class TTreeItem extends TWidget {
+
+    /**
+     * Hang onto reference to my parent TTreeView so I can call its reflow()
+     * when I add a child node.
+     */
+    private TTreeView view;
+
+    /**
+     * Get the parent TTreeView.
+     *
+     * @return the parent TTreeView
+     */
+    public final TTreeView getTreeView() {
+        return view;
+    }
+
+    /**
+     * Displayable text for this item.
+     */
+    private String text;
+
+    /**
+     * Get the displayable text for this item.
+     *
+     * @return the displayable text for this item
+     */
+    public final String getText() {
+        return text;
+    }
+
+    /**
+     * Set the displayable text for this item.
+     *
+     * @param the displayable text for this item
+     */
+    public final void setText(final String text) {
+        this.text = text;
+    }
+
+    /**
+     * If true, this item is expanded in the tree view.
+     */
+    private boolean expanded = true;
+
+    /**
+     * Get expanded value.
+     *
+     * @return if true, this item is expanded
+     */
+    public final boolean isExpanded() {
+        return expanded;
+    }
+
+    /**
+     * Set expanded value.
+     *
+     * @param expanded new value
+     */
+    public final void setExpanded(boolean expanded) {
+        this.expanded = expanded;
+    }
+
+    /**
+     * If true, this item can be expanded in the tree view.
+     */
+    private boolean expandable = false;
+
+    /**
+     * Get expandable value.
+     *
+     * @return if true, this item is expandable
+     */
+    public final boolean isExpandable() {
+        return expandable;
+    }
+
+    /**
+     * Set expandable value.
+     *
+     * @param expandable new value
+     */
+    public final void setExpandable(boolean expandable) {
+        this.expandable = expandable;
+    }
+
+    /**
+     * The vertical bars and such along the left side.
+     */
+    private String prefix = "";
+
+    /**
+     * Get the vertical bars and such along the left side.
+     *
+     * @return the vertical bars and such along the left side
+     */
+    public final String getPrefix() {
+        return prefix;
+    }
+
+    /**
+     * Whether or not this item is last in its parent's list of children.
+     */
+    private boolean last = false;
+
+    /**
+     * Tree level.  Note package private access.
+     */
+    int level = 0;
+
+    /**
+     * If true, this item will not be drawn.
+     */
+    private boolean invisible = false;
+
+    /**
+     * Set invisible value.
+     *
+     * @param invisible new value
+     */
+    public final void setInvisible(boolean invisible) {
+        this.invisible = invisible;
+    }
+
+    /**
+     * True means selected.
+     */
+    private boolean selected = false;
+
+    /**
+     * Get selected value.
+     *
+     * @return if true, this item is selected
+     */
+    public final boolean isSelected() {
+        return selected;
+    }
+
+    /**
+     * Set selected value.
+     *
+     * @param selected new value
+     */
+    public final void setSelected(boolean selected) {
+        this.selected = selected;
+    }
+
+    /**
+     * True means select-able.
+     */
+    private boolean selectable = true;
+
+    /**
+     * Set selectable value.
+     *
+     * @param selectable new value
+     */
+    public final void setSelectable(boolean selectable) {
+        this.selectable = selectable;
+    }
+
+    /**
+     * Public constructor.
+     *
+     * @param view root TTreeView
+     * @param text text for this item
+     * @param expanded if true, have it expanded immediately
+     */
+    public TTreeItem(final TTreeView view, final String text,
+        final boolean expanded) {
+
+        super(view, 0, 0, view.getWidth() - 3, 1);
+        this.text = text;
+        this.expanded = expanded;
+        this.view = view;
+
+        if (view.getTreeRoot() == null) {
+            view.setTreeRoot(this, true);
+        }
+
+        view.reflow();
+    }
+
+    /**
+     * Add a child item.
+     *
+     * @param text text for this item
+     * @return the new child item
+     */
+    public TTreeItem addChild(final String text) {
+        return addChild(text, true);
+    }
+
+    /**
+     * Add a child item.
+     *
+     * @param text text for this item
+     * @param expanded if true, have it expanded immediately
+     * @return the new child item
+     */
+    public TTreeItem addChild(final String text, final boolean expanded) {
+        TTreeItem item = new TTreeItem(view, text, expanded);
+        item.level = this.level + 1;
+        getChildren().add(item);
+        view.reflow();
+        return item;
+    }
+
+    /**
+     * Recursively expand the tree into a linear array of items.
+     *
+     * @param prefix = vertical bar of parent levels and such that is set on
+     * each child
+     * @param last = if true, this is the "last" leaf node of a tree
+     * @param additional items to add to the array
+     */
+    public List<TTreeItem> expandTree(final String prefix, final boolean last) {
+        List<TTreeItem> array = new ArrayList<TTreeItem>();
+        this.last = last;
+        this.prefix = prefix;
+        array.add(this);
+
+        if ((getChildren().size() == 0) || (expanded == false)) {
+            return array;
+        }
+
+        String newPrefix = prefix;
+        if (level > 0) {
+            if (last) {
+                newPrefix += "  ";
+            } else {
+                newPrefix += GraphicsChars.CP437[0xB3];
+                newPrefix += ' ';
+            }
+        }
+        for (int i = 0; i < getChildren().size(); i++) {
+            TTreeItem item = (TTreeItem) getChildren().get(i);
+            if (i == getChildren().size() - 1) {
+                array.addAll(item.expandTree(newPrefix, true));
+            } else {
+                array.addAll(item.expandTree(newPrefix, false));
+            }
+        }
+        return array;
+    }
+
+    /**
+     * Get the x spot for the + or - to expand/collapse.
+     *
+     * @return column of the expand/collapse button
+     */
+    private int getExpanderX() {
+        if ((level == 0) || (!expandable)) {
+            return 0;
+        }
+        return prefix.length() + 3;
+    }
+
+    /**
+     * Recursively unselect my or my children.
+     */
+    public void unselect() {
+        if (selected == true) {
+            selected = false;
+            view.setSelected(null);
+        }
+        for (TWidget widget: getChildren()) {
+            if (widget instanceof TTreeItem) {
+                TTreeItem item = (TTreeItem) widget;
+                item.unselect();
+            }
+        }
+    }
+
+    /**
+     * Handle mouse release events.
+     *
+     * @param mouse mouse button release event
+     */
+    @Override
+    public void onMouseUp(final TMouseEvent mouse) {
+        if ((mouse.getX() == (getExpanderX() - view.hScroller.getValue()))
+            && (mouse.getY() == 0)
+        ) {
+            if (selectable) {
+                // Flip expanded flag
+                expanded = !expanded;
+                if (expanded == false) {
+                    // Unselect children that became invisible
+                    unselect();
+                }
+            }
+            // Let subclasses do something with this
+            onExpand();
+        } else if (mouse.getY() == 0) {
+            view.setSelected(this);
+            view.dispatch();
+        }
+
+        // Update the screen after any thing has expanded/contracted
+        view.reflow();
+    }
+
+    /**
+     * Called when this item is expanded or collapsed.  this.expanded will be
+     * true if this item was just expanded from a mouse click or keypress.
+     */
+    public void onExpand() {
+        // Default: do nothing.
+        if (!expandable) {
+            return;
+        }
+    }
+
+    /**
+     * Draw this item to a window.
+     */
+    @Override
+    public void draw() {
+        if (invisible) {
+            return;
+        }
+
+        int offset = -view.hScroller.getValue();
+
+        CellAttributes color = getTheme().getColor("ttreeview");
+        CellAttributes textColor = getTheme().getColor("ttreeview");
+        CellAttributes expanderColor = getTheme().getColor("ttreeview.expandbutton");
+        CellAttributes selectedColor = getTheme().getColor("ttreeview.selected");
+
+        if (!getParent().isAbsoluteActive()) {
+            color = getTheme().getColor("ttreeview.inactive");
+            textColor = getTheme().getColor("ttreeview.inactive");
+        }
+
+        if (!selectable) {
+            textColor = getTheme().getColor("ttreeview.unreadable");
+        }
+
+        // Blank out the background
+        getScreen().hLineXY(0, 0, getWidth(), ' ', color);
+
+        int expandX = 0;
+        String line = prefix;
+        if (level > 0) {
+            if (last) {
+                line += GraphicsChars.CP437[0xC0];
+            } else {
+                line += GraphicsChars.CP437[0xC3];
+            }
+            line += GraphicsChars.CP437[0xC4];
+            if (expandable) {
+                line += "[ ] ";
+            }
+        }
+        getScreen().putStrXY(offset, 0, line, color);
+        if (selected) {
+            getScreen().putStrXY(offset + line.length(), 0, text,
+                selectedColor);
+        } else {
+            getScreen().putStrXY(offset + line.length(), 0, text, textColor);
+        }
+        if ((level > 0) && (expandable)) {
+            if (expanded) {
+                getScreen().putCharXY(offset + getExpanderX(), 0, '-',
+                    expanderColor);
+            } else {
+                getScreen().putCharXY(offset + getExpanderX(), 0, '+',
+                    expanderColor);
+            }
+        }
+    }
+
+}
diff --git a/src/jexer/TTreeView.java b/src/jexer/TTreeView.java
new file mode 100644 (file)
index 0000000..8c710f3
--- /dev/null
@@ -0,0 +1,384 @@
+/**
+ * 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 jexer.bits.CellAttributes;
+import jexer.bits.GraphicsChars;
+import jexer.event.TKeypressEvent;
+import jexer.event.TMouseEvent;
+import static jexer.TKeypress.*;
+
+/**
+ * TTreeView implements a simple tree view.
+ */
+public class TTreeView extends TWidget {
+
+    /**
+     * Vertical scrollbar.
+     */
+    private TVScroller vScroller;
+
+    /**
+     * Horizontal scrollbar.  Note package private access.
+     */
+    THScroller hScroller;
+
+    /**
+     * Root of the tree.
+     */
+    private TTreeItem treeRoot;
+
+    /**
+     * Get the root of the tree.
+     *
+     * @return the root of the tree
+     */
+    public final TTreeItem getTreeRoot() {
+        return treeRoot;
+    }
+
+    /**
+     * Set the root of the tree.
+     *
+     * @param treeRoot the new root of the tree
+     */
+    public final void setTreeRoot(final TTreeItem treeRoot) {
+        this.treeRoot = treeRoot;
+    }
+
+    /**
+     * Maximum width of a single line.
+     */
+    private int maxLineWidth;
+
+    /**
+     * Only one of my children can be selected.
+     */
+    private TTreeItem selectedItem = null;
+
+    /**
+     * If true, move the window to put the selected item in view.  This
+     * normally only happens once after setting treeRoot.
+     */
+    public boolean centerWindow = false;
+
+    /**
+     * The action to perform when the user selects an item.
+     */
+    private TAction action = null;
+
+    /**
+     * Set treeRoot.
+     *
+     * @param treeRoot ultimate root of tree
+     * @param centerWindow if true, move the window to put the root in view
+     */
+    public void setTreeRoot(final TTreeItem treeRoot, final boolean centerWindow) {
+        this.treeRoot = treeRoot;
+        this.centerWindow = centerWindow;
+    }
+
+    /**
+     * Public constructor.
+     *
+     * @param parent parent widget
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of tree view
+     * @param height height of tree view
+     */
+    public TTreeView(final TWidget parent, final int x, final int y,
+        final int width, final int height) {
+
+        this(parent, x, y, width, height, null);
+    }
+
+    /**
+     * Public constructor.
+     *
+     * @param parent parent widget
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of tree view
+     * @param height height of tree view
+     * @param action action to perform when an item is selected
+     */
+    public TTreeView(final TWidget parent, final int x, final int y,
+        final int width, final int height, final TAction action) {
+
+        super(parent, x, y, width, height);
+        this.action = action;
+    }
+
+    /**
+     * Get the tree view item that was selected.
+     *
+     * @return the selected item, or null if no item is selected
+     */
+    public final TTreeItem getSelected() {
+        return selectedItem;
+    }
+
+    /**
+     * Set the new selected tree view item.  Note package private access.
+     *
+     * @param item new item that became selected
+     */
+    void setSelected(final TTreeItem item) {
+        if (item != null) {
+            item.setSelected(true);
+        }
+        if ((selectedItem != null) && (selectedItem != item)) {
+            selectedItem.setSelected(false);
+        }
+        selectedItem = item;
+    }
+
+    /**
+     * Perform user selection action.  Note package private access.
+     */
+    void dispatch() {
+        if (action != null) {
+            action.DO();
+        }
+    }
+
+    /**
+     * Update (or instantiate) vScroller and hScroller.
+     */
+    private void updateScrollers() {
+        // Setup vertical scroller
+        if (vScroller == null) {
+            vScroller = new TVScroller(this, getWidth() - 1, 0,
+                getHeight() - 1);
+            vScroller.setValue(0);
+            vScroller.setTopValue(0);
+        }
+        vScroller.setX(getWidth() - 1);
+        vScroller.setHeight(getHeight() - 1);
+        vScroller.setBigChange(getHeight() - 1);
+
+        // Setup horizontal scroller
+        if (hScroller == null) {
+            hScroller = new THScroller(this, 0, getHeight() - 1,
+                getWidth() - 1);
+            hScroller.setValue(0);
+            hScroller.setLeftValue(0);
+        }
+        hScroller.setY(getHeight() - 1);
+        hScroller.setWidth(getWidth() - 1);
+        hScroller.setBigChange(getWidth() - 1);
+    }
+
+    /**
+     * Resize text and scrollbars for a new width/height.
+     */
+    public void reflow() {
+        int selectedRow = 0;
+        boolean foundSelectedRow = false;
+
+        updateScrollers();
+        if (treeRoot == null) {
+            return;
+        }
+
+        // Make each child invisible/inactive to start, expandTree() will
+        // reactivate the visible ones.
+        for (TWidget widget: getChildren()) {
+            if (widget instanceof TTreeItem) {
+                TTreeItem item = (TTreeItem) widget;
+                item.setInvisible(true);
+                item.setEnabled(false);
+            }
+        }
+
+        // Expand the tree into a linear list
+        getChildren().clear();
+        getChildren().addAll(treeRoot.expandTree("", true));
+        for (TWidget widget: getChildren()) {
+            TTreeItem item = (TTreeItem) widget;
+
+            if (item == selectedItem) {
+                foundSelectedRow = true;
+            }
+            if (foundSelectedRow == false) {
+                selectedRow++;
+            }
+
+            int lineWidth = item.getText().length()
+            + item.getPrefix().length() + 4;
+            if (lineWidth > maxLineWidth) {
+                maxLineWidth = lineWidth;
+            }
+        }
+        if ((centerWindow) && (foundSelectedRow)) {
+            if ((selectedRow < vScroller.getValue())
+                || (selectedRow > vScroller.getValue() + getHeight() - 2)
+            ) {
+                vScroller.setValue(selectedRow);
+                centerWindow = false;
+            }
+        }
+        updatePositions();
+
+        // Rescale the scroll bars
+        vScroller.setBottomValue(getChildren().size() - getHeight() + 1);
+        if (vScroller.getBottomValue() < 0) {
+            vScroller.setBottomValue(0);
+        }
+        /*
+        if (vScroller.getValue() > vScroller.getBottomValue()) {
+            vScroller.setValue(vScroller.getBottomValue());
+        }
+         */
+        hScroller.setRightValue(maxLineWidth - getWidth() + 3);
+        if (hScroller.getRightValue() < 0) {
+            hScroller.setRightValue(0);
+        }
+        /*
+        if (hScroller.getValue() > hScroller.getRightValue()) {
+            hScroller.setValue(hScroller.getRightValue());
+        }
+         */
+        getChildren().add(hScroller);
+        getChildren().add(vScroller);
+    }
+
+    /**
+     * Update the Y positions of all the children items.
+     */
+    private void updatePositions() {
+        if (treeRoot == null) {
+            return;
+        }
+
+        int begin = vScroller.getValue();
+        int topY = 0;
+        for (int i = 0; i < getChildren().size(); i++) {
+            if (!(getChildren().get(i) instanceof TTreeItem)) {
+                // Skip
+                continue;
+            }
+            TTreeItem item = (TTreeItem) getChildren().get(i);
+
+            if (i < begin) {
+                // Render invisible
+                item.setEnabled(false);
+                item.setInvisible(true);
+                continue;
+            }
+
+            if (topY >= getHeight() - 1) {
+                // Render invisible
+                item.setEnabled(false);
+                item.setInvisible(true);
+                continue;
+            }
+
+            item.setY(topY);
+            item.setEnabled(true);
+            item.setInvisible(false);
+            item.setWidth(getWidth() - 1);
+            topY++;
+        }
+    }
+
+    /**
+     * Handle mouse press events.
+     *
+     * @param mouse mouse button press event
+     */
+    @Override
+    public void onMouseDown(final TMouseEvent mouse) {
+        if (mouse.isMouseWheelUp()) {
+            vScroller.decrement();
+        } else if (mouse.isMouseWheelDown()) {
+            vScroller.increment();
+        } else {
+            // Pass to children
+            super.onMouseDown(mouse);
+        }
+
+        // Update the screen after the scrollbars have moved
+        reflow();
+    }
+
+    /**
+     * Handle mouse release events.
+     *
+     * @param mouse mouse button release event
+     */
+    @Override
+    public void onMouseUp(TMouseEvent mouse) {
+        // Pass to children
+        super.onMouseDown(mouse);
+
+        // Update the screen after any thing has expanded/contracted
+        reflow();
+    }
+
+    /**
+     * 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 if (keypress.equals(kbEnter)) {
+            if (selectedItem != null) {
+                dispatch();
+            }
+        } else {
+            // Pass other keys (tab etc.) on
+            super.onKeypress(keypress);
+        }
+
+        // Update the screen after any thing has expanded/contracted
+        reflow();
+    }
+
+}
index 7ea1a427cbb78227e40c9b4935139631a66b3919..a32b716a523eebe2231947b1f680da4b693aa2a2 100644 (file)
@@ -370,11 +370,16 @@ public abstract class TWidget implements Comparable<TWidget> {
     }
 
     /**
-     * Comparison operator sorts on tabOrder for TWidgets and z for TWindows.
-     *
-     * @param that another TWidget or TWindow instance
+     * Comparison operator sorts on:
+     * <ul>
+     * <li>tabOrder for TWidgets</li>
+     * <li>z for TWindows</li>
+     * <li>text for TTreeItems</li>
+     * </ul>
+     *
+     * @param that another TWidget, TWindow, or TTreeItem instance
      * @return difference between this.tabOrder and that.tabOrder, or
-     * difference between this.z and that.z
+     * difference between this.z and that.z, or String.compareTo(text)
      */
     @Override
     public final int compareTo(final TWidget that) {
@@ -383,6 +388,12 @@ public abstract class TWidget implements Comparable<TWidget> {
         ) {
             return (((TWindow) this).getZ() - ((TWindow) that).getZ());
         }
+        if ((this instanceof TTreeItem)
+            && (that instanceof TTreeItem)
+        ) {
+            return (((TTreeItem) this).getText().compareTo(
+                ((TTreeItem) that).getText()));
+        }
         return (this.tabOrder - that.tabOrder);
     }
 
@@ -1272,4 +1283,34 @@ public abstract class TWidget implements Comparable<TWidget> {
             updateAction);
     }
 
+    /**
+     * Convenience function to add a tree view to this container/window.
+     *
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of tree view
+     * @param height height of tree view
+     */
+    public final TTreeView addTreeView(final int x, final int y,
+        final int width, final int height) {
+
+        return new TTreeView(this, x, y, width, height);
+    }
+
+    /**
+     * Convenience function to add a tree view to this container/window.
+     *
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of tree view
+     * @param height height of tree view
+     * @param action action to perform when an item is selected
+     */
+    public final TTreeView addTreeView(final int x, final int y,
+        final int width, final int height, final TAction action) {
+
+        return new TTreeView(this, x, y, width, height, action);
+    }
+
+
 }
index 22609620b5f2d56979b53f9c4d2588710b76cf92..4b31c717a0a89c5a9ac746cf2c50de9eb7360769 100644 (file)
@@ -39,7 +39,7 @@ import jexer.menu.*;
 /**
  * The demo application itself.
  */
-class DemoApplication extends TApplication {
+public class DemoApplication extends TApplication {
 
     /**
      * Add all the widgets of the demo.
index 8f92bff307bffdbf635287af490322c9273bcc95..783ad8bbdff64536acb1182d7d731116a62a81b6 100644 (file)
@@ -38,7 +38,7 @@ import jexer.menu.*;
  * This window demonstates the TRadioGroup, TRadioButton, and TCheckbox
  * widgets.
  */
-class DemoCheckboxWindow extends TWindow {
+public class DemoCheckboxWindow extends TWindow {
 
     /**
      * Constructor.
index cff9bbdbbee180ff2f8706ac194035cb38731971..c2f77e80882b95d509636d5a2173950a4a960cd9 100644 (file)
@@ -38,7 +38,7 @@ import jexer.menu.*;
  * This is the main "demo" application window.  It makes use of the TTimer,
  * TProgressBox, TLabel, TButton, and TField widgets.
  */
-class DemoMainWindow extends TWindow {
+public class DemoMainWindow extends TWindow {
 
     // Timer that increments a number.
     private TTimer timer;
@@ -153,17 +153,17 @@ class DemoMainWindow extends TWindow {
         }
         row += 2;
 
-        /*
         if (!isModal()) {
             addLabel("Tree views", 1, row);
             addButton("Tree&View", 35, row,
-                {
-                    new DemoTreeViewWindow(application);
+                new TAction() {
+                    public void DO() {
+                        new DemoTreeViewWindow(getApplication());
+                    }
                 }
             );
         }
         row += 2;
-         */
 
         if (!isModal()) {
             addLabel("Terminal", 1, row);
index 25115ea08a47e41285665191f8747def3025bfac..e0c7b2112ce43da33263cd04bba8ecebcf1f4c37 100644 (file)
@@ -37,7 +37,7 @@ import jexer.menu.*;
 /**
  * This window demonstates the TMessageBox and TInputBox widgets.
  */
-class DemoMsgBoxWindow extends TWindow {
+public class DemoMsgBoxWindow extends TWindow {
 
     /**
      * Constructor.
index aa68f892828d19d274d69758bb157fa9d186ccf7..81235cdad2d1b8330dfcc8de0d6602838e2ea42e 100644 (file)
@@ -37,7 +37,7 @@ import jexer.menu.*;
 /**
  * This window demonstates the TText, THScroller, and TVScroller widgets.
  */
-class DemoTextWindow extends TWindow {
+public class DemoTextWindow extends TWindow {
 
     /**
      * Hang onto my TText so I can resize it with the window.
diff --git a/src/jexer/demos/DemoTreeViewWindow.java b/src/jexer/demos/DemoTreeViewWindow.java
new file mode 100644 (file)
index 0000000..4bd5a38
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.demos;
+
+import jexer.*;
+import jexer.event.*;
+import jexer.menu.*;
+
+/**
+ * This window demonstates the TTreeView widget.
+ */
+public class DemoTreeViewWindow extends TWindow {
+
+    /**
+     * Hang onto my TTreeView so I can resize it with the window.
+     */
+    private TTreeView treeView;
+
+    /**
+     * Public constructor.
+     *
+     * @param parent the main application
+     */
+    public DemoTreeViewWindow(TApplication parent) {
+        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);
+    }
+
+    /**
+     * Handle window/screen resize events.
+     *
+     * @param resize resize event
+     */
+    @Override
+    public void onResize(TResizeEvent resize) {
+        if (resize.getType() == TResizeEvent.Type.WIDGET) {
+            // Resize the text field
+            treeView.setWidth(resize.getWidth() - 4);
+            treeView.setHeight(resize.getHeight() - 4);
+            treeView.reflow();
+            return;
+        }
+
+        // Pass to children instead
+        for (TWidget widget: getChildren()) {
+            widget.onResize(resize);
+        }
+    }
+
+}