import jexer.bits.CellAttributes;
import jexer.bits.ColorTheme;
-import jexer.bits.GraphicsChars;
import jexer.event.TCommandEvent;
import jexer.event.TInputEvent;
import jexer.event.TKeypressEvent;
import jexer.event.TMouseEvent;
import jexer.event.TResizeEvent;
import jexer.backend.Backend;
+import jexer.backend.Screen;
import jexer.backend.SwingBackend;
import jexer.backend.ECMA48Backend;
-import jexer.io.Screen;
+import jexer.backend.TWindowBackend;
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.
+ * TApplication is the main driver class for a full Text User Interface
+ * application. It manages windows, provides a menu bar and status bar, and
+ * processes events received from the user.
*/
public class TApplication implements Runnable {
* @return the Screen
*/
public final Screen getScreen() {
- return backend.getScreen();
+ if (backend instanceof TWindowBackend) {
+ // We are being rendered to a TWindow. We can't use its
+ // getScreen() method because that is how it is rendering to a
+ // hardware backend somewhere. Instead use its getOtherScreen()
+ // method.
+ return ((TWindowBackend) backend).getOtherScreen();
+ } else {
+ return backend.getScreen();
+ }
}
/**
}
/**
- * Get the list of windows.
+ * Get a (shallow) copy of the window list.
*
* @return a copy of the list of windows for this application
*/
return result;
}
+ /**
+ * If true, focus follows mouse: windows automatically raised if the
+ * mouse passes over them.
+ */
+ private boolean focusFollowsMouse = false;
+
+ /**
+ * Get focusFollowsMouse flag.
+ *
+ * @return true if focus follows mouse: windows automatically raised if
+ * the mouse passes over them
+ */
+ public boolean getFocusFollowsMouse() {
+ return focusFollowsMouse;
+ }
+
+ /**
+ * Set focusFollowsMouse flag.
+ *
+ * @param focusFollowsMouse if true, focus follows mouse: windows
+ * automatically raised if the mouse passes over them
+ */
+ public void setFocusFollowsMouse(final boolean focusFollowsMouse) {
+ this.focusFollowsMouse = focusFollowsMouse;
+ }
+
// ------------------------------------------------------------------------
// General behavior -------------------------------------------------------
// ------------------------------------------------------------------------
switch (backendType) {
case SWING:
+ // The default SwingBackend is 80x25, 20 pt font. If you want to
+ // change that, you can pass the extra arguments to the
+ // SwingBackend constructor here. For example, if you wanted
+ // 90x30, 16 pt font:
+ //
+ // backend = new SwingBackend(this, 90, 30, 16);
backend = new SwingBackend(this);
break;
case XTERM:
*/
public TApplication(final Backend backend) {
this.backend = backend;
+ backend.setListener(this);
TApplicationImpl();
}
// Main loop --------------------------------------------------------------
// ------------------------------------------------------------------------
+ /**
+ * Force this application to exit.
+ */
+ public void exit() {
+ quit = true;
+ }
+
/**
* Run this application until it exits.
*/
return;
}
- // Peek at the mouse position
- if (event instanceof TMouseEvent) {
- TMouseEvent mouse = (TMouseEvent) event;
- synchronized (getScreen()) {
- if ((mouseX != mouse.getX()) || (mouseY != mouse.getY())) {
- oldMouseX = mouseX;
- oldMouseY = mouseY;
- mouseX = mouse.getX();
- mouseY = mouse.getY();
- }
- }
- }
-
// Put into the main queue
drainEventQueue.add(event);
}
// Peek at the mouse position
if (event instanceof TMouseEvent) {
+ TMouseEvent mouse = (TMouseEvent) event;
+ if ((mouseX != mouse.getX()) || (mouseY != mouse.getY())) {
+ oldMouseX = mouseX;
+ oldMouseY = mouseY;
+ mouseX = mouse.getX();
+ mouseY = mouse.getY();
+ }
+
// See if we need to switch focus to another window or the menu
checkSwitchFocus((TMouseEvent) event);
}
* @see #primaryHandleEvent(TInputEvent event)
*/
private void secondaryHandleEvent(final TInputEvent event) {
+ // Peek at the mouse position
+ if (event instanceof TMouseEvent) {
+ TMouseEvent mouse = (TMouseEvent) event;
+ if ((mouseX != mouse.getX()) || (mouseY != mouse.getY())) {
+ oldMouseX = mouseX;
+ oldMouseY = mouseY;
+ mouseX = mouse.getX();
+ mouseY = mouse.getY();
+ }
+ }
+
secondaryEventReceiver.handleEvent(event);
}
return;
}
+ // Whatever window might be moving/dragging, stop it now.
+ for (TWindow w: windows) {
+ if (w.inMovements()) {
+ w.stopMovements();
+ }
+ }
+
assert (windows.size() > 0);
if (window.isHidden()) {
return;
}
+ // Whatever window might be moving/dragging, stop it now.
+ for (TWindow w: windows) {
+ if (w.inMovements()) {
+ w.stopMovements();
+ }
+ }
+
assert (windows.size() > 0);
if (!window.hidden) {
return;
}
+ // Whatever window might be moving/dragging, stop it now.
+ for (TWindow w: windows) {
+ if (w.inMovements()) {
+ w.stopMovements();
+ }
+ }
+
assert (windows.size() > 0);
if (window.hidden) {
}
synchronized (windows) {
+ // Whatever window might be moving/dragging, stop it now.
+ for (TWindow w: windows) {
+ if (w.inMovements()) {
+ w.stopMovements();
+ }
+ }
+
int z = window.getZ();
window.setZ(-1);
window.onUnfocus();
assert (activeWindow != null);
synchronized (windows) {
+ // Whatever window might be moving/dragging, stop it now.
+ for (TWindow w: windows) {
+ if (w.inMovements()) {
+ w.stopMovements();
+ }
+ }
// Swap z/active between active window and the next in the list
int activeWindowI = -1;
}
synchronized (windows) {
+ // Whatever window might be moving/dragging, stop it now.
+ for (TWindow w: windows) {
+ if (w.inMovements()) {
+ w.stopMovements();
+ }
+ }
+
// Do not allow a modal window to spawn a non-modal window. If a
// modal window is active, then this window will become modal
// too.
return;
}
- // Only switch if there are multiple windows
- if (windows.size() < 2) {
+ // If a menu is still active, don't switch windows
+ if (activeMenu != null) {
return;
}
- // Switch on the upclick
- if (mouse.getType() != TMouseEvent.Type.MOUSE_UP) {
+ // Only switch if there are multiple windows
+ if (windows.size() < 2) {
return;
}
- synchronized (windows) {
- Collections.sort(windows);
- if (windows.get(0).isModal()) {
- // Modal windows don't switch
- return;
- }
+ if (((focusFollowsMouse == true)
+ && (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION))
+ || (mouse.getType() == TMouseEvent.Type.MOUSE_UP)
+ ) {
+ synchronized (windows) {
+ Collections.sort(windows);
+ if (windows.get(0).isModal()) {
+ // Modal windows don't switch
+ return;
+ }
- for (TWindow window: windows) {
- assert (!window.isModal());
+ for (TWindow window: windows) {
+ assert (!window.isModal());
- if (window.isHidden()) {
- assert (!window.isActive());
- continue;
- }
+ if (window.isHidden()) {
+ assert (!window.isActive());
+ continue;
+ }
- if (window.mouseWouldHit(mouse)) {
- if (window == windows.get(0)) {
- // Clicked on the same window, nothing to do
- assert (window.isActive());
+ if (window.mouseWouldHit(mouse)) {
+ if (window == windows.get(0)) {
+ // Clicked on the same window, nothing to do
+ assert (window.isActive());
+ return;
+ }
+
+ // We will be switching to another window
+ assert (windows.get(0).isActive());
+ assert (windows.get(0) == activeWindow);
+ assert (!window.isActive());
+ activeWindow.onUnfocus();
+ activeWindow.setActive(false);
+ activeWindow.setZ(window.getZ());
+ activeWindow = window;
+ window.setZ(0);
+ window.setActive(true);
+ window.onFocus();
return;
}
-
- // We will be switching to another window
- assert (windows.get(0).isActive());
- assert (windows.get(0) == activeWindow);
- assert (!window.isActive());
- activeWindow.onUnfocus();
- activeWindow.setActive(false);
- activeWindow.setZ(window.getZ());
- activeWindow = window;
- window.setZ(0);
- window.setActive(true);
- window.onFocus();
- return;
}
}
+
+ // Clicked on the background, nothing to do
+ return;
}
- // Clicked on the background, nothing to do
+ // Nothing to do: this isn't a mouse up, or focus isn't following
+ // mouse.
return;
}
}
}
+ /**
+ * Get a (shallow) copy of the menu list.
+ *
+ * @return a copy of the menu list
+ */
+ public final List<TMenu> getAllMenus() {
+ return new LinkedList<TMenu>(menus);
+ }
+
+ /**
+ * Add a top-level menu to the list.
+ *
+ * @param menu the menu to add
+ * @throws IllegalArgumentException if the menu is already used in
+ * another TApplication
+ */
+ public final void addMenu(final TMenu menu) {
+ if ((menu.getApplication() != null)
+ && (menu.getApplication() != this)
+ ) {
+ throw new IllegalArgumentException("Menu " + menu + " is already " +
+ "part of application " + menu.getApplication());
+ }
+ closeMenu();
+ menus.add(menu);
+ recomputeMenuX();
+ }
+
+ /**
+ * Remove a top-level menu from the list.
+ *
+ * @param menu the menu to remove
+ * @throws IllegalArgumentException if the menu is already used in
+ * another TApplication
+ */
+ public final void removeMenu(final TMenu menu) {
+ if ((menu.getApplication() != null)
+ && (menu.getApplication() != this)
+ ) {
+ throw new IllegalArgumentException("Menu " + menu + " is already " +
+ "part of application " + menu.getApplication());
+ }
+ closeMenu();
+ menus.remove(menu);
+ recomputeMenuX();
+ }
+
/**
* Turn off a sub-menu.
*/
return true;
}
+ if (command.equals(cmMenu)) {
+ if (!modalWindowActive() && (activeMenu == null)) {
+ if (menus.size() > 0) {
+ menus.get(0).setActive(true);
+ activeMenu = menus.get(0);
+ return true;
+ }
+ }
+ }
+
return false;
}
/**
* Convenience function to create a new window and make it active.
*
- * @param application TApplication that manages this window
* @param title window title, will be centered along the top border
* @param x column relative to parent
* @param y row relative to parent