X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2FTApplication.java;h=8be6af5af54a9a024d67976a7f6960fe2d4bba90;hb=7c870d89433346ccb5505f8f9ba62d3fc18fe996;hp=e710895b555c6d645f21cc06870a4e25afff0a7a;hpb=92554d64c21c6a477fd23a06ca3a64a542b622a3;p=fanfix.git diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index e710895..8be6af5 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -51,18 +51,17 @@ import jexer.event.TMenuEvent; import jexer.event.TMouseEvent; import jexer.event.TResizeEvent; import jexer.backend.Backend; -import jexer.backend.AWTBackend; +import jexer.backend.SwingBackend; import jexer.backend.ECMA48Backend; import jexer.io.Screen; import jexer.menu.TMenu; import jexer.menu.TMenuItem; import static jexer.TCommand.*; -import static jexer.TKeypress.*; /** * TApplication sets up a full Text User Interface application. */ -public class TApplication { +public class TApplication implements Runnable { /** * If true, emit thread stuff to System.err. @@ -74,6 +73,26 @@ public class TApplication { */ private static final boolean debugEvents = false; + /** + * Two backend types are available. + */ + public static enum BackendType { + /** + * A Swing JFrame. + */ + SWING, + + /** + * An ECMA48 / ANSI X3.64 / XTERM style terminal. + */ + ECMA48, + + /** + * Synonym for ECMA48 + */ + XTERM + } + /** * WidgetEventHandler is the main event consumer loop. There are at most * two such threads in existence: the primary for normal case and a @@ -122,17 +141,17 @@ public class TApplication { } synchronized (this) { - /* - System.err.printf("%s %s sleep\n", this, primary ? - "primary" : "secondary"); - */ + if (debugThreads) { + System.err.printf("%s %s sleep\n", this, + primary ? "primary" : "secondary"); + } this.wait(); - /* - System.err.printf("%s %s AWAKE\n", this, primary ? - "primary" : "secondary"); - */ + if (debugThreads) { + System.err.printf("%s %s AWAKE\n", this, + primary ? "primary" : "secondary"); + } if ((!primary) && (application.secondaryEventReceiver == null) @@ -141,13 +160,12 @@ public class TApplication { // got here then something went wrong with // the handoff between yield() and // closeWindow(). - - System.err.printf("secondary exiting at wrong time, why?\n"); synchronized (application.primaryEventHandler) { application.primaryEventHandler.notify(); } application.secondaryEventHandler = null; - return; + throw new RuntimeException( + "secondary exited at wrong time"); } break; } @@ -169,12 +187,12 @@ public class TApplication { // the event. boolean oldLock = lockHandleEvent(); assert (oldLock == false); + application.repaint = true; if (primary) { primaryHandleEvent(event); } else { secondaryHandleEvent(event); } - application.repaint = true; if ((!primary) && (application.secondaryEventReceiver == null) ) { @@ -263,7 +281,7 @@ public class TApplication { synchronized (this) { // Wait for TApplication.run() to finish using the global state // before allowing further event processing. - while (lockoutHandleEvent == true); + while (lockoutHandleEvent == true) {} oldValue = insideHandleEvent; insideHandleEvent = true; @@ -312,7 +330,7 @@ public class TApplication { lockoutHandleEvent = true; // Wait for the last event to finish processing before returning // control to TApplication.run(). - while (insideHandleEvent == true); + while (insideHandleEvent == true) {} if (debugThreads) { System.err.printf(" XXX\n"); @@ -355,6 +373,16 @@ public class TApplication { */ private int mouseY; + /** + * Old version of mouse coordinate X. + */ + private int oldMouseX; + + /** + * Old version mouse coordinate Y. + */ + private int oldMouseY; + /** * Event queue that is filled by run(). */ @@ -420,12 +448,6 @@ public class TApplication { */ private volatile boolean repaint = true; - /** - * When true, just flush updates from the screen. This is only used to - * minimize physical writes for the mouse cursor. - */ - private boolean flush = false; - /** * Y coordinate of the top edge of the desktop. For now this is a * constant. Someday it would be nice to have a multi-line menu or @@ -459,6 +481,29 @@ public class TApplication { /** * Public constructor. * + * @param backendType BackendType.XTERM, BackendType.ECMA48 or + * BackendType.SWING + * @throws UnsupportedEncodingException if an exception is thrown when + * creating the InputStreamReader + */ + public TApplication(final BackendType backendType) + throws UnsupportedEncodingException { + + switch (backendType) { + case SWING: + backend = new SwingBackend(this); + break; + case XTERM: + // Fall through... + case ECMA48: + backend = new ECMA48Backend(this, null, null); + } + TApplicationImpl(); + } + + /** + * Public constructor. The backend type will be BackendType.ECMA48. + * * @param input an InputStream connected to the remote user, or null for * System.in. If System.in is used, then on non-Windows systems it will * be put in raw mode; shutdown() will (blindly!) put System.in in cooked @@ -472,26 +517,25 @@ public class TApplication { public TApplication(final InputStream input, final OutputStream output) throws UnsupportedEncodingException { - // AWT is the default backend on Windows unless explicitly overridden - // by jexer.AWT. - boolean useAWT = false; - if (System.getProperty("os.name").startsWith("Windows")) { - useAWT = true; - } - if (System.getProperty("jexer.AWT") != null) { - if (System.getProperty("jexer.AWT", "false").equals("true")) { - useAWT = true; - } else { - useAWT = false; - } - } + backend = new ECMA48Backend(this, input, output); + TApplicationImpl(); + } + /** + * Public constructor. This hook enables use with new non-Jexer + * backends. + * + * @param backend a Backend that is already ready to go. + */ + public TApplication(final Backend backend) { + this.backend = backend; + TApplicationImpl(); + } - if (useAWT) { - backend = new AWTBackend(this); - } else { - backend = new ECMA48Backend(this, input, output); - } + /** + * Finish construction once the backend is set. + */ + private void TApplicationImpl() { theme = new ColorTheme(); desktopBottom = getScreen().getHeight() - 1; fillEventQueue = new ArrayList(); @@ -508,31 +552,42 @@ public class TApplication { } /** - * Invert the cell at the mouse pointer position. + * Invert the cell color at a position. This is used to track the mouse. + * + * @param x column position + * @param y row position */ - private void drawMouse() { - CellAttributes attr = getScreen().getAttrXY(mouseX, mouseY); - attr.setForeColor(attr.getForeColor().invert()); - attr.setBackColor(attr.getBackColor().invert()); - getScreen().putAttrXY(mouseX, mouseY, attr, false); - flush = true; - - if (windows.size() == 0) { - repaint = true; + private void invertCell(final int x, final int y) { + synchronized (getScreen()) { + CellAttributes attr = getScreen().getAttrXY(x, y); + attr.setForeColor(attr.getForeColor().invert()); + attr.setBackColor(attr.getBackColor().invert()); + getScreen().putAttrXY(x, y, attr, false); } } /** * Draw everything. */ - public final void drawAll() { + private void drawAll() { if (debugThreads) { System.err.printf("drawAll() enter\n"); } - if ((flush) && (!repaint)) { - backend.flushScreen(); - flush = false; + if (!repaint) { + synchronized (getScreen()) { + if ((oldMouseX != mouseX) || (oldMouseY != mouseY)) { + // The only thing that has happened is the mouse moved. + // Clear the old position and draw the new position. + invertCell(oldMouseX, oldMouseY); + invertCell(mouseX, mouseY); + oldMouseX = mouseX; + oldMouseY = mouseY; + } + } + if (getScreen().isDirty()) { + backend.flushScreen(); + } return; } @@ -568,7 +623,7 @@ public class TApplication { for (TMenu menu: menus) { CellAttributes menuColor; CellAttributes menuMnemonicColor; - if (menu.getActive()) { + if (menu.isActive()) { menuColor = theme.getColor("tmenu.highlighted"); menuMnemonicColor = theme.getColor("tmenu.mnemonic.highlighted"); } else { @@ -583,7 +638,7 @@ public class TApplication { getScreen().putCharXY(x + 1 + menu.getMnemonic().getShortcutIdx(), 0, menu.getMnemonic().getShortcut(), menuMnemonicColor); - if (menu.getActive()) { + if (menu.isActive()) { menu.drawChildren(); // Reset the screen clipping so we can draw the next title. getScreen().resetClipping(); @@ -598,13 +653,13 @@ public class TApplication { } // Draw the mouse pointer - drawMouse(); + invertCell(mouseX, mouseY); // Place the cursor if it is visible TWidget activeWidget = null; if (sorted.size() > 0) { activeWidget = sorted.get(sorted.size() - 1).getActiveChild(); - if (activeWidget.visibleCursor()) { + if (activeWidget.isCursorVisible()) { getScreen().putCursor(true, activeWidget.getCursorAbsoluteX(), activeWidget.getCursorAbsoluteY()); cursor = true; @@ -620,13 +675,12 @@ public class TApplication { backend.flushScreen(); repaint = false; - flush = false; } /** * Run this application until it exits. */ - public final void run() { + public void run() { while (!quit) { // Timeout is in milliseconds, so default timeout after 1 second // of inactivity. @@ -634,7 +688,9 @@ public class TApplication { // If I've got no updates to render, wait for something from the // backend or a timer. - if (!repaint && !flush) { + if (!repaint + && ((mouseX == oldMouseX) && (mouseY == oldMouseY)) + ) { // Never sleep longer than 100 millis, to get windows with // background tasks an opportunity to update the display. timeout = getSleepTime(100); @@ -668,6 +724,7 @@ public class TApplication { // I'm awake and don't care why, let's see what's going // on out there. } + repaint = true; } // Pull any pending I/O events @@ -762,26 +819,32 @@ public class TApplication { // Screen resize if (event instanceof TResizeEvent) { TResizeEvent resize = (TResizeEvent) event; - getScreen().setDimensions(resize.getWidth(), - resize.getHeight()); - desktopBottom = getScreen().getHeight() - 1; - repaint = true; - mouseX = 0; - mouseY = 0; + synchronized (getScreen()) { + getScreen().setDimensions(resize.getWidth(), + resize.getHeight()); + desktopBottom = getScreen().getHeight() - 1; + mouseX = 0; + mouseY = 0; + oldMouseX = 0; + oldMouseY = 0; + } return; } // Peek at the mouse position if (event instanceof TMouseEvent) { TMouseEvent mouse = (TMouseEvent) event; - if ((mouseX != mouse.getX()) || (mouseY != mouse.getY())) { - mouseX = mouse.getX(); - mouseY = mouse.getY(); - drawMouse(); + synchronized (getScreen()) { + if ((mouseX != mouse.getX()) || (mouseY != mouse.getY())) { + oldMouseX = mouseX; + oldMouseY = mouseY; + mouseX = mouse.getX(); + mouseY = mouse.getY(); + } } } - // Put into the main queue + // Put into the main queue synchronized (drainEventQueue) { drainEventQueue.add(event); } @@ -822,11 +885,11 @@ public class TApplication { break; } if ((mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) - && (!mouse.getMouse1()) - && (!mouse.getMouse2()) - && (!mouse.getMouse3()) - && (!mouse.getMouseWheelUp()) - && (!mouse.getMouseWheelDown()) + && (!mouse.isMouse1()) + && (!mouse.isMouse2()) + && (!mouse.isMouse3()) + && (!mouse.isMouseWheelUp()) + && (!mouse.isMouseWheelDown()) ) { break; } @@ -858,15 +921,16 @@ public class TApplication { item = accelerators.get(keypressLowercase); } if (item != null) { - // Let the menu item dispatch - item.dispatch(); - return; - } else { - // Handle the keypress - if (onKeypress(keypress)) { + if (item.isEnabled()) { + // Let the menu item dispatch + item.dispatch(); return; } } + // Handle the keypress + if (onKeypress(keypress)) { + return; + } } if (event instanceof TCommandEvent) { @@ -883,7 +947,7 @@ public class TApplication { // Dispatch events to the active window ------------------------------- for (TWindow window: windows) { - if (window.getActive()) { + if (window.isActive()) { if (event instanceof TMouseEvent) { TMouseEvent mouse = (TMouseEvent) event; // Convert the mouse relative x/y to window coordinates @@ -925,9 +989,6 @@ public class TApplication { secondaryEventReceiver = widget; secondaryEventHandler = new WidgetEventHandler(this, false); (new Thread(secondaryEventHandler)).start(); - - // Refresh - repaint = true; } /** @@ -942,8 +1003,6 @@ public class TApplication { boolean oldLock = unlockHandleEvent(); assert (oldLock == true); - // System.err.printf("YIELD\n"); - while (secondaryEventReceiver != null) { synchronized (primaryEventHandler) { try { @@ -953,8 +1012,6 @@ public class TApplication { } } } - - // System.err.printf("EXIT YIELD\n"); } /** @@ -992,7 +1049,7 @@ public class TApplication { * @param timeout = initial (maximum) timeout in millis * @return number of milliseconds between now and the next timer event */ - protected long getSleepTime(final long timeout) { + private long getSleepTime(final long timeout) { Date now = new Date(); long nowTime = now.getTime(); long sleepTime = timeout; @@ -1019,20 +1076,22 @@ public class TApplication { * @param window the window to remove */ public final void closeWindow(final TWindow window) { - int z = window.getZ(); - window.setZ(-1); - Collections.sort(windows); - windows.remove(0); - TWindow activeWindow = null; - for (TWindow w: windows) { - if (w.getZ() > z) { - w.setZ(w.getZ() - 1); - if (w.getZ() == 0) { - w.setActive(true); - assert (activeWindow == null); - activeWindow = w; - } else { - w.setActive(false); + synchronized (windows) { + int z = window.getZ(); + window.setZ(-1); + Collections.sort(windows); + windows.remove(0); + TWindow activeWindow = null; + for (TWindow w: windows) { + if (w.getZ() > z) { + w.setZ(w.getZ() - 1); + if (w.getZ() == 0) { + w.setActive(true); + assert (activeWindow == null); + activeWindow = w; + } else { + w.setActive(false); + } } } } @@ -1040,9 +1099,6 @@ public class TApplication { // Perform window cleanup window.onClose(); - // Refresh screen - repaint = true; - // Check if we are closing a TMessageBox or similar if (secondaryEventReceiver != null) { assert (secondaryEventHandler != null); @@ -1071,38 +1127,40 @@ public class TApplication { return; } - // Swap z/active between active window and the next in the list - int activeWindowI = -1; - for (int i = 0; i < windows.size(); i++) { - if (windows.get(i).getActive()) { - activeWindowI = i; - break; + synchronized (windows) { + + // Swap z/active between active window and the next in the list + int activeWindowI = -1; + for (int i = 0; i < windows.size(); i++) { + if (windows.get(i).isActive()) { + activeWindowI = i; + break; + } } - } - assert (activeWindowI >= 0); + assert (activeWindowI >= 0); - // Do not switch if a window is modal - if (windows.get(activeWindowI).isModal()) { - return; - } + // Do not switch if a window is modal + if (windows.get(activeWindowI).isModal()) { + return; + } - int nextWindowI; - if (forward) { - nextWindowI = (activeWindowI + 1) % windows.size(); - } else { - if (activeWindowI == 0) { - nextWindowI = windows.size() - 1; + int nextWindowI; + if (forward) { + nextWindowI = (activeWindowI + 1) % windows.size(); } else { - nextWindowI = activeWindowI - 1; + if (activeWindowI == 0) { + nextWindowI = windows.size() - 1; + } else { + nextWindowI = activeWindowI - 1; + } } - } - windows.get(activeWindowI).setActive(false); - windows.get(activeWindowI).setZ(windows.get(nextWindowI).getZ()); - windows.get(nextWindowI).setZ(0); - windows.get(nextWindowI).setActive(true); + windows.get(activeWindowI).setActive(false); + windows.get(activeWindowI).setZ(windows.get(nextWindowI).getZ()); + windows.get(nextWindowI).setZ(0); + windows.get(nextWindowI).setActive(true); + + } // synchronized (windows) - // Refresh - repaint = true; } /** @@ -1111,17 +1169,19 @@ public class TApplication { * @param window new window to add */ public final void addWindow(final TWindow window) { - // Do not allow a modal window to spawn a non-modal window - if ((windows.size() > 0) && (windows.get(0).isModal())) { - assert (window.isModal()); - } - for (TWindow w: windows) { - w.setActive(false); - w.setZ(w.getZ() + 1); + synchronized (windows) { + // Do not allow a modal window to spawn a non-modal window + if ((windows.size() > 0) && (windows.get(0).isModal())) { + assert (window.isModal()); + } + for (TWindow w: windows) { + w.setActive(false); + w.setZ(w.getZ() + 1); + } + windows.add(window); + window.setActive(true); + window.setZ(0); } - windows.add(window); - window.setActive(true); - window.setZ(0); } /** @@ -1181,7 +1241,7 @@ public class TApplication { // See if they hit the menu bar if ((mouse.getType() == TMouseEvent.Type.MOUSE_DOWN) - && (mouse.getMouse1()) + && (mouse.isMouse1()) && (!modalWindowActive()) && (mouse.getAbsoluteY() == 0) ) { @@ -1203,13 +1263,12 @@ public class TApplication { menu.setActive(false); } } - repaint = true; return; } // See if they hit the menu bar if ((mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) - && (mouse.getMouse1()) + && (mouse.isMouse1()) && (activeMenu != null) && (mouse.getAbsoluteY() == 0) ) { @@ -1234,7 +1293,6 @@ public class TApplication { // They switched menus oldMenu.setActive(false); } - repaint = true; return; } @@ -1248,29 +1306,30 @@ public class TApplication { return; } - Collections.sort(windows); - if (windows.get(0).isModal()) { - // Modal windows don't switch - return; - } + synchronized (windows) { + Collections.sort(windows); + if (windows.get(0).isModal()) { + // Modal windows don't switch + return; + } - for (TWindow window: windows) { - assert (!window.isModal()); - if (window.mouseWouldHit(mouse)) { - if (window == windows.get(0)) { - // Clicked on the same window, nothing to do + for (TWindow window: windows) { + assert (!window.isModal()); + if (window.mouseWouldHit(mouse)) { + if (window == windows.get(0)) { + // Clicked on the same window, nothing to do + return; + } + + // We will be switching to another window + assert (windows.get(0).isActive()); + assert (!window.isActive()); + windows.get(0).setActive(false); + windows.get(0).setZ(window.getZ()); + window.setZ(0); + window.setActive(true); return; } - - // We will be switching to another window - assert (windows.get(0).getActive()); - assert (!window.getActive()); - windows.get(0).setActive(false); - windows.get(0).setZ(window.getZ()); - window.setZ(0); - window.setActive(true); - repaint = true; - return; } } @@ -1290,7 +1349,6 @@ public class TApplication { } subMenus.clear(); } - repaint = true; } /** @@ -1302,7 +1360,6 @@ public class TApplication { assert (item != null); item.setActive(false); subMenus.remove(subMenus.size() - 1); - repaint = true; } /** @@ -1333,7 +1390,6 @@ public class TApplication { activeMenu.setActive(false); activeMenu = menus.get(i); activeMenu.setActive(true); - repaint = true; return; } } @@ -1353,29 +1409,24 @@ public class TApplication { TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) { quit = true; } - repaint = true; return true; } if (command.equals(cmShell)) { openTerminal(0, 0, TWindow.RESIZABLE); - repaint = true; return true; } if (command.equals(cmTile)) { tileWindows(); - repaint = true; return true; } if (command.equals(cmCascade)) { cascadeWindows(); - repaint = true; return true; } if (command.equals(cmCloseAll)) { closeAllWindows(); - repaint = true; return true; } @@ -1397,30 +1448,24 @@ public class TApplication { TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) { quit = true; } - // System.err.printf("onMenu MID_EXIT result: quit = %s\n", quit); - repaint = true; return true; } if (menu.getId() == TMenu.MID_SHELL) { openTerminal(0, 0, TWindow.RESIZABLE); - repaint = true; return true; } if (menu.getId() == TMenu.MID_TILE) { tileWindows(); - repaint = true; return true; } if (menu.getId() == TMenu.MID_CASCADE) { cascadeWindows(); - repaint = true; return true; } if (menu.getId() == TMenu.MID_CLOSE_ALL) { closeAllWindows(); - repaint = true; return true; } return false; @@ -1436,9 +1481,9 @@ public class TApplication { // Default: only menu shortcuts // Process Alt-F, Alt-E, etc. menu shortcut keys - if (!keypress.getKey().getIsKey() - && keypress.getKey().getAlt() - && !keypress.getKey().getCtrl() + if (!keypress.getKey().isFnKey() + && keypress.getKey().isAlt() + && !keypress.getKey().isCtrl() && (activeMenu == null) ) { @@ -1446,11 +1491,10 @@ public class TApplication { for (TMenu menu: menus) { if (Character.toLowerCase(menu.getMnemonic().getShortcut()) - == Character.toLowerCase(keypress.getKey().getCh()) + == Character.toLowerCase(keypress.getKey().getChar()) ) { activeMenu = menu; menu.setActive(true); - repaint = true; return true; } } @@ -1468,8 +1512,6 @@ public class TApplication { public final void addAccelerator(final TMenuItem item, final TKeypress keypress) { - // System.err.printf("addAccelerator: key %s item %s\n", keypress, item); - synchronized (accelerators) { assert (accelerators.get(keypress) == null); accelerators.put(keypress, item); @@ -1578,8 +1620,11 @@ public class TApplication { if (activeMenu != null) { return; } - for (TWindow window: windows) { - closeWindow(window); + + synchronized (windows) { + for (TWindow window: windows) { + closeWindow(window); + } } } @@ -1588,52 +1633,54 @@ public class TApplication { * almost the same results as Turbo Pascal 7.0's IDE. */ private void tileWindows() { - // Don't do anything if we are in the menu - if (activeMenu != null) { - return; - } - int z = windows.size(); - if (z == 0) { - return; - } - int a = 0; - int b = 0; - a = (int)(Math.sqrt(z)); - int c = 0; - while (c < a) { - b = (z - c) / a; - if (((a * b) + c) == z) { - break; + synchronized (windows) { + // Don't do anything if we are in the menu + if (activeMenu != null) { + return; } - c++; - } - assert (a > 0); - assert (b > 0); - assert (c < a); - int newWidth = (getScreen().getWidth() / a); - int newHeight1 = ((getScreen().getHeight() - 1) / b); - int newHeight2 = ((getScreen().getHeight() - 1) / (b + c)); - - List sorted = new LinkedList(windows); - Collections.sort(sorted); - Collections.reverse(sorted); - for (int i = 0; i < sorted.size(); i++) { - int logicalX = i / b; - int logicalY = i % b; - if (i >= ((a - 1) * b)) { - logicalX = a - 1; - logicalY = i - ((a - 1) * b); + int z = windows.size(); + if (z == 0) { + return; + } + int a = 0; + int b = 0; + a = (int)(Math.sqrt(z)); + int c = 0; + while (c < a) { + b = (z - c) / a; + if (((a * b) + c) == z) { + break; + } + c++; } + assert (a > 0); + assert (b > 0); + assert (c < a); + int newWidth = (getScreen().getWidth() / a); + int newHeight1 = ((getScreen().getHeight() - 1) / b); + int newHeight2 = ((getScreen().getHeight() - 1) / (b + c)); + + List sorted = new LinkedList(windows); + Collections.sort(sorted); + Collections.reverse(sorted); + for (int i = 0; i < sorted.size(); i++) { + int logicalX = i / b; + int logicalY = i % b; + if (i >= ((a - 1) * b)) { + logicalX = a - 1; + logicalY = i - ((a - 1) * b); + } - TWindow w = sorted.get(i); - w.setX(logicalX * newWidth); - w.setWidth(newWidth); - if (i >= ((a - 1) * b)) { - w.setY((logicalY * newHeight2) + 1); - w.setHeight(newHeight2); - } else { - w.setY((logicalY * newHeight1) + 1); - w.setHeight(newHeight1); + TWindow w = sorted.get(i); + w.setX(logicalX * newWidth); + w.setWidth(newWidth); + if (i >= ((a - 1) * b)) { + w.setY((logicalY * newHeight2) + 1); + w.setHeight(newHeight2); + } else { + w.setY((logicalY * newHeight1) + 1); + w.setHeight(newHeight1); + } } } } @@ -1642,25 +1689,27 @@ public class TApplication { * Re-layout the open windows as overlapping cascaded windows. */ private void cascadeWindows() { - // Don't do anything if we are in the menu - if (activeMenu != null) { - return; - } - int x = 0; - int y = 1; - List sorted = new LinkedList(windows); - Collections.sort(sorted); - Collections.reverse(sorted); - for (TWindow window: sorted) { - window.setX(x); - window.setY(y); - x++; - y++; - if (x > getScreen().getWidth()) { - x = 0; + synchronized (windows) { + // Don't do anything if we are in the menu + if (activeMenu != null) { + return; } - if (y >= getScreen().getHeight()) { - y = 1; + int x = 0; + int y = 1; + List sorted = new LinkedList(windows); + Collections.sort(sorted); + Collections.reverse(sorted); + for (TWindow window: sorted) { + window.setX(x); + window.setY(y); + x++; + y++; + if (x > getScreen().getWidth()) { + x = 0; + } + if (y >= getScreen().getHeight()) { + y = 1; + } } } }