TSplitPane initial
[fanfix.git] / src / jexer / TWidget.java
index 9e02613735d210d2d95e00641c766b024c61ad4f..bcf798ae36e9f40336d40fcd18d11e3a58b0de6c 100644 (file)
@@ -43,6 +43,7 @@ import jexer.event.TKeypressEvent;
 import jexer.event.TMenuEvent;
 import jexer.event.TMouseEvent;
 import jexer.event.TResizeEvent;
+import jexer.layout.LayoutManager;
 import jexer.menu.TMenu;
 import jexer.ttree.TTreeItem;
 import jexer.ttree.TTreeView;
@@ -136,6 +137,11 @@ public abstract class TWidget implements Comparable<TWidget> {
      */
     private int cursorY = 0;
 
+    /**
+     * Layout manager.
+     */
+    private LayoutManager layout = null;
+
     // ------------------------------------------------------------------------
     // Constructors -----------------------------------------------------------
     // ------------------------------------------------------------------------
@@ -212,15 +218,15 @@ public abstract class TWidget implements Comparable<TWidget> {
         this.parent = parent;
         children = new ArrayList<TWidget>();
 
-        if (parent != null) {
-            this.window = parent.window;
-            parent.addChild(this);
-        }
-
         this.x = x;
         this.y = y;
         this.width = width;
         this.height = height;
+
+        if (parent != null) {
+            this.window = parent.window;
+            parent.addChild(this);
+        }
     }
 
     /**
@@ -570,6 +576,14 @@ public abstract class TWidget implements Comparable<TWidget> {
         if (resize.getType() == TResizeEvent.Type.WIDGET) {
             width = resize.getWidth();
             height = resize.getHeight();
+            if (layout != null) {
+                if (this instanceof TWindow) {
+                    layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
+                            width - 2, height - 2));
+                } else {
+                    layout.onResize(resize);
+                }
+            }
         } else {
             // Let children see the screen resize
             for (TWidget widget: children) {
@@ -597,6 +611,61 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @param menu menu event
      */
     public void onMenu(final TMenuEvent menu) {
+
+        // Special case: if a split command comes in, insert a TPanel and
+        // TSplitPane in the hierarchy here.
+        TPanel panel = null;
+        TSplitPane pane = null;
+        List<TWidget> widgets = null;
+        switch (menu.getId()) {
+        case TMenu.MID_SPLIT_VERTICAL:
+            panel = new TPanel(null, x, y, width, height);
+            pane = new TSplitPane(null, x, y, width, height, true);
+            widgets = new ArrayList<TWidget>(children);
+            for (TWidget w: widgets) {
+                w.setParent(panel, false);
+            }
+            children.clear();
+            pane.setParent(this, false);
+            pane.setLeft(panel);
+            activate(pane);
+            for (TWidget w: widgets) {
+                assert (w.window != null);
+                assert (w.parent != null);
+            }
+            assert (pane.getWindow() != null);
+            assert (pane.getParent() != null);
+            assert (panel.getWindow() != null);
+            assert (panel.getParent() != null);
+            assert (pane.isActive() == true);
+            assert (panel.isActive() == true);
+            return;
+        case TMenu.MID_SPLIT_HORIZONTAL:
+            panel = new TPanel(null, x, y, width, height);
+            pane = new TSplitPane(null, x, y, width, height, false);
+            widgets = new ArrayList<TWidget>(children);
+            for (TWidget w: widgets) {
+                w.setParent(panel, false);
+            }
+            children.clear();
+            pane.setParent(this, false);
+            pane.setTop(panel);
+            activate(pane);
+            for (TWidget w: widgets) {
+                assert (w.window != null);
+                assert (w.parent != null);
+            }
+            assert (pane.getWindow() != null);
+            assert (pane.getParent() != null);
+            assert (panel.getWindow() != null);
+            assert (panel.getParent() != null);
+            assert (pane.isActive() == true);
+            assert (panel.isActive() == true);
+            return;
+        default:
+            break;
+        }
+
         // Default: do nothing, pass to children instead
         for (TWidget widget: children) {
             widget.onMenu(menu);
@@ -715,6 +784,15 @@ public abstract class TWidget implements Comparable<TWidget> {
         }
     }
 
+    /**
+     * Remove a child widget from this container.
+     *
+     * @param child the child widget to remove
+     */
+    public final void remove(final TWidget child) {
+        remove(child, true);
+    }
+
     /**
      * Remove a child widget from this container.
      *
@@ -732,6 +810,10 @@ public abstract class TWidget implements Comparable<TWidget> {
         }
         children.remove(child);
         child.parent = null;
+        child.window = null;
+        if (layout != null) {
+            layout.remove(this);
+        }
     }
 
     /**
@@ -746,29 +828,31 @@ public abstract class TWidget implements Comparable<TWidget> {
 
         if (parent != null) {
             parent.remove(this, doClose);
+            window = null;
         }
         assert (parent == null);
-        window = newParent.window;
-        newParent.addChild(this);
+        assert (window == null);
+        parent = newParent;
+        setWindow(parent.window);
+        parent.addChild(this);
     }
 
     /**
-     * Set this widget's window to a specific window.  Parent must already be
-     * null.  Having a null parent with a specified window is only used
-     * within Jexer by TStatusBar because TApplication routes events directly
-     * to it and calls its draw() method.  Any other non-parented widgets
-     * will require similar special case functionality to receive events or
-     * be drawn to screen.
+     * Set this widget's window to a specific window.
+     *
+     * Having a null parent with a specified window is only used within Jexer
+     * by TStatusBar because TApplication routes events directly to it and
+     * calls its draw() method.  Any other non-parented widgets will require
+     * similar special case functionality to receive events or be drawn to
+     * screen.
      *
      * @param window the window to use
      */
     public final void setWindow(final TWindow window) {
-
-        if (parent != null) {
-            throw new IllegalArgumentException("Cannot have different " +
-                "windows for parent and child");
-        }
         this.window = window;
+        for (TWidget child: getChildren()) {
+            child.setWindow(window);
+        }
     }
 
     /**
@@ -854,7 +938,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      *
      * @return widget width
      */
-    public final int getWidth() {
+    public int getWidth() {
         return this.width;
     }
 
@@ -863,8 +947,12 @@ public abstract class TWidget implements Comparable<TWidget> {
      *
      * @param width new widget width
      */
-    public final void setWidth(final int width) {
+    public void setWidth(final int width) {
         this.width = width;
+        if (layout != null) {
+            layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
+                    width, height));
+        }
     }
 
     /**
@@ -872,7 +960,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      *
      * @return widget height
      */
-    public final int getHeight() {
+    public int getHeight() {
         return this.height;
     }
 
@@ -881,8 +969,12 @@ public abstract class TWidget implements Comparable<TWidget> {
      *
      * @param height new widget height
      */
-    public final void setHeight(final int height) {
+    public void setHeight(final int height) {
         this.height = height;
+        if (layout != null) {
+            layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
+                    width, height));
+        }
     }
 
     /**
@@ -900,6 +992,39 @@ public abstract class TWidget implements Comparable<TWidget> {
         setY(y);
         setWidth(width);
         setHeight(height);
+        if (layout != null) {
+            layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
+                    width, height));
+        }
+    }
+
+    /**
+     * Get the layout manager.
+     *
+     * @return the layout manager, or null if not set
+     */
+    public LayoutManager getLayoutManager() {
+        return layout;
+    }
+
+    /**
+     * Set the layout manager.
+     *
+     * @param layout the new layout manager
+     */
+    public void setLayoutManager(LayoutManager layout) {
+        if (this.layout != null) {
+            for (TWidget w: children) {
+                this.layout.remove(w);
+            }
+            this.layout = null;
+        }
+        this.layout = layout;
+        if (this.layout != null) {
+            for (TWidget w: children) {
+                this.layout.add(w);
+            }
+        }
     }
 
     /**
@@ -1262,6 +1387,9 @@ public abstract class TWidget implements Comparable<TWidget> {
         for (int i = 0; i < children.size(); i++) {
             children.get(i).tabOrder = i;
         }
+        if (layout != null) {
+            layout.add(child);
+        }
     }
 
     /**
@@ -1501,7 +1629,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @param ch character to draw
      * @param attr attributes to use (bold, foreColor, backColor)
      */
-    protected final void putAll(final char ch, final CellAttributes attr) {
+    protected final void putAll(final int ch, final CellAttributes attr) {
         getScreen().putAll(ch, attr);
     }
 
@@ -1524,7 +1652,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @param ch character to draw
      * @param attr attributes to use (bold, foreColor, backColor)
      */
-    protected final void putCharXY(final int x, final int y, final char ch,
+    protected final void putCharXY(final int x, final int y, final int ch,
         final CellAttributes attr) {
 
         getScreen().putCharXY(x, y, ch, attr);
@@ -1537,7 +1665,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @param y row coordinate.  0 is the top-most row.
      * @param ch character to draw
      */
-    protected final void putCharXY(final int x, final int y, final char ch) {
+    protected final void putCharXY(final int x, final int y, final int ch) {
         getScreen().putCharXY(x, y, ch);
     }
 
@@ -1577,7 +1705,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @param attr attributes to use (bold, foreColor, backColor)
      */
     protected final void vLineXY(final int x, final int y, final int n,
-        final char ch, final CellAttributes attr) {
+        final int ch, final CellAttributes attr) {
 
         getScreen().vLineXY(x, y, n, ch, attr);
     }
@@ -1592,7 +1720,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @param attr attributes to use (bold, foreColor, backColor)
      */
     protected final void hLineXY(final int x, final int y, final int n,
-        final char ch, final CellAttributes attr) {
+        final int ch, final CellAttributes attr) {
 
         getScreen().hLineXY(x, y, n, ch, attr);
     }
@@ -1791,18 +1919,18 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @param values the possible values for the box, shown in the drop-down
      * @param valuesIndex the initial index in values, or -1 for no default
      * value
-     * @param valuesHeight the height of the values drop-down when it is
-     * visible
+     * @param maxValuesHeight the maximum height of the values drop-down when
+     * it is visible
      * @param updateAction action to call when a new value is selected from
      * the list or enter is pressed in the edit field
      * @return the new combobox
      */
     public final TComboBox addComboBox(final int x, final int y,
         final int width, final List<String> values, final int valuesIndex,
-        final int valuesHeight, final TAction updateAction) {
+        final int maxValuesHeight, final TAction updateAction) {
 
         return new TComboBox(this, x, y, width, values, valuesIndex,
-            valuesHeight, updateAction);
+            maxValuesHeight, updateAction);
     }
 
     /**
@@ -2334,6 +2462,31 @@ public abstract class TWidget implements Comparable<TWidget> {
             moveAction);
     }
 
+    /**
+     * Convenience function to add a list to this container/window.
+     *
+     * @param strings list of strings to show.  This is allowed to be null
+     * and set later with setList() or by subclasses.
+     * @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 enterAction action to perform when an item is selected
+     * @param moveAction action to perform when the user navigates to a new
+     * item with arrow/page keys
+     * @param singleClickAction action to perform when the user clicks on an
+     * item
+     */
+    public TList addList(final List<String> strings, final int x,
+        final int y, final int width, final int height,
+        final TAction enterAction, final TAction moveAction,
+        final TAction singleClickAction) {
+
+        return new TList(this, strings, x, y, width, height, enterAction,
+            moveAction, singleClickAction);
+    }
+
+
     /**
      * Convenience function to add an image to this container/window.
      *
@@ -2406,4 +2559,35 @@ public abstract class TWidget implements Comparable<TWidget> {
             gridRows);
     }
 
+    /**
+     * Convenience function to add a panel to this container/window.
+     *
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of text area
+     * @param height height of text area
+     * @return the new panel
+     */
+    public final TPanel addPanel(final int x, final int y, final int width,
+        final int height) {
+
+        return new TPanel(this, x, y, width, height);
+    }
+
+    /**
+     * Convenience function to add a split pane to this container/window.
+     *
+     * @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 vertical if true, split vertically
+     * @return the new split pane
+     */
+    public final TSplitPane addSplitPane(final int x, final int y,
+        final int width, final int height, final boolean vertical) {
+
+        return new TSplitPane(this, x, y, width, height, vertical);
+    }
+
 }