PMD code sweep, #6 don't add MyWindow twice to MyApplication
[fanfix.git] / src / jexer / ttree / TTreeViewWidget.java
similarity index 58%
rename from src/jexer/TTreeView.java
rename to src/jexer/ttree/TTreeViewWidget.java
index b9b05bf0fd492dc0d5b83933a0736d52d7720c55..adb9a5d8e354b1974d70843e608944e0dd4b5c8b 100644 (file)
  * @author Kevin Lamonte [kevin.lamonte@gmail.com]
  * @version 1
  */
-package jexer;
-
+package jexer.ttree;
+
+import jexer.TAction;
+import jexer.THScroller;
+import jexer.TKeypress;
+import jexer.TScrollableWidget;
+import jexer.TVScroller;
+import jexer.TWidget;
 import jexer.event.TKeypressEvent;
 import jexer.event.TMouseEvent;
 import static jexer.TKeypress.*;
 
 /**
- * TTreeView implements a simple tree view.
+ * TTreeViewWidget wraps a tree view with horizontal and vertical scrollbars.
  */
-public class TTreeView extends TScrollableWidget {
-
-    /**
-     * 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;
-    }
+public class TTreeViewWidget extends TScrollableWidget {
 
-    /**
-     * Maximum width of a single line.
-     */
-    private int maxLineWidth;
+    // ------------------------------------------------------------------------
+    // Variables --------------------------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
-     * Only one of my children can be selected.
+     * The TTreeView
      */
-    private TTreeItem selectedItem = null;
+    private TTreeView treeView;
 
     /**
      * If true, move the window to put the selected item in view.  This
@@ -77,22 +59,13 @@ public class TTreeView extends TScrollableWidget {
     private 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
+     * Maximum width of a single line.
      */
-    public void setTreeRoot(final TTreeItem treeRoot,
-        final boolean centerWindow) {
+    private int maxLineWidth;
 
-        this.treeRoot = treeRoot;
-        this.centerWindow = centerWindow;
-    }
+    // ------------------------------------------------------------------------
+    // Constructors -----------------------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
      * Public constructor.
@@ -103,7 +76,7 @@ public class TTreeView extends TScrollableWidget {
      * @param width width of tree view
      * @param height height of tree view
      */
-    public TTreeView(final TWidget parent, final int x, final int y,
+    public TTreeViewWidget(final TWidget parent, final int x, final int y,
         final int width, final int height) {
 
         this(parent, x, y, width, height, null);
@@ -119,174 +92,22 @@ public class TTreeView extends TScrollableWidget {
      * @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,
+    public TTreeViewWidget(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;
+
+        treeView = new TTreeView(this, 0, 0, getWidth() - 1, getHeight() - 1,
+            action);
 
         vScroller = new TVScroller(this, getWidth() - 1, 0, getHeight() - 1);
         hScroller = new THScroller(this, 0, getHeight() - 1, getWidth() - 1);
-    }
-
-    /**
-     * 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.
-     *
-     * @param item new item that became selected
-     */
-    public 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.
-     */
-    public void dispatch() {
-        if (action != null) {
-            action.DO();
-        }
-    }
 
-    /**
-     * Resize text and scrollbars for a new width/height.
-     */
-    @Override
-    public void reflowData() {
-        int selectedRow = 0;
-        boolean foundSelectedRow = false;
-
-        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);
-                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;
-
-            if (item == selectedItem) {
-                foundSelectedRow = true;
-            }
-            if (!foundSelectedRow) {
-                selectedRow++;
-            }
-
-            int lineWidth = item.getText().length()
-                + item.getPrefix().length() + 4;
-            if (lineWidth > maxLineWidth) {
-                maxLineWidth = lineWidth;
-            }
-        }
-
-        if ((centerWindow) && (foundSelectedRow)) {
-            if ((selectedRow < getVerticalValue())
-                || (selectedRow > getVerticalValue() + getHeight() - 2)
-            ) {
-                setVerticalValue(selectedRow);
-                centerWindow = false;
-            }
-        }
-        updatePositions();
-
-        // Rescale the scroll bars
-        setBottomValue(getChildren().size() - getHeight() + 1);
-        if (getBottomValue() < 0) {
-            setBottomValue(0);
-        }
-        if (getVerticalValue() > getBottomValue()) {
-            setVerticalValue(getBottomValue());
-        }
-        setRightValue(maxLineWidth - getWidth() + 3);
-        if (getRightValue() < 0) {
-            setRightValue(0);
-        }
-        if (getHorizontalValue() > getRightValue()) {
-            setHorizontalValue(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 = getVerticalValue();
-        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 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.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++;
-        }
-
-    }
+    // ------------------------------------------------------------------------
+    // Event handlers ---------------------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
      * Handle mouse press events.
@@ -300,11 +121,13 @@ public class TTreeView extends TScrollableWidget {
         } else if (mouse.isMouseWheelDown()) {
             verticalIncrement();
         } else {
-            // Pass to children
+            // Pass to the TreeView or scrollbars
             super.onMouseDown(mouse);
         }
 
-        // Update the screen after the scrollbars have moved
+        // Update the view to reflect the new scrollbar positions
+        treeView.setTopLine(getVerticalValue());
+        treeView.setLeftColumn(getHorizontalValue());
         reflowData();
     }
 
@@ -315,10 +138,28 @@ public class TTreeView extends TScrollableWidget {
      */
     @Override
     public void onMouseUp(final TMouseEvent mouse) {
-        // Pass to children
-        super.onMouseDown(mouse);
+        // Pass to the TreeView or scrollbars
+        super.onMouseUp(mouse);
+
+        // Update the view to reflect the new scrollbar positions
+        treeView.setTopLine(getVerticalValue());
+        treeView.setLeftColumn(getHorizontalValue());
+        reflowData();
+    }
+
+    /**
+     * Handle mouse motion events.
+     *
+     * @param mouse mouse motion event
+     */
+    @Override
+    public void onMouseMotion(final TMouseEvent mouse) {
+        // Pass to the TreeView or scrollbars
+        super.onMouseMotion(mouse);
 
-        // Update the screen after any thing has expanded/contracted
+        // Update the view to reflect the new scrollbar positions
+        treeView.setTopLine(getVerticalValue());
+        treeView.setLeftColumn(getHorizontalValue());
         reflowData();
     }
 
@@ -359,36 +200,29 @@ public class TTreeView extends TScrollableWidget {
             || keypress.equals(kbAltPgDn)
         ) {
             bigVerticalIncrement();
-        } else if (keypress.equals(kbHome)) {
-            toTop();
-        } else if (keypress.equals(kbEnd)) {
-            toBottom();
-        } else if (keypress.equals(kbEnter)) {
-            if (selectedItem != null) {
-                dispatch();
+        } else if (keypress.equals(kbPgDn)) {
+            for (int i = 0; i < getHeight() - 2; i++) {
+                treeView.onKeypress(new TKeypressEvent(TKeypress.kbDown));
             }
-        } 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) {
-                        verticalDecrement();
-                    }
-                }
-            }
-        } 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) {
-                        verticalIncrement();
-                    }
-                }
+            reflowData();
+            return;
+        } else if (keypress.equals(kbPgUp)) {
+            for (int i = 0; i < getHeight() - 2; i++) {
+                treeView.onKeypress(new TKeypressEvent(TKeypress.kbUp));
             }
+            reflowData();
+            return;
+        } else if (keypress.equals(kbHome)) {
+            treeView.setSelected((TTreeItem) treeView.getChildren().get(0),
+                false);
+            treeView.setTopLine(0);
+            reflowData();
+            return;
+        } else if (keypress.equals(kbEnd)) {
+            treeView.setSelected((TTreeItem)  treeView.getChildren().get(
+                treeView.getChildren().size() - 1), true);
+            reflowData();
+            return;
         } else if (keypress.equals(kbTab)) {
             getParent().switchWidget(true);
             return;
@@ -396,17 +230,157 @@ public class TTreeView extends TScrollableWidget {
                 || keypress.equals(kbBackTab)) {
             getParent().switchWidget(false);
             return;
-        } else if (selectedItem != null) {
-            // Give the TTreeItem a chance to handle arrow keys
-            selectedItem.onKeypress(keypress);
         } else {
-            // Pass other keys (tab etc.) on to TWidget's handler.
-            super.onKeypress(keypress);
+            treeView.onKeypress(keypress);
+
+            // Update the scrollbars to reflect the new data position
+            reflowData();
             return;
         }
 
-        // Update the screen after any thing has expanded/contracted
+        // Update the view to reflect the new scrollbar position
+        treeView.setTopLine(getVerticalValue());
+        treeView.setLeftColumn(getHorizontalValue());
         reflowData();
     }
 
+    // ------------------------------------------------------------------------
+    // TScrollableWidget ------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Resize text and scrollbars for a new width/height.
+     */
+    @Override
+    public void reflowData() {
+        int selectedRow = 0;
+        boolean foundSelectedRow = false;
+
+        // Reset the keyboard list, expandTree() will recreate it.
+        for (TWidget widget: treeView.getChildren()) {
+            TTreeItem item = (TTreeItem) widget;
+            item.keyboardPrevious = null;
+            item.keyboardNext = null;
+        }
+
+        // Expand the tree into a linear list
+        treeView.getChildren().clear();
+        treeView.getChildren().addAll(treeView.getTreeRoot().expandTree("",
+                true));
+
+        // Locate the selected row and maximum line width
+        for (TWidget widget: treeView.getChildren()) {
+            TTreeItem item = (TTreeItem) widget;
+
+            if (item == treeView.getSelected()) {
+                foundSelectedRow = true;
+            }
+            if (!foundSelectedRow) {
+                selectedRow++;
+            }
+
+            int lineWidth = item.getText().length()
+                + item.getPrefix().length() + 4;
+            if (lineWidth > maxLineWidth) {
+                maxLineWidth = lineWidth;
+            }
+        }
+
+        if ((centerWindow) && (foundSelectedRow)) {
+            if ((selectedRow < getVerticalValue())
+                || (selectedRow > getVerticalValue() + getHeight() - 2)
+            ) {
+                treeView.setTopLine(selectedRow);
+                centerWindow = false;
+            }
+        }
+        treeView.alignTree();
+
+        // Rescale the scroll bars
+        setVerticalValue(treeView.getTopLine());
+        setBottomValue(treeView.getTotalLineCount() - (getHeight() - 1));
+        if (getBottomValue() < getTopValue()) {
+            setBottomValue(getTopValue());
+        }
+        if (getVerticalValue() > getBottomValue()) {
+            setVerticalValue(getBottomValue());
+        }
+        setRightValue(maxLineWidth - 2);
+        if (getHorizontalValue() > getRightValue()) {
+            setHorizontalValue(getRightValue());
+        }
+
+    }
+
+    // ------------------------------------------------------------------------
+    // TTreeView --------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Get the underlying TTreeView.
+     *
+     * @return the TTreeView
+     */
+    public TTreeView getTreeView() {
+        return treeView;
+    }
+
+    /**
+     * Get the root of the tree.
+     *
+     * @return the root of the tree
+     */
+    public final TTreeItem getTreeRoot() {
+        return treeView.getTreeRoot();
+    }
+
+    /**
+     * Set the root of the tree.
+     *
+     * @param treeRoot the new root of the tree
+     */
+    public final void setTreeRoot(final TTreeItem treeRoot) {
+        treeView.setTreeRoot(treeRoot);
+    }
+
+    /**
+     * 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) {
+
+        treeView.setTreeRoot(treeRoot);
+        this.centerWindow = centerWindow;
+    }
+
+    /**
+     * Get the tree view item that was selected.
+     *
+     * @return the selected item, or null if no item is selected
+     */
+    public final TTreeItem getSelected() {
+        return treeView.getSelected();
+    }
+
+    /**
+     * Set the new selected tree view item.
+     *
+     * @param item new item that became selected
+     * @param centerWindow if true, move the window to put the selected into
+     * view
+     */
+    public void setSelected(final TTreeItem item, final boolean centerWindow) {
+        treeView.setSelected(item, centerWindow);
+    }
+
+    /**
+     * Perform user selection action.
+     */
+    public void dispatch() {
+        treeView.dispatch();
+    }
+
 }