Merge branch 'subtree'
[fanfix.git] / src / jexer / TSplitPane.java
index 796a04a8d12c6854f1a0a6607661f4a4093472ec..b308e9b79162a57aea99d55bad2a77f22e951eb3 100644 (file)
@@ -30,10 +30,8 @@ package jexer;
 
 import jexer.bits.CellAttributes;
 import jexer.bits.GraphicsChars;
-import jexer.event.TMenuEvent;
 import jexer.event.TMouseEvent;
 import jexer.event.TResizeEvent;
-import jexer.menu.TMenu;
 
 /**
  * TSplitPane contains two widgets with a draggable horizontal or vertical
@@ -147,9 +145,9 @@ public class TSplitPane extends TWidget {
 
         if (mouse.isMouse1()) {
             if (vertical) {
-                inSplitMove = (mouse.getX() == split);
+                inSplitMove = (mouse.getAbsoluteX() - getAbsoluteX() == split);
             } else {
-                inSplitMove = (mouse.getY() == split);
+                inSplitMove = (mouse.getAbsoluteY() - getAbsoluteY() == split);
             }
             if (inSplitMove) {
                 return;
@@ -199,10 +197,10 @@ public class TSplitPane extends TWidget {
 
         if (inSplitMove) {
             if (vertical) {
-                split = mouse.getX();
+                split = mouse.getAbsoluteX() - getAbsoluteX();
                 split = Math.min(Math.max(1, split), getWidth() - 2);
             } else {
-                split = mouse.getY();
+                split = mouse.getAbsoluteY() - getAbsoluteY();
                 split = Math.min(Math.max(1, split), getHeight() - 2);
             }
             layoutChildren();
@@ -225,7 +223,28 @@ public class TSplitPane extends TWidget {
         CellAttributes attr = getTheme().getColor("tsplitpane");
         if (vertical) {
             vLineXY(split, 0, getHeight(), GraphicsChars.WINDOW_SIDE, attr);
-            // TODO: draw intersections of children
+
+            // Draw intersections of children
+            if ((left instanceof TSplitPane)
+                && (((TSplitPane) left).vertical == false)
+                && (right instanceof TSplitPane)
+                && (((TSplitPane) right).vertical == false)
+                && (((TSplitPane) left).split == ((TSplitPane) right).split)
+            ) {
+                putCharXY(split, ((TSplitPane) left).split, '\u253C', attr);
+            } else {
+                if ((left instanceof TSplitPane)
+                    && (((TSplitPane) left).vertical == false)
+                ) {
+                    putCharXY(split, ((TSplitPane) left).split, '\u2524', attr);
+                }
+                if ((right instanceof TSplitPane)
+                    && (((TSplitPane) right).vertical == false)
+                ) {
+                    putCharXY(split, ((TSplitPane) right).split, '\u251C',
+                        attr);
+                }
+            }
 
             if ((mouse != null)
                 && (mouse.getAbsoluteX() == getAbsoluteX() + split)
@@ -237,7 +256,28 @@ public class TSplitPane extends TWidget {
             }
         } else {
             hLineXY(0, split, getWidth(), GraphicsChars.SINGLE_BAR, attr);
-            // TODO: draw intersections of children
+
+            // Draw intersections of children
+            if ((top instanceof TSplitPane)
+                && (((TSplitPane) top).vertical == true)
+                && (bottom instanceof TSplitPane)
+                && (((TSplitPane) bottom).vertical == true)
+                && (((TSplitPane) top).split == ((TSplitPane) bottom).split)
+            ) {
+                putCharXY(((TSplitPane) top).split, split, '\u253C', attr);
+            } else {
+                if ((top instanceof TSplitPane)
+                    && (((TSplitPane) top).vertical == true)
+                ) {
+                    putCharXY(((TSplitPane) top).split, split, '\u2534', attr);
+                }
+                if ((bottom instanceof TSplitPane)
+                    && (((TSplitPane) bottom).vertical == true)
+                ) {
+                    putCharXY(((TSplitPane) bottom).split, split, '\u252C',
+                        attr);
+                }
+            }
 
             if ((mouse != null)
                 && (mouse.getAbsoluteY() == getAbsoluteY() + split)
@@ -251,6 +291,29 @@ public class TSplitPane extends TWidget {
 
     }
 
+    /**
+     * Generate a human-readable string for this widget.
+     *
+     * @return a human-readable string
+     */
+    @Override
+    public String toString() {
+        return String.format("%s(%8x) %s position (%d, %d) geometry %dx%d " +
+            "split %d left %s(%8x) right %s(%8x) top %s(%8x) bottom %s(%8x) " +
+            "active %s enabled %s visible %s", getClass().getName(),
+            hashCode(), (vertical ? "VERTICAL" : "HORIZONTAL"),
+            getX(), getY(), getWidth(), getHeight(), split,
+            (left == null ? "null" : left.getClass().getName()),
+            (left == null ? 0 : left.hashCode()),
+            (right == null ? "null" : right.getClass().getName()),
+            (right == null ? 0 : right.hashCode()),
+            (top == null ? "null" : top.getClass().getName()),
+            (top == null ? 0 : top.hashCode()),
+            (bottom == null ? "null" : bottom.getClass().getName()),
+            (bottom == null ? 0 : bottom.hashCode()),
+            isActive(), isEnabled(), isVisible());
+    }
+
     // ------------------------------------------------------------------------
     // TSplitPane -------------------------------------------------------------
     // ------------------------------------------------------------------------
@@ -275,7 +338,9 @@ public class TSplitPane extends TWidget {
                 "horizontal split pane");
         }
         if (left == null) {
-            remove(this.left);
+            if (this.left != null) {
+                remove(this.left);
+            }
             this.left = null;
             return;
         }
@@ -305,7 +370,9 @@ public class TSplitPane extends TWidget {
                 "horizontal split pane");
         }
         if (right == null) {
-            remove(this.right);
+            if (this.right != null) {
+                remove(this.right);
+            }
             this.right = null;
             return;
         }
@@ -335,7 +402,9 @@ public class TSplitPane extends TWidget {
                 "split pane");
         }
         if (top == null) {
-            remove(this.top);
+            if (this.top != null) {
+                remove(this.top);
+            }
             this.top = null;
             return;
         }
@@ -365,7 +434,9 @@ public class TSplitPane extends TWidget {
                 "vertical split pane");
         }
         if (bottom == null) {
-            remove(this.bottom);
+            if (this.bottom != null) {
+                remove(this.bottom);
+            }
             this.bottom = null;
             return;
         }
@@ -375,6 +446,92 @@ public class TSplitPane extends TWidget {
                 getHeight()));
     }
 
+    /**
+     * Remove a widget, regardless of what pane it is on.
+     *
+     * @param widget the widget to remove
+     */
+    public void removeWidget(final TWidget widget) {
+        if (widget == null) {
+            throw new IllegalArgumentException("cannot remove null widget");
+        }
+        if (left == widget) {
+            left = null;
+            assert(right != widget);
+            assert(top != widget);
+            assert(bottom != widget);
+            return;
+        }
+        if (right == widget) {
+            right = null;
+            assert(left != widget);
+            assert(top != widget);
+            assert(bottom != widget);
+            return;
+        }
+        if (top == widget) {
+            top = null;
+            assert(left != widget);
+            assert(right != widget);
+            assert(bottom != widget);
+            return;
+        }
+        if (bottom == widget) {
+            bottom = null;
+            assert(left != widget);
+            assert(right != widget);
+            assert(top != widget);
+            return;
+        }
+        throw new IllegalArgumentException("widget " + widget +
+            " not in this split");
+    }
+
+    /**
+     * Replace a widget, regardless of what pane it is on, with another
+     * widget.
+     *
+     * @param oldWidget the widget to remove
+     * @param newWidget the widget to replace it with
+     */
+    public void replaceWidget(final TWidget oldWidget,
+        final TWidget newWidget) {
+
+        if (oldWidget == null) {
+            throw new IllegalArgumentException("cannot remove null oldWidget");
+        }
+        if (left == oldWidget) {
+            setLeft(newWidget);
+            assert(right != newWidget);
+            assert(top != newWidget);
+            assert(bottom != newWidget);
+            return;
+        }
+        if (right == oldWidget) {
+            setRight(newWidget);
+            assert(left != newWidget);
+            assert(top != newWidget);
+            assert(bottom != newWidget);
+            return;
+        }
+        if (top == oldWidget) {
+            setTop(newWidget);
+            assert(left != newWidget);
+            assert(right != newWidget);
+            assert(bottom != newWidget);
+            return;
+        }
+        if (bottom == oldWidget) {
+            setBottom(newWidget);
+            assert(left != newWidget);
+            assert(right != newWidget);
+            assert(top != newWidget);
+            return;
+        }
+        throw new IllegalArgumentException("oldWidget " + oldWidget +
+            " not in this split");
+    }
+
     /**
      * Layout the two child widgets.
      */
@@ -418,4 +575,68 @@ public class TSplitPane extends TWidget {
         layoutChildren();
     }
 
+    /**
+     * Remove this split, removing the widget specified.
+     *
+     * @param widgetToRemove the widget to remove
+     * @param doClose if true, call the close() method before removing the
+     * child
+     * @return the pane that remains, or null if nothing is retained
+     */
+    public TWidget removeSplit(final TWidget widgetToRemove,
+        final boolean doClose) {
+
+        TWidget keep = null;
+        if (vertical) {
+            if ((widgetToRemove != left) && (widgetToRemove != right)) {
+                throw new IllegalArgumentException("widget to remove is not " +
+                    "either of the panes in this splitpane");
+            }
+            if (widgetToRemove == left) {
+                keep = right;
+            } else {
+                keep = left;
+            }
+
+        } else {
+            if ((widgetToRemove != top) && (widgetToRemove != bottom)) {
+                throw new IllegalArgumentException("widget to remove is not " +
+                    "either of the panes in this splitpane");
+            }
+            if (widgetToRemove == top) {
+                keep = bottom;
+            } else {
+                keep = top;
+            }
+        }
+
+        // Remove me from my parent widget.
+        TWidget myParent = getParent();
+        remove(false);
+
+        if (keep == null) {
+            if (myParent instanceof TSplitPane) {
+                // TSplitPane has a left/right/top/bottom link to me
+                // somewhere, remove it.
+                ((TSplitPane) myParent).removeWidget(this);
+            }
+
+            // Nothing is left of either pane.  Remove me and bail out.
+            return null;
+        }
+
+        if (myParent instanceof TSplitPane) {
+            // TSplitPane has a left/right/top/bottom link to me
+            // somewhere, replace me with keep.
+            ((TSplitPane) myParent).replaceWidget(this, keep);
+        } else {
+            keep.setParent(myParent, false);
+            keep.setDimensions(getX(), getY(), getWidth(), getHeight());
+            keep.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET, getWidth(),
+                    getHeight()));
+        }
+
+        return keep;
+    }
+
 }