X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2FTApplication.java;h=e6c5819a22c58fb2195e0cfe185a7c4d8c43a2b1;hb=be9987235887cb73637b91ab6921158ecaef9d95;hp=66f711017604bfb1f37b89fb5ad91612ec4eee78;hpb=1c19fdeaff911aa6e9e028f3ad4db06c8d0597dd;p=fanfix.git diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index 66f7110..e6c5819 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -28,6 +28,7 @@ */ package jexer; +import java.io.File; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; @@ -282,6 +283,10 @@ public class TApplication implements Runnable { * Wake the sleeping active event handler. */ private void wakeEventHandler() { + if (!started) { + return; + } + if (secondaryEventHandler != null) { synchronized (secondaryEventHandler) { secondaryEventHandler.notify(); @@ -349,6 +354,17 @@ public class TApplication implements Runnable { */ private int oldMouseY; + /** + * The last mouse up click time, used to determine if this is a mouse + * double-click. + */ + private long lastMouseUpTime; + + /** + * The amount of millis between mouse up events to assume a double-click. + */ + private long doubleClickTime = 250; + /** * Event queue that is filled by run(). */ @@ -414,6 +430,11 @@ public class TApplication implements Runnable { */ private List timers; + /** + * When true, the application has been started. + */ + private volatile boolean started = false; + /** * When true, exit the application. */ @@ -919,6 +940,8 @@ public class TApplication implements Runnable { primaryEventHandler = new WidgetEventHandler(this, true); (new Thread(primaryEventHandler)).start(); + started = true; + while (!quit) { synchronized (this) { boolean doWait = false; @@ -1043,6 +1066,10 @@ public class TApplication implements Runnable { desktop.setDimensions(0, 0, resize.getWidth(), resize.getHeight() - 1); } + + // Change menu edges if needed. + recomputeMenuX(); + // We are dirty, redraw the screen. doRepaint(); return; @@ -1066,6 +1093,7 @@ public class TApplication implements Runnable { if (debugEvents) { System.err.printf("Handle event: %s\n", event); } + TMouseEvent doubleClick = null; // Special application-wide events ----------------------------------- @@ -1077,6 +1105,25 @@ public class TApplication implements Runnable { oldMouseY = mouseY; mouseX = mouse.getX(); mouseY = mouse.getY(); + } else { + if (mouse.getType() == TMouseEvent.Type.MOUSE_UP) { + if ((mouse.getTime().getTime() - lastMouseUpTime) < + doubleClickTime) { + + // This is a double-click. + doubleClick = new TMouseEvent(TMouseEvent.Type. + MOUSE_DOUBLE_CLICK, + mouse.getX(), mouse.getY(), + mouse.getAbsoluteX(), mouse.getAbsoluteY(), + mouse.isMouse1(), mouse.isMouse2(), + mouse.isMouse3(), + mouse.isMouseWheelUp(), mouse.isMouseWheelDown()); + + } else { + // The first click of a potential double-click. + lastMouseUpTime = mouse.getTime().getTime(); + } + } } // See if we need to switch focus to another window or the menu @@ -1184,6 +1231,11 @@ public class TApplication implements Runnable { mouse.setX(mouse.getX() - window.getX()); mouse.setY(mouse.getY() - window.getY()); + if (doubleClick != null) { + doubleClick.setX(doubleClick.getX() - window.getX()); + doubleClick.setY(doubleClick.getY() - window.getY()); + } + if (window.mouseWouldHit(mouse)) { dispatchToDesktop = false; } @@ -1196,11 +1248,17 @@ public class TApplication implements Runnable { event); } window.handleEvent(event); + if (doubleClick != null) { + window.handleEvent(doubleClick); + } } if (dispatchToDesktop) { // This event is fair game for the desktop to process. if (desktop != null) { desktop.handleEvent(event); + if (doubleClick != null) { + desktop.handleEvent(doubleClick); + } } } } @@ -1214,6 +1272,8 @@ public class TApplication implements Runnable { * @see #primaryHandleEvent(TInputEvent event) */ private void secondaryHandleEvent(final TInputEvent event) { + TMouseEvent doubleClick = null; + // Peek at the mouse position if (event instanceof TMouseEvent) { TMouseEvent mouse = (TMouseEvent) event; @@ -1222,10 +1282,32 @@ public class TApplication implements Runnable { oldMouseY = mouseY; mouseX = mouse.getX(); mouseY = mouse.getY(); + } else { + if (mouse.getType() == TMouseEvent.Type.MOUSE_UP) { + if ((mouse.getTime().getTime() - lastMouseUpTime) < + doubleClickTime) { + + // This is a double-click. + doubleClick = new TMouseEvent(TMouseEvent.Type. + MOUSE_DOUBLE_CLICK, + mouse.getX(), mouse.getY(), + mouse.getAbsoluteX(), mouse.getAbsoluteY(), + mouse.isMouse1(), mouse.isMouse2(), + mouse.isMouse3(), + mouse.isMouseWheelUp(), mouse.isMouseWheelDown()); + + } else { + // The first click of a potential double-click. + lastMouseUpTime = mouse.getTime().getTime(); + } + } } } secondaryEventReceiver.handleEvent(event); + if (doubleClick != null) { + secondaryEventReceiver.handleEvent(doubleClick); + } } /** @@ -1426,9 +1508,15 @@ public class TApplication implements Runnable { if (activeWindow != null) { assert (activeWindow.getZ() == 0); - activeWindow.onUnfocus(); activeWindow.setActive(false); activeWindow.setZ(window.getZ()); + + // Unset activeWindow now before unfocus, so that a window + // lifecycle change inside onUnfocus() doesn't call + // switchWindow() and lead to a stack overflow. + TWindow oldActiveWindow = activeWindow; + activeWindow = null; + oldActiveWindow.onUnfocus(); } activeWindow = window; activeWindow.setZ(0); @@ -1535,6 +1623,12 @@ public class TApplication implements Runnable { windows.remove(0); activeWindow = null; for (TWindow w: windows) { + + // Do not activate a hidden window. + if (w.isHidden()) { + continue; + } + if (w.getZ() > z) { w.setZ(w.getZ() - 1); if (w.getZ() == 0) { @@ -1853,10 +1947,16 @@ public class TApplication implements Runnable { continue; } for (int x = w.getX(); x < w.getX() + w.getWidth(); x++) { + if (x < 0) { + continue; + } if (x >= width) { continue; } for (int y = w.getY(); y < w.getY() + w.getHeight(); y++) { + if (y < 0) { + continue; + } if (y >= height) { continue; } @@ -2005,8 +2105,8 @@ public class TApplication implements Runnable { // They selected the menu, go activate it for (TMenu menu: menus) { - if ((mouse.getAbsoluteX() >= menu.getX()) - && (mouse.getAbsoluteX() < menu.getX() + if ((mouse.getAbsoluteX() >= menu.getTitleX()) + && (mouse.getAbsoluteX() < menu.getTitleX() + menu.getTitle().length() + 2) ) { menu.setActive(true); @@ -2033,8 +2133,8 @@ public class TApplication implements Runnable { // See if we should switch menus for (TMenu menu: menus) { - if ((mouse.getAbsoluteX() >= menu.getX()) - && (mouse.getAbsoluteX() < menu.getX() + if ((mouse.getAbsoluteX() >= menu.getTitleX()) + && (mouse.getAbsoluteX() < menu.getTitleX() + menu.getTitle().length() + 2) ) { menu.setActive(true); @@ -2295,7 +2395,32 @@ public class TApplication implements Runnable { int x = 0; for (TMenu menu: menus) { menu.setX(x); + menu.setTitleX(x); x += menu.getTitle().length() + 2; + + // Don't let the menu window exceed the screen width + int rightEdge = menu.getX() + menu.getWidth(); + if (rightEdge > getScreen().getWidth()) { + menu.setX(getScreen().getWidth() - menu.getWidth()); + } + } + } + + /** + * Post an event to process. + * + * @param event new event to add to the queue + */ + public final void postEvent(final TInputEvent event) { + synchronized (this) { + synchronized (fillEventQueue) { + fillEventQueue.add(event); + } + if (debugThreads) { + System.err.println(System.currentTimeMillis() + " " + + Thread.currentThread() + " postEvent() wake up main"); + } + this.notify(); } } @@ -2702,6 +2827,53 @@ public class TApplication implements Runnable { return new TTerminalWindow(this, x, y, flags); } + /** + * Convenience function to open a terminal window and execute a custom + * command line inside it. + * + * @param x column relative to parent + * @param y row relative to parent + * @param commandLine the command line to execute + * @return the terminal new window + */ + public final TTerminalWindow openTerminal(final int x, final int y, + final String commandLine) { + + return openTerminal(x, y, TWindow.RESIZABLE, commandLine); + } + + /** + * Convenience function to open a terminal window and execute a custom + * command line inside it. + * + * @param x column relative to parent + * @param y row relative to parent + * @param flags mask of CENTERED, MODAL, or RESIZABLE + * @param command the command line to execute + * @return the terminal new window + */ + public final TTerminalWindow openTerminal(final int x, final int y, + final int flags, final String [] command) { + + return new TTerminalWindow(this, x, y, flags, command); + } + + /** + * Convenience function to open a terminal window and execute a custom + * command line inside it. + * + * @param x column relative to parent + * @param y row relative to parent + * @param flags mask of CENTERED, MODAL, or RESIZABLE + * @param commandLine the command line to execute + * @return the terminal new window + */ + public final TTerminalWindow openTerminal(final int x, final int y, + final int flags, final String commandLine) { + + return new TTerminalWindow(this, x, y, flags, commandLine.split("\\s")); + } + /** * Convenience function to spawn an file open box. * @@ -2744,6 +2916,7 @@ public class TApplication implements Runnable { TWindow window = new TWindow(this, title, 0, 0, width, height); return window; } + /** * Convenience function to create a new window and make it active. * Window will be located at (0, 0). @@ -2794,4 +2967,17 @@ public class TApplication implements Runnable { return window; } + /** + * Convenience function to open a file in an editor window and make it + * active. + * + * @param file the file to open + * @throws IOException if a java.io operation throws + */ + public final TEditorWindow addEditor(final File file) throws IOException { + + TEditorWindow editor = new TEditorWindow(this, file); + return editor; + } + }