| 1 | import jexer.TAction; |
| 2 | import jexer.TApplication; |
| 3 | import jexer.TDesktop; |
| 4 | import jexer.TTerminalWidget; |
| 5 | import jexer.TSplitPane; |
| 6 | import jexer.TWidget; |
| 7 | import jexer.event.TMenuEvent; |
| 8 | import jexer.menu.TMenu; |
| 9 | |
| 10 | /** |
| 11 | * Implements a simple tiling window manager. A terminal widget is added to |
| 12 | * the desktop, which can be split horizontally or vertically. A close |
| 13 | * action is provided to each window to remove the split when its shell |
| 14 | * exits. |
| 15 | * |
| 16 | * This example shows what can be done with minimal changes to stock Jexer |
| 17 | * widgets. |
| 18 | */ |
| 19 | public class JexerTilingWindowManager2 extends TApplication { |
| 20 | |
| 21 | /** |
| 22 | * Menu item: split the terminal vertically. |
| 23 | */ |
| 24 | private static final int MENU_SPLIT_VERTICAL = 2000; |
| 25 | |
| 26 | /** |
| 27 | * Menu item: split the terminal horizontally. |
| 28 | */ |
| 29 | private static final int MENU_SPLIT_HORIZONTAL = 2001; |
| 30 | /** |
| 31 | * Menu item: recreate the root terminal. |
| 32 | */ |
| 33 | private static final int MENU_RESPAWN_ROOT = 2002; |
| 34 | |
| 35 | /** |
| 36 | * Handle to the root widget. |
| 37 | */ |
| 38 | private TWidget root = null; |
| 39 | |
| 40 | /** |
| 41 | * Main entry point. |
| 42 | */ |
| 43 | public static void main(String [] args) throws Exception { |
| 44 | // For this application, we must use ptypipe so that the terminal |
| 45 | // shells can be aware of their size. |
| 46 | System.setProperty("jexer.TTerminal.ptypipe", "true"); |
| 47 | |
| 48 | // Let's also suppress the status line. |
| 49 | System.setProperty("jexer.hideStatusBar", "true"); |
| 50 | |
| 51 | JexerTilingWindowManager2 jtwm = new JexerTilingWindowManager2(); |
| 52 | (new Thread(jtwm)).start(); |
| 53 | } |
| 54 | |
| 55 | /** |
| 56 | * Public constructor chooses the ECMA-48 / Xterm backend. |
| 57 | */ |
| 58 | public JexerTilingWindowManager2() throws Exception { |
| 59 | super(BackendType.XTERM); |
| 60 | |
| 61 | // The stock tool menu has items for redrawing the screen, opening |
| 62 | // images, and (when using the Swing backend) setting the font. |
| 63 | addToolMenu(); |
| 64 | |
| 65 | // We will have one menu containing a mix of new and stock commands |
| 66 | TMenu tileMenu = addMenu("&Tile"); |
| 67 | |
| 68 | // New commands for this example: split vertical and horizontal. |
| 69 | tileMenu.addItem(MENU_SPLIT_VERTICAL, "&Vertical Split"); |
| 70 | tileMenu.addItem(MENU_SPLIT_HORIZONTAL, "&Horizontal Split"); |
| 71 | tileMenu.addItem(MENU_RESPAWN_ROOT, "&Respawn Root Terminal"); |
| 72 | |
| 73 | // Stock commands: a new shell with resizable window, and exit |
| 74 | // program. |
| 75 | tileMenu.addSeparator(); |
| 76 | tileMenu.addItem(TMenu.MID_SHELL, "&New Windowed Terminal"); |
| 77 | tileMenu.addSeparator(); |
| 78 | tileMenu.addDefaultItem(TMenu.MID_EXIT); |
| 79 | |
| 80 | // TTerminalWidget can request the text-block mouse pointer be |
| 81 | // suppressed, but the default TDesktop will ignore it. Let's set a |
| 82 | // new TDesktop to pass that mouse pointer visibility option to |
| 83 | // TApplication. |
| 84 | setDesktop(new TDesktop(this) { |
| 85 | @Override |
| 86 | public boolean hasHiddenMouse() { |
| 87 | TWidget active = getActiveChild(); |
| 88 | if (active instanceof TTerminalWidget) { |
| 89 | return ((TTerminalWidget) active).hasHiddenMouse(); |
| 90 | } |
| 91 | return false; |
| 92 | } |
| 93 | }); |
| 94 | |
| 95 | // Spin up the root terminal |
| 96 | createRootTerminal(); |
| 97 | } |
| 98 | |
| 99 | /** |
| 100 | * Process menu events. |
| 101 | */ |
| 102 | @Override |
| 103 | protected boolean onMenu(TMenuEvent event) { |
| 104 | TWidget active = getDesktop().getActiveChild(); |
| 105 | TSplitPane split = null; |
| 106 | |
| 107 | switch (event.getId()) { |
| 108 | case MENU_RESPAWN_ROOT: |
| 109 | assert (root == null); |
| 110 | createRootTerminal(); |
| 111 | return true; |
| 112 | |
| 113 | case MENU_SPLIT_VERTICAL: |
| 114 | if (root == null) { |
| 115 | assert (getDesktop().getActiveChild() == null); |
| 116 | createRootTerminal(); |
| 117 | return true; |
| 118 | } |
| 119 | split = active.splitVertical(false, createTerminal()); |
| 120 | if (active == root) { |
| 121 | root = split; |
| 122 | } |
| 123 | return true; |
| 124 | |
| 125 | case MENU_SPLIT_HORIZONTAL: |
| 126 | if (root == null) { |
| 127 | assert (getDesktop().getActiveChild() == null); |
| 128 | createRootTerminal(); |
| 129 | return true; |
| 130 | } |
| 131 | split = active.splitHorizontal(false, createTerminal()); |
| 132 | if (active == root) { |
| 133 | root = split; |
| 134 | } |
| 135 | return true; |
| 136 | |
| 137 | default: |
| 138 | return super.onMenu(event); |
| 139 | } |
| 140 | |
| 141 | } |
| 142 | |
| 143 | /** |
| 144 | * Create the root terminal. |
| 145 | */ |
| 146 | private void createRootTerminal() { |
| 147 | assert (root == null); |
| 148 | disableMenuItem(MENU_RESPAWN_ROOT); |
| 149 | root = createTerminal(); |
| 150 | } |
| 151 | |
| 152 | /** |
| 153 | * Create a new terminal. |
| 154 | * |
| 155 | * @return the new terminal |
| 156 | */ |
| 157 | private TWidget createTerminal() { |
| 158 | return new TTerminalWidget(getDesktop(), 0, 0, |
| 159 | getDesktop().getWidth(), getDesktop().getHeight(), |
| 160 | new TAction() { |
| 161 | public void DO() { |
| 162 | if (source.getParent() instanceof TSplitPane) { |
| 163 | ((TSplitPane) source.getParent()).removeSplit(source, |
| 164 | true); |
| 165 | } else { |
| 166 | source.getApplication().enableMenuItem( |
| 167 | MENU_RESPAWN_ROOT); |
| 168 | source.remove(); |
| 169 | root = null; |
| 170 | } |
| 171 | } |
| 172 | }); |
| 173 | } |
| 174 | |
| 175 | } |