X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2FTApplication.java;h=b7c5f8ef51e5c63f30605d840d8dab0967f1b86b;hb=b2d49e0f15810a35a206e88c9bad11b053ed65fc;hp=3d93e56b3af26e67fe7b3a4899fedf7988948245;hpb=68c5cd6bec3dc425ef5b55fec399d4bdc9afa7cb;p=fanfix.git diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index 3d93e56..b7c5f8e 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; @@ -1070,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 ----------------------------------- @@ -1081,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 @@ -1188,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; } @@ -1200,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); + } } } } @@ -1218,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; @@ -1226,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); + } } /** @@ -1430,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); @@ -2009,8 +2093,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); @@ -2037,8 +2121,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); @@ -2299,6 +2383,7 @@ 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 @@ -2309,6 +2394,24 @@ public class TApplication implements Runnable { } } + /** + * 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(); + } + } + /** * Post an event to process and turn off the menu. * @@ -2712,6 +2815,37 @@ 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 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); + } + /** * Convenience function to spawn an file open box. * @@ -2754,6 +2888,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). @@ -2804,4 +2939,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; + } + }