*/
package jexer;
+import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
+import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import jexer.backend.TWindowBackend;
import jexer.menu.TMenu;
import jexer.menu.TMenuItem;
+import jexer.menu.TSubMenu;
import static jexer.TCommand.*;
import static jexer.TKeypress.*;
// resumes working on the primary.
application.secondaryEventHandler = null;
- // DO NOT UNLOCK. Primary thread just came back from
- // primaryHandleEvent() and will unlock in the else
- // block below. Just wake it up.
+ // We are ready to exit, wake up the primary thread.
+ // Remember that it is currently sleeping inside its
+ // primaryHandleEvent().
synchronized (application.primaryEventHandler) {
application.primaryEventHandler.notify();
}
* Run this application until it exits.
*/
public void run() {
+ // System.err.println("*** TApplication.run() begins ***");
+
// Start the main consumer thread
primaryEventHandler = new WidgetEventHandler(this, true);
(new Thread(primaryEventHandler)).start();
// resources.
closeAllWindows();
+ // Give the overarching application an opportunity to release
+ // resources.
+ onExit();
+
+ // System.err.println("*** TApplication.run() exits ***");
}
// ------------------------------------------------------------------------
closeAllWindows();
return true;
}
+ if (menu.getId() == TMenu.MID_ABOUT) {
+ showAboutDialog();
+ return true;
+ }
if (menu.getId() == TMenu.MID_REPAINT) {
getScreen().clearPhysical();
doRepaint();
return true;
}
+ if (menu.getId() == TMenu.MID_VIEW_IMAGE) {
+ openImage();
+ return true;
+ }
+ if (menu.getId() == TMenu.MID_CHANGE_FONT) {
+ new TFontChooserWindow(this);
+ return true;
+ }
return false;
}
// Abort everything
if (event instanceof TCommandEvent) {
TCommandEvent command = (TCommandEvent) event;
- if (command.getCmd().equals(cmAbort)) {
+ if (command.equals(cmAbort)) {
exit();
return;
}
if (desktop != null) {
desktop.setDimensions(0, 0, resize.getWidth(),
resize.getHeight() - 1);
+ desktop.onResize(resize);
}
// Change menu edges if needed.
mouseX = mouse.getX();
mouseY = mouse.getY();
} else {
- if (mouse.getType() == TMouseEvent.Type.MOUSE_UP) {
+ if ((mouse.getType() == TMouseEvent.Type.MOUSE_DOWN)
+ && (!mouse.isMouseWheelUp())
+ && (!mouse.isMouseWheelDown())
+ ) {
if ((mouse.getTime().getTime() - lastMouseUpTime) <
doubleClickTime) {
mouseX = mouse.getX();
mouseY = mouse.getY();
} else {
- if (mouse.getType() == TMouseEvent.Type.MOUSE_UP) {
+ if ((mouse.getType() == TMouseEvent.Type.MOUSE_DOWN)
+ && (!mouse.isMouseWheelUp())
+ && (!mouse.isMouseWheelDown())
+ ) {
if ((mouse.getTime().getTime() - lastMouseUpTime) <
doubleClickTime) {
synchronized (invokeLaters) {
invokeLaters.add(command);
}
+ doRepaint();
}
/**
this.focusFollowsMouse = focusFollowsMouse;
}
+ /**
+ * Display the about dialog.
+ */
+ protected void showAboutDialog() {
+ String version = getClass().getPackage().getImplementationVersion();
+ if (version == null) {
+ // This is Java 9+, use a hardcoded string here.
+ version = "0.3.1";
+ }
+ messageBox(i18n.getString("aboutDialogTitle"),
+ MessageFormat.format(i18n.getString("aboutDialogText"), version),
+ TMessageBox.Type.OK);
+ }
+
+ /**
+ * Handle the Tool | Open image menu item.
+ */
+ private void openImage() {
+ try {
+ List<String> filters = new ArrayList<String>();
+ filters.add("^.*\\.[Jj][Pp][Gg]$");
+ filters.add("^.*\\.[Jj][Pp][Ee][Gg]$");
+ filters.add("^.*\\.[Pp][Nn][Gg]$");
+ filters.add("^.*\\.[Gg][Ii][Ff]$");
+ filters.add("^.*\\.[Bb][Mm][Pp]$");
+ String filename = fileOpenBox(".", TFileOpenBox.Type.OPEN, filters);
+ if (filename != null) {
+ new TImageWindow(this, new File(filename));
+ }
+ } catch (IOException e) {
+ // Show this exception to the user.
+ new TExceptionDialog(this, e);
+ }
+ }
+
+ /**
+ * Check if application is still running.
+ *
+ * @return true if the application is running
+ */
+ public final boolean isRunning() {
+ if (quit == true) {
+ return false;
+ }
+ return true;
+ }
+
// ------------------------------------------------------------------------
// Screen refresh loop ----------------------------------------------------
// ------------------------------------------------------------------------
if (debugThreads) {
System.err.printf("%d %s invertCell() %d %d\n",
System.currentTimeMillis(), Thread.currentThread(), x, y);
+
+ if (activeWindow != null) {
+ System.err.println("activeWindow.hasHiddenMouse() " +
+ activeWindow.hasHiddenMouse());
+ }
}
+
+ // If this cell is on top of a visible window that has requested a
+ // hidden mouse, bail out.
+ if ((activeWindow != null) && (activeMenu == null)) {
+ if ((activeWindow.hasHiddenMouse() == true)
+ && (x > activeWindow.getX())
+ && (x < activeWindow.getX() + activeWindow.getWidth() - 1)
+ && (y > activeWindow.getY())
+ && (y < activeWindow.getY() + activeWindow.getHeight() - 1)
+ ) {
+ return;
+ }
+ }
+
Cell cell = getScreen().getCharXY(x, y);
if (cell.isImage()) {
cell.invertImage();
// Flush the screen contents
if ((images.size() > 0) || getScreen().isDirty()) {
+ if (debugThreads) {
+ System.err.printf("%d %s backend.flushScreen()\n",
+ System.currentTimeMillis(), Thread.currentThread());
+ }
backend.flushScreen();
}
}
}
+ /**
+ * Subclasses can use this hook to cleanup resources. Called as the last
+ * step of TApplication.run().
+ */
+ public void onExit() {
+ // Default does nothing.
+ }
+
// ------------------------------------------------------------------------
// TWindow management -----------------------------------------------------
// ------------------------------------------------------------------------
assert (!window.isActive());
if (activeWindow != null) {
- assert (activeWindow.getZ() == 0);
+ // TODO: see if this assertion is really necessary.
+ // assert (activeWindow.getZ() == 0);
activeWindow.setActive(false);
int z = window.getZ();
window.setZ(-1);
window.onUnfocus();
+ windows.remove(window);
Collections.sort(windows);
- windows.remove(0);
activeWindow = null;
+ int newZ = 0;
+ boolean foundNextWindow = false;
+
for (TWindow w: windows) {
+ w.setZ(newZ);
+ newZ++;
// Do not activate a hidden window.
if (w.isHidden()) {
continue;
}
- if (w.getZ() > z) {
- w.setZ(w.getZ() - 1);
- if (w.getZ() == 0) {
- w.setActive(true);
- w.onFocus();
- assert (activeWindow == null);
- activeWindow = w;
- } else {
- if (w.isActive()) {
- w.setActive(false);
- w.onUnfocus();
- }
- }
+ if (foundNextWindow == false) {
+ foundNextWindow = true;
+ w.setActive(true);
+ w.onFocus();
+ assert (activeWindow == null);
+ activeWindow = w;
+ continue;
+ }
+
+ if (w.isActive()) {
+ w.setActive(false);
+ w.onUnfocus();
}
}
}
return false;
}
+ /**
+ * Check if there is a window with overridden menu flag on top.
+ *
+ * @return true if the active window is overriding the menu
+ */
+ private boolean overrideMenuWindowActive() {
+ if (activeWindow != null) {
+ if (activeWindow.hasOverriddenMenu()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* Close all open windows.
*/
if ((mouse.getType() == TMouseEvent.Type.MOUSE_DOWN)
&& (mouse.isMouse1())
&& (!modalWindowActive())
+ && (!overrideMenuWindowActive())
&& (mouse.getAbsoluteY() == 0)
) {
if (((focusFollowsMouse == true)
&& (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION))
- || (mouse.getType() == TMouseEvent.Type.MOUSE_UP)
+ || (mouse.getType() == TMouseEvent.Type.MOUSE_DOWN)
) {
synchronized (windows) {
Collections.sort(windows);
return menu;
}
+ /**
+ * Convenience function to add a default tools (hamburger) menu.
+ *
+ * @return the new menu
+ */
+ public final TMenu addToolMenu() {
+ TMenu toolMenu = addMenu(i18n.getString("toolMenuTitle"));
+ toolMenu.addDefaultItem(TMenu.MID_REPAINT);
+ toolMenu.addDefaultItem(TMenu.MID_VIEW_IMAGE);
+ toolMenu.addDefaultItem(TMenu.MID_CHANGE_FONT);
+ TStatusBar toolStatusBar = toolMenu.newStatusBar(i18n.
+ getString("toolMenuStatus"));
+ toolStatusBar.addShortcutKeypress(kbF1, cmHelp, i18n.getString("Help"));
+ return toolMenu;
+ }
+
/**
* Convenience function to add a default "File" menu.
*
*/
public final TMenu addFileMenu() {
TMenu fileMenu = addMenu(i18n.getString("fileMenuTitle"));
- fileMenu.addDefaultItem(TMenu.MID_OPEN_FILE);
- fileMenu.addSeparator();
fileMenu.addDefaultItem(TMenu.MID_SHELL);
+ fileMenu.addSeparator();
fileMenu.addDefaultItem(TMenu.MID_EXIT);
TStatusBar statusBar = fileMenu.newStatusBar(i18n.
getString("fileMenuStatus"));
return helpMenu;
}
+ /**
+ * Convenience function to add a default "Table" menu.
+ *
+ * @return the new menu
+ */
+ public final TMenu addTableMenu() {
+ TMenu tableMenu = addMenu(i18n.getString("tableMenuTitle"));
+ TSubMenu borderMenu = tableMenu.addSubMenu(i18n.
+ getString("tableSubMenuBorders"));
+ borderMenu.addDefaultItem(TMenu.MID_TABLE_BORDER_NONE, false);
+ borderMenu.addDefaultItem(TMenu.MID_TABLE_BORDER_ALL, false);
+ borderMenu.addDefaultItem(TMenu.MID_TABLE_BORDER_RIGHT, false);
+ borderMenu.addDefaultItem(TMenu.MID_TABLE_BORDER_LEFT, false);
+ borderMenu.addDefaultItem(TMenu.MID_TABLE_BORDER_TOP, false);
+ borderMenu.addDefaultItem(TMenu.MID_TABLE_BORDER_BOTTOM, false);
+ borderMenu.addDefaultItem(TMenu.MID_TABLE_BORDER_DOUBLE_BOTTOM, false);
+ borderMenu.addDefaultItem(TMenu.MID_TABLE_BORDER_THICK_BOTTOM, false);
+ TSubMenu deleteMenu = tableMenu.addSubMenu(i18n.
+ getString("tableSubMenuDelete"));
+ deleteMenu.addDefaultItem(TMenu.MID_TABLE_DELETE_LEFT, false);
+ deleteMenu.addDefaultItem(TMenu.MID_TABLE_DELETE_UP, false);
+ deleteMenu.addDefaultItem(TMenu.MID_TABLE_DELETE_ROW, false);
+ deleteMenu.addDefaultItem(TMenu.MID_TABLE_DELETE_COLUMN, false);
+ TSubMenu insertMenu = tableMenu.addSubMenu(i18n.
+ getString("tableSubMenuInsert"));
+ insertMenu.addDefaultItem(TMenu.MID_TABLE_INSERT_LEFT, false);
+ insertMenu.addDefaultItem(TMenu.MID_TABLE_INSERT_RIGHT, false);
+ insertMenu.addDefaultItem(TMenu.MID_TABLE_INSERT_ABOVE, false);
+ insertMenu.addDefaultItem(TMenu.MID_TABLE_INSERT_BELOW, false);
+ TSubMenu columnMenu = tableMenu.addSubMenu(i18n.
+ getString("tableSubMenuColumn"));
+ columnMenu.addDefaultItem(TMenu.MID_TABLE_COLUMN_NARROW, false);
+ columnMenu.addDefaultItem(TMenu.MID_TABLE_COLUMN_WIDEN, false);
+ TSubMenu fileMenu = tableMenu.addSubMenu(i18n.
+ getString("tableSubMenuFile"));
+ fileMenu.addDefaultItem(TMenu.MID_TABLE_FILE_SAVE_CSV, false);
+ fileMenu.addDefaultItem(TMenu.MID_TABLE_FILE_SAVE_TEXT, false);
+
+ TStatusBar statusBar = tableMenu.newStatusBar(i18n.
+ getString("tableMenuStatus"));
+ statusBar.addShortcutKeypress(kbF1, cmHelp, i18n.getString("Help"));
+ return tableMenu;
+ }
+
// ------------------------------------------------------------------------
// TTimer management ------------------------------------------------------
// ------------------------------------------------------------------------
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"));
+ return new TTerminalWindow(this, x, y, flags, commandLine.split("\\s+"));
}
/**
public final TTerminalWindow openTerminal(final int x, final int y,
final int flags, final String commandLine, final boolean closeOnExit) {
- return new TTerminalWindow(this, x, y, flags, commandLine.split("\\s"),
+ return new TTerminalWindow(this, x, y, flags, commandLine.split("\\s+"),
closeOnExit);
}