Update tests and Status/UI
This commit is contained in:
@@ -45,6 +45,17 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
private FieldInspectorDialog fieldInspectorDialog;
|
private FieldInspectorDialog fieldInspectorDialog;
|
||||||
private PrinterSessionDialog printerSessionDialog;
|
private PrinterSessionDialog printerSessionDialog;
|
||||||
|
|
||||||
|
// Modes menu items
|
||||||
|
private JCheckBoxMenuItem docModeItem;
|
||||||
|
private JCheckBoxMenuItem wordWrapItem;
|
||||||
|
private JCheckBoxMenuItem aplModeItem;
|
||||||
|
private JCheckBoxMenuItem insertModeItem;
|
||||||
|
private JCheckBoxMenuItem fourColorItem;
|
||||||
|
private JCheckBoxMenuItem numLockItem;
|
||||||
|
private JCheckBoxMenuItem autoSkipItem;
|
||||||
|
private JCheckBoxMenuItem insertOffAidItem;
|
||||||
|
private JCheckBoxMenuItem statusBarItem;
|
||||||
|
|
||||||
public J3270App() {
|
public J3270App() {
|
||||||
super("j3270 — Java TN3270 Terminal Emulator");
|
super("j3270 — Java TN3270 Terminal Emulator");
|
||||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
@@ -63,6 +74,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
refreshTimer = new Timer(100, e -> {
|
refreshTimer = new Timer(100, e -> {
|
||||||
if (client != null) {
|
if (client != null) {
|
||||||
statusBar.updateStatus();
|
statusBar.updateStatus();
|
||||||
|
syncModeMenuItems();
|
||||||
if (isActive() && !terminalPanel.hasFocus()) {
|
if (isActive() && !terminalPanel.hasFocus()) {
|
||||||
terminalPanel.requestFocusInWindow();
|
terminalPanel.requestFocusInWindow();
|
||||||
}
|
}
|
||||||
@@ -93,6 +105,10 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
terminalPanel = new TerminalPanel();
|
terminalPanel = new TerminalPanel();
|
||||||
statusBar = new StatusBar();
|
statusBar = new StatusBar();
|
||||||
terminalPanel.setStatusBar(statusBar);
|
terminalPanel.setStatusBar(statusBar);
|
||||||
|
terminalPanel.setStatusBarToggleCallback(this::toggleStatusBar);
|
||||||
|
terminalPanel.addModeChangeListener(this::syncModeMenuItems);
|
||||||
|
|
||||||
|
statusBar.setVisible(haus.nightmare.j3270.config.Settings.getStatusBarVisible());
|
||||||
|
|
||||||
getContentPane().setLayout(new BorderLayout());
|
getContentPane().setLayout(new BorderLayout());
|
||||||
getContentPane().setBackground(Color.BLACK);
|
getContentPane().setBackground(Color.BLACK);
|
||||||
@@ -100,6 +116,17 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
getContentPane().add(statusBar, BorderLayout.SOUTH);
|
getContentPane().add(statusBar, BorderLayout.SOUTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void syncModeMenuItems() {
|
||||||
|
if (docModeItem != null) docModeItem.setSelected(terminalPanel.isDocMode());
|
||||||
|
if (wordWrapItem != null) wordWrapItem.setSelected(terminalPanel.isWordWrap());
|
||||||
|
if (aplModeItem != null) aplModeItem.setSelected(terminalPanel.isAplMode());
|
||||||
|
if (insertModeItem != null) insertModeItem.setSelected(terminalPanel.isInsertMode());
|
||||||
|
if (fourColorItem != null) fourColorItem.setSelected(haus.nightmare.j3270.config.Settings.getFourColorOverride());
|
||||||
|
if (numLockItem != null) numLockItem.setSelected(haus.nightmare.j3270.config.Settings.getNumericFieldLock());
|
||||||
|
if (autoSkipItem != null) autoSkipItem.setSelected(haus.nightmare.j3270.config.Settings.getAutoSkipEnabled());
|
||||||
|
if (insertOffAidItem != null) insertOffAidItem.setSelected(haus.nightmare.j3270.config.Settings.getInsertOffOnAid());
|
||||||
|
}
|
||||||
|
|
||||||
private void onThemeChanged(UITheme theme) {
|
private void onThemeChanged(UITheme theme) {
|
||||||
buildMenuBar();
|
buildMenuBar();
|
||||||
statusBar.applyTheme(theme);
|
statusBar.applyTheme(theme);
|
||||||
@@ -179,6 +206,12 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
viewMenu.add(themeMenu);
|
viewMenu.add(themeMenu);
|
||||||
viewMenu.addSeparator();
|
viewMenu.addSeparator();
|
||||||
|
|
||||||
|
statusBarItem = new JCheckBoxMenuItem("Status Bar", statusBar != null ? statusBar.isVisible() : haus.nightmare.j3270.config.Settings.getStatusBarVisible());
|
||||||
|
ThemeManager.styleMenuItem(statusBarItem);
|
||||||
|
statusBarItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, InputEvent.ALT_DOWN_MASK));
|
||||||
|
statusBarItem.addActionListener(e -> setStatusBarVisible(statusBarItem.isSelected()));
|
||||||
|
viewMenu.add(statusBarItem);
|
||||||
|
|
||||||
JCheckBoxMenuItem rulerItem = new JCheckBoxMenuItem("Crosshair Ruler", terminalPanel.isCrosshairRulerEnabled());
|
JCheckBoxMenuItem rulerItem = new JCheckBoxMenuItem("Crosshair Ruler", terminalPanel.isCrosshairRulerEnabled());
|
||||||
ThemeManager.styleMenuItem(rulerItem);
|
ThemeManager.styleMenuItem(rulerItem);
|
||||||
rulerItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.ALT_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
|
rulerItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.ALT_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
|
||||||
@@ -262,38 +295,33 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
|
|
||||||
// 4. Modes menu
|
// 4. Modes menu
|
||||||
JMenu modesMenu = createMenu("Modes");
|
JMenu modesMenu = createMenu("Modes");
|
||||||
JCheckBoxMenuItem docModeItem = new JCheckBoxMenuItem("Document Mode (DOC)", terminalPanel.isDocMode());
|
docModeItem = new JCheckBoxMenuItem("Document Mode (DOC)", terminalPanel.isDocMode());
|
||||||
ThemeManager.styleMenuItem(docModeItem);
|
ThemeManager.styleMenuItem(docModeItem);
|
||||||
docModeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, InputEvent.ALT_DOWN_MASK));
|
docModeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, InputEvent.ALT_DOWN_MASK));
|
||||||
docModeItem.addActionListener(e -> terminalPanel.toggleDocMode());
|
docModeItem.addActionListener(e -> terminalPanel.toggleDocMode());
|
||||||
modesMenu.add(docModeItem);
|
modesMenu.add(docModeItem);
|
||||||
|
|
||||||
JCheckBoxMenuItem wordWrapItem = new JCheckBoxMenuItem("Word Wrap Mode", terminalPanel.isWordWrap());
|
wordWrapItem = new JCheckBoxMenuItem("Word Wrap Mode", terminalPanel.isWordWrap());
|
||||||
ThemeManager.styleMenuItem(wordWrapItem);
|
ThemeManager.styleMenuItem(wordWrapItem);
|
||||||
wordWrapItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F2, InputEvent.ALT_DOWN_MASK));
|
wordWrapItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F2, InputEvent.ALT_DOWN_MASK));
|
||||||
wordWrapItem.addActionListener(e -> terminalPanel.toggleWordWrap());
|
wordWrapItem.addActionListener(e -> terminalPanel.toggleWordWrap());
|
||||||
modesMenu.add(wordWrapItem);
|
modesMenu.add(wordWrapItem);
|
||||||
|
|
||||||
JCheckBoxMenuItem aplModeItem = new JCheckBoxMenuItem("APL Keyboard Mode", terminalPanel.isAplMode());
|
aplModeItem = new JCheckBoxMenuItem("APL Keyboard Mode", terminalPanel.isAplMode());
|
||||||
ThemeManager.styleMenuItem(aplModeItem);
|
ThemeManager.styleMenuItem(aplModeItem);
|
||||||
aplModeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.ALT_DOWN_MASK));
|
aplModeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.ALT_DOWN_MASK));
|
||||||
aplModeItem.addActionListener(e -> terminalPanel.toggleAplMode());
|
aplModeItem.addActionListener(e -> terminalPanel.toggleAplMode());
|
||||||
modesMenu.add(aplModeItem);
|
modesMenu.add(aplModeItem);
|
||||||
|
|
||||||
JCheckBoxMenuItem insertModeItem = new JCheckBoxMenuItem("Insert Mode", client != null && client.isInsertMode());
|
insertModeItem = new JCheckBoxMenuItem("Insert Mode", terminalPanel.isInsertMode());
|
||||||
ThemeManager.styleMenuItem(insertModeItem);
|
ThemeManager.styleMenuItem(insertModeItem);
|
||||||
insertModeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0));
|
insertModeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0));
|
||||||
insertModeItem.addActionListener(e -> {
|
insertModeItem.addActionListener(e -> terminalPanel.toggleInsertMode());
|
||||||
if (client != null) {
|
|
||||||
client.toggleInsert();
|
|
||||||
terminalPanel.refreshScreen();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
modesMenu.add(insertModeItem);
|
modesMenu.add(insertModeItem);
|
||||||
|
|
||||||
modesMenu.addSeparator();
|
modesMenu.addSeparator();
|
||||||
|
|
||||||
JCheckBoxMenuItem fourColorItem = new JCheckBoxMenuItem("Base 4-Color Override", haus.nightmare.j3270.config.Settings.getFourColorOverride());
|
fourColorItem = new JCheckBoxMenuItem("Base 4-Color Override", haus.nightmare.j3270.config.Settings.getFourColorOverride());
|
||||||
ThemeManager.styleMenuItem(fourColorItem);
|
ThemeManager.styleMenuItem(fourColorItem);
|
||||||
fourColorItem.addActionListener(e -> {
|
fourColorItem.addActionListener(e -> {
|
||||||
haus.nightmare.j3270.config.Settings.setFourColorOverride(fourColorItem.isSelected());
|
haus.nightmare.j3270.config.Settings.setFourColorOverride(fourColorItem.isSelected());
|
||||||
@@ -301,7 +329,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
});
|
});
|
||||||
modesMenu.add(fourColorItem);
|
modesMenu.add(fourColorItem);
|
||||||
|
|
||||||
JCheckBoxMenuItem numLockItem = new JCheckBoxMenuItem("Numeric Field Lock", haus.nightmare.j3270.config.Settings.getNumericFieldLock());
|
numLockItem = new JCheckBoxMenuItem("Numeric Field Lock", haus.nightmare.j3270.config.Settings.getNumericFieldLock());
|
||||||
ThemeManager.styleMenuItem(numLockItem);
|
ThemeManager.styleMenuItem(numLockItem);
|
||||||
numLockItem.addActionListener(e -> {
|
numLockItem.addActionListener(e -> {
|
||||||
haus.nightmare.j3270.config.Settings.setNumericFieldLock(numLockItem.isSelected());
|
haus.nightmare.j3270.config.Settings.setNumericFieldLock(numLockItem.isSelected());
|
||||||
@@ -309,7 +337,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
});
|
});
|
||||||
modesMenu.add(numLockItem);
|
modesMenu.add(numLockItem);
|
||||||
|
|
||||||
JCheckBoxMenuItem autoSkipItem = new JCheckBoxMenuItem("Auto-Skip Across Fields", haus.nightmare.j3270.config.Settings.getAutoSkipEnabled());
|
autoSkipItem = new JCheckBoxMenuItem("Auto-Skip Across Fields", haus.nightmare.j3270.config.Settings.getAutoSkipEnabled());
|
||||||
ThemeManager.styleMenuItem(autoSkipItem);
|
ThemeManager.styleMenuItem(autoSkipItem);
|
||||||
autoSkipItem.addActionListener(e -> {
|
autoSkipItem.addActionListener(e -> {
|
||||||
haus.nightmare.j3270.config.Settings.setAutoSkipEnabled(autoSkipItem.isSelected());
|
haus.nightmare.j3270.config.Settings.setAutoSkipEnabled(autoSkipItem.isSelected());
|
||||||
@@ -317,7 +345,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
});
|
});
|
||||||
modesMenu.add(autoSkipItem);
|
modesMenu.add(autoSkipItem);
|
||||||
|
|
||||||
JCheckBoxMenuItem insertOffAidItem = new JCheckBoxMenuItem("Reset Insert on AID Key", haus.nightmare.j3270.config.Settings.getInsertOffOnAid());
|
insertOffAidItem = new JCheckBoxMenuItem("Reset Insert on AID Key", haus.nightmare.j3270.config.Settings.getInsertOffOnAid());
|
||||||
ThemeManager.styleMenuItem(insertOffAidItem);
|
ThemeManager.styleMenuItem(insertOffAidItem);
|
||||||
insertOffAidItem.addActionListener(e -> {
|
insertOffAidItem.addActionListener(e -> {
|
||||||
haus.nightmare.j3270.config.Settings.setInsertOffOnAid(insertOffAidItem.isSelected());
|
haus.nightmare.j3270.config.Settings.setInsertOffOnAid(insertOffAidItem.isSelected());
|
||||||
@@ -328,14 +356,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
modesMenu.addMenuListener(new javax.swing.event.MenuListener() {
|
modesMenu.addMenuListener(new javax.swing.event.MenuListener() {
|
||||||
@Override
|
@Override
|
||||||
public void menuSelected(javax.swing.event.MenuEvent e) {
|
public void menuSelected(javax.swing.event.MenuEvent e) {
|
||||||
docModeItem.setSelected(terminalPanel.isDocMode());
|
syncModeMenuItems();
|
||||||
wordWrapItem.setSelected(terminalPanel.isWordWrap());
|
|
||||||
aplModeItem.setSelected(terminalPanel.isAplMode());
|
|
||||||
insertModeItem.setSelected(client != null && client.isInsertMode());
|
|
||||||
fourColorItem.setSelected(haus.nightmare.j3270.config.Settings.getFourColorOverride());
|
|
||||||
numLockItem.setSelected(haus.nightmare.j3270.config.Settings.getNumericFieldLock());
|
|
||||||
autoSkipItem.setSelected(haus.nightmare.j3270.config.Settings.getAutoSkipEnabled());
|
|
||||||
insertOffAidItem.setSelected(haus.nightmare.j3270.config.Settings.getInsertOffOnAid());
|
|
||||||
}
|
}
|
||||||
@Override public void menuDeselected(javax.swing.event.MenuEvent e) {}
|
@Override public void menuDeselected(javax.swing.event.MenuEvent e) {}
|
||||||
@Override public void menuCanceled(javax.swing.event.MenuEvent e) {}
|
@Override public void menuCanceled(javax.swing.event.MenuEvent e) {}
|
||||||
@@ -726,6 +747,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
client = null;
|
client = null;
|
||||||
terminalPanel.setClient(null);
|
terminalPanel.setClient(null);
|
||||||
statusBar.setClient(null, terminalPanel);
|
statusBar.setClient(null, terminalPanel);
|
||||||
|
syncModeMenuItems();
|
||||||
terminalPanel.repaint();
|
terminalPanel.repaint();
|
||||||
setTitle("j3270 — Java TN3270 Terminal Emulator");
|
setTitle("j3270 — Java TN3270 Terminal Emulator");
|
||||||
}
|
}
|
||||||
@@ -738,6 +760,27 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
terminalPanel.guardedPack();
|
terminalPanel.guardedPack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void toggleStatusBar() {
|
||||||
|
if (statusBar != null) {
|
||||||
|
setStatusBarVisible(!statusBar.isVisible());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatusBarVisible(boolean visible) {
|
||||||
|
if (statusBar != null) {
|
||||||
|
statusBar.setVisible(visible);
|
||||||
|
}
|
||||||
|
haus.nightmare.j3270.config.Settings.setStatusBarVisible(visible);
|
||||||
|
if (statusBarItem != null) {
|
||||||
|
statusBarItem.setSelected(visible);
|
||||||
|
}
|
||||||
|
getContentPane().revalidate();
|
||||||
|
getContentPane().repaint();
|
||||||
|
if ((getExtendedState() & Frame.MAXIMIZED_BOTH) == 0) {
|
||||||
|
terminalPanel.guardedPack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ========== ConnectionListener ==========
|
// ========== ConnectionListener ==========
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -786,6 +829,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
}
|
}
|
||||||
terminalPanel.repaint();
|
terminalPanel.repaint();
|
||||||
statusBar.updateStatus();
|
statusBar.updateStatus();
|
||||||
|
syncModeMenuItems();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -818,7 +862,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
"End — Erase to end of field\n" +
|
"End — Erase to end of field\n" +
|
||||||
"Delete — Delete character\n" +
|
"Delete — Delete character\n" +
|
||||||
"Backspace — Backspace\n" +
|
"Backspace — Backspace\n" +
|
||||||
"Insert — Toggle insert mode\n" +
|
"Insert / Help / Alt+I — Toggle insert mode\n" +
|
||||||
"Escape / Alt+R — Reset\n" +
|
"Escape / Alt+R — Reset\n" +
|
||||||
"PageUp/Down — PF7/PF8\n" +
|
"PageUp/Down — PF7/PF8\n" +
|
||||||
"Alt+C / Alt+K — Clear\n" +
|
"Alt+C / Alt+K — Clear\n" +
|
||||||
@@ -829,7 +873,11 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
"Alt+L — Toggle Light Pen\n" +
|
"Alt+L — Toggle Light Pen\n" +
|
||||||
"Alt+T — File Transfer\n" +
|
"Alt+T — File Transfer\n" +
|
||||||
"Alt+Shift+R — Crosshair Ruler\n" +
|
"Alt+Shift+R — Crosshair Ruler\n" +
|
||||||
|
"Alt+B — Toggle Status Bar\n" +
|
||||||
"Alt+=/-/0 — Font size +/-/reset\n" +
|
"Alt+=/-/0 — Font size +/-/reset\n" +
|
||||||
|
"Cmd/Ctrl+C / Ctrl+Ins — Copy\n" +
|
||||||
|
"Cmd/Ctrl+V / Shift+Ins — Paste\n" +
|
||||||
|
"Cmd/Ctrl+A — Select All\n" +
|
||||||
"Cmd/Ctrl+F — Find on Screen\n" +
|
"Cmd/Ctrl+F — Find on Screen\n" +
|
||||||
"Cmd/Ctrl+G / F3— Find Next\n" +
|
"Cmd/Ctrl+G / F3— Find Next\n" +
|
||||||
"Cmd/Ctrl+D — Disconnect\n" +
|
"Cmd/Ctrl+D — Disconnect\n" +
|
||||||
|
|||||||
@@ -164,8 +164,61 @@ public class Settings {
|
|||||||
prefs.put("mono_" + key, String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue()));
|
prefs.put("mono_" + key, String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isMac() {
|
||||||
|
String os = System.getProperty("os.name");
|
||||||
|
return os != null && os.toLowerCase().contains("mac");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getDefaultBinding(String action) {
|
||||||
|
boolean mac = isMac();
|
||||||
|
switch (action) {
|
||||||
|
case "COPY":
|
||||||
|
return mac ? "meta C, ctrl C, ctrl INSERT, ctrl shift C" : "ctrl C, ctrl INSERT, ctrl shift C";
|
||||||
|
case "PASTE":
|
||||||
|
return mac ? "meta V, ctrl V, shift INSERT, ctrl shift V" : "ctrl V, shift INSERT, ctrl shift V";
|
||||||
|
case "SELECTALL":
|
||||||
|
return mac ? "meta A, ctrl A" : "ctrl A";
|
||||||
|
case "INSERT":
|
||||||
|
return mac ? "INSERT, HELP, alt I" : "INSERT, ctrl I";
|
||||||
|
case "ERASE_INPUT":
|
||||||
|
return "alt E";
|
||||||
|
case "NEWLINE":
|
||||||
|
return "shift ENTER";
|
||||||
|
case "DUP":
|
||||||
|
return "alt D";
|
||||||
|
case "FIELD_MARK":
|
||||||
|
return "alt M";
|
||||||
|
case "ATTN":
|
||||||
|
return "alt A";
|
||||||
|
case "SYSREQ":
|
||||||
|
return "alt S";
|
||||||
|
case "CURSEL":
|
||||||
|
return "alt Q";
|
||||||
|
case "CLEAR":
|
||||||
|
return "alt C";
|
||||||
|
case "STATUS_BAR":
|
||||||
|
return "alt B";
|
||||||
|
default:
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static String getKeyBinding(String action, String defaultBinding) {
|
public static String getKeyBinding(String action, String defaultBinding) {
|
||||||
return prefs.get("key_" + action, defaultBinding);
|
String val = prefs.get("key_" + action, null);
|
||||||
|
if (val == null) {
|
||||||
|
return defaultBinding;
|
||||||
|
}
|
||||||
|
// If stored binding is the legacy single-key "INSERT" default, migrate it to the current platform default
|
||||||
|
if ("INSERT".equals(action) && "INSERT".equals(val)) {
|
||||||
|
prefs.put("key_INSERT", defaultBinding);
|
||||||
|
return defaultBinding;
|
||||||
|
}
|
||||||
|
// If stored binding is the legacy or corrupted "alt STE" for PASTE, migrate it to the current platform default
|
||||||
|
if ("PASTE".equals(action) && ("alt STE".equals(val) || "PASTE".equals(val))) {
|
||||||
|
prefs.put("key_PASTE", defaultBinding);
|
||||||
|
return defaultBinding;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setKeyBinding(String action, String binding) {
|
public static void setKeyBinding(String action, String binding) {
|
||||||
@@ -268,6 +321,16 @@ public class Settings {
|
|||||||
prefs.putBoolean("crosshairRuler", enabled);
|
prefs.putBoolean("crosshairRuler", enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== Status Bar Visibility ==========
|
||||||
|
|
||||||
|
public static boolean getStatusBarVisible() {
|
||||||
|
return prefs.getBoolean("statusBarVisible", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setStatusBarVisible(boolean visible) {
|
||||||
|
prefs.putBoolean("statusBarVisible", visible);
|
||||||
|
}
|
||||||
|
|
||||||
// ========== Cursor Style ==========
|
// ========== Cursor Style ==========
|
||||||
|
|
||||||
public static String getCursorStyle() {
|
public static String getCursorStyle() {
|
||||||
@@ -388,6 +451,12 @@ public class Settings {
|
|||||||
case "ruler":
|
case "ruler":
|
||||||
setCrosshairRuler(Boolean.parseBoolean(value));
|
setCrosshairRuler(Boolean.parseBoolean(value));
|
||||||
break;
|
break;
|
||||||
|
case "statusBarVisible":
|
||||||
|
case "statusbar":
|
||||||
|
case "statusBar":
|
||||||
|
case "showStatusBar":
|
||||||
|
setStatusBarVisible(Boolean.parseBoolean(value));
|
||||||
|
break;
|
||||||
case "cursorStyle":
|
case "cursorStyle":
|
||||||
setCursorStyle(value);
|
setCursorStyle(value);
|
||||||
break;
|
break;
|
||||||
@@ -545,6 +614,7 @@ public class Settings {
|
|||||||
w.println("fontFamily = " + getFontFamily());
|
w.println("fontFamily = " + getFontFamily());
|
||||||
w.println("fontSize = " + getFontSize());
|
w.println("fontSize = " + getFontSize());
|
||||||
w.println("crosshairRuler = " + getCrosshairRuler());
|
w.println("crosshairRuler = " + getCrosshairRuler());
|
||||||
|
w.println("statusBarVisible = " + getStatusBarVisible());
|
||||||
w.println("cursorStyle = " + getCursorStyle());
|
w.println("cursorStyle = " + getCursorStyle());
|
||||||
w.println();
|
w.println();
|
||||||
|
|
||||||
@@ -610,9 +680,10 @@ public class Settings {
|
|||||||
|
|
||||||
// [keybindings]
|
// [keybindings]
|
||||||
w.println("[keybindings]");
|
w.println("[keybindings]");
|
||||||
// Navigation keys
|
// Navigation & clipboard keys
|
||||||
String[] navActions = {"ENTER", "TAB", "shift TAB", "UP", "DOWN", "LEFT", "RIGHT",
|
String[] navActions = {"ENTER", "TAB", "shift TAB", "UP", "DOWN", "LEFT", "RIGHT",
|
||||||
"HOME", "END", "PAGE_UP", "PAGE_DOWN", "ESCAPE", "INSERT", "DELETE", "BACK_SPACE", "CLEAR"};
|
"HOME", "END", "PAGE_UP", "PAGE_DOWN", "ESCAPE", "INSERT", "DELETE", "BACK_SPACE", "CLEAR",
|
||||||
|
"STATUS_BAR", "COPY", "PASTE", "SELECTALL"};
|
||||||
for (String act : navActions) {
|
for (String act : navActions) {
|
||||||
String val = prefs.get("key_" + act, null);
|
String val = prefs.get("key_" + act, null);
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
|
|||||||
@@ -502,7 +502,9 @@ public class SettingsDialog extends JDialog {
|
|||||||
|
|
||||||
String[] actions = {"ENTER", "TAB", "shift TAB", "UP", "DOWN", "LEFT", "RIGHT",
|
String[] actions = {"ENTER", "TAB", "shift TAB", "UP", "DOWN", "LEFT", "RIGHT",
|
||||||
"HOME", "END", "PAGE_UP", "PAGE_DOWN", "ESCAPE", "INSERT", "DELETE", "BACK_SPACE", "CLEAR",
|
"HOME", "END", "PAGE_UP", "PAGE_DOWN", "ESCAPE", "INSERT", "DELETE", "BACK_SPACE", "CLEAR",
|
||||||
"ERASE_INPUT", "NEWLINE", "DUP", "FIELD_MARK", "ATTN", "SYSREQ", "CURSEL"};
|
"STATUS_BAR",
|
||||||
|
"ERASE_INPUT", "NEWLINE", "DUP", "FIELD_MARK", "ATTN", "SYSREQ", "CURSEL",
|
||||||
|
"COPY", "PASTE", "SELECTALL"};
|
||||||
|
|
||||||
keymapModel = new DefaultTableModel(new Object[]{"Action", "Key Binding"}, 0) {
|
keymapModel = new DefaultTableModel(new Object[]{"Action", "Key Binding"}, 0) {
|
||||||
@Override
|
@Override
|
||||||
@@ -513,15 +515,7 @@ public class SettingsDialog extends JDialog {
|
|||||||
|
|
||||||
// Populate table from Settings or Defaults
|
// Populate table from Settings or Defaults
|
||||||
for(String act : actions) {
|
for(String act : actions) {
|
||||||
String def = act;
|
String def = getDefaultBinding(act);
|
||||||
if (def.equals("ERASE_INPUT")) def = "alt E";
|
|
||||||
else if (def.equals("NEWLINE")) def = "shift ENTER";
|
|
||||||
else if (def.equals("DUP")) def = "alt D";
|
|
||||||
else if (def.equals("FIELD_MARK")) def = "alt M";
|
|
||||||
else if (def.equals("ATTN")) def = "alt A";
|
|
||||||
else if (def.equals("SYSREQ")) def = "alt S";
|
|
||||||
else if (def.equals("CURSEL")) def = "alt Q";
|
|
||||||
else if (def.equals("CLEAR")) def = "alt C";
|
|
||||||
String current = haus.nightmare.j3270.config.Settings.getKeyBinding(act, def);
|
String current = haus.nightmare.j3270.config.Settings.getKeyBinding(act, def);
|
||||||
tempKeyBindings.put(act, current);
|
tempKeyBindings.put(act, current);
|
||||||
keymapModel.addRow(new Object[]{act, current});
|
keymapModel.addRow(new Object[]{act, current});
|
||||||
@@ -667,13 +661,12 @@ public class SettingsDialog extends JDialog {
|
|||||||
|
|
||||||
/** Returns the factory-default binding for a given action name. */
|
/** Returns the factory-default binding for a given action name. */
|
||||||
private String getDefaultBinding(String action) {
|
private String getDefaultBinding(String action) {
|
||||||
if (action.startsWith("PF")) {
|
if (action.matches("PF[0-9]+")) {
|
||||||
int n = Integer.parseInt(action.substring(2));
|
int n = Integer.parseInt(action.substring(2));
|
||||||
return n <= 12 ? "F" + n : "shift F" + (n - 12);
|
return n <= 12 ? "F" + n : "shift F" + (n - 12);
|
||||||
}
|
}
|
||||||
if (action.startsWith("PA")) return "alt " + action.substring(2);
|
if (action.matches("PA[1-3]")) return "alt " + action.substring(2);
|
||||||
if (action.equals("CLEAR")) return "alt C";
|
return haus.nightmare.j3270.config.Settings.getDefaultBinding(action);
|
||||||
return action; // nav keys default to their own name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== Apply Settings ==========
|
// ========== Apply Settings ==========
|
||||||
|
|||||||
@@ -43,31 +43,35 @@ public class StatusBar extends JPanel {
|
|||||||
luName = createLabel("", oiaFont, ThemeManager.getOiaFgNormal());
|
luName = createLabel("", oiaFont, ThemeManager.getOiaFgNormal());
|
||||||
lockStatus = createLabel("", oiaFont, ThemeManager.getOiaInputInhibited());
|
lockStatus = createLabel("", oiaFont, ThemeManager.getOiaInputInhibited());
|
||||||
insertStatus = createClickableLabel("", oiaFont, ThemeManager.getOiaStatusSysAvail(), "Insert Mode (^ / Insert key) - Click to toggle", () -> {
|
insertStatus = createClickableLabel("", oiaFont, ThemeManager.getOiaStatusSysAvail(), "Insert Mode (^ / Insert key) - Click to toggle", () -> {
|
||||||
if (client != null) {
|
if (terminalPanel != null) {
|
||||||
|
terminalPanel.toggleInsertMode();
|
||||||
|
} else if (client != null) {
|
||||||
client.toggleInsert();
|
client.toggleInsert();
|
||||||
if (terminalPanel != null) terminalPanel.refreshScreen();
|
|
||||||
updateStatus();
|
updateStatus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
aplStatus = createClickableLabel("", oiaFont, ThemeManager.getOiaFgAlert(), "APL Keyboard Mode (Alt+F3) - Click to toggle", () -> {
|
aplStatus = createClickableLabel("", oiaFont, ThemeManager.getOiaFgAlert(), "APL Keyboard Mode (Alt+F3) - Click to toggle", () -> {
|
||||||
if (client != null) {
|
if (terminalPanel != null) {
|
||||||
|
terminalPanel.toggleAplMode();
|
||||||
|
} else if (client != null) {
|
||||||
client.toggleAplMode();
|
client.toggleAplMode();
|
||||||
if (terminalPanel != null) terminalPanel.refreshScreen();
|
|
||||||
updateStatus();
|
updateStatus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
fieldTypeStatus = createLabel("", oiaFont, ThemeManager.getOiaFgDim());
|
fieldTypeStatus = createLabel("", oiaFont, ThemeManager.getOiaFgDim());
|
||||||
docModeStatus = createClickableLabel("", oiaFont, ThemeManager.getOiaStatusSysAvail(), "Entry Assist Document Mode (Alt+F1) - Click to toggle", () -> {
|
docModeStatus = createClickableLabel("", oiaFont, ThemeManager.getOiaStatusSysAvail(), "Entry Assist Document Mode (Alt+F1) - Click to toggle", () -> {
|
||||||
if (client != null) {
|
if (terminalPanel != null) {
|
||||||
|
terminalPanel.toggleDocMode();
|
||||||
|
} else if (client != null) {
|
||||||
client.toggleDocMode();
|
client.toggleDocMode();
|
||||||
if (terminalPanel != null) terminalPanel.refreshScreen();
|
|
||||||
updateStatus();
|
updateStatus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
wordWrapStatus = createClickableLabel("", oiaFont, ThemeManager.getOiaStatusSysAvail(), "Entry Assist Word Wrap (Alt+F2) - Click to toggle", () -> {
|
wordWrapStatus = createClickableLabel("", oiaFont, ThemeManager.getOiaStatusSysAvail(), "Entry Assist Word Wrap (Alt+F2) - Click to toggle", () -> {
|
||||||
if (client != null) {
|
if (terminalPanel != null) {
|
||||||
|
terminalPanel.toggleWordWrap();
|
||||||
|
} else if (client != null) {
|
||||||
client.toggleWordWrap();
|
client.toggleWordWrap();
|
||||||
if (terminalPanel != null) terminalPanel.refreshScreen();
|
|
||||||
updateStatus();
|
updateStatus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -269,7 +273,13 @@ public class StatusBar extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Insert Mode Indicator
|
// Insert Mode Indicator
|
||||||
if (client.getInputProcessor() != null && client.getInputProcessor().isInsertMode()) {
|
boolean isInsert = false;
|
||||||
|
if (terminalPanel != null) {
|
||||||
|
isInsert = terminalPanel.isInsertMode();
|
||||||
|
} else if (client.getInputProcessor() != null) {
|
||||||
|
isInsert = client.getInputProcessor().isInsertMode();
|
||||||
|
}
|
||||||
|
if (isInsert) {
|
||||||
insertStatus.setText("^ INS");
|
insertStatus.setText("^ INS");
|
||||||
insertStatus.setForeground(ThemeManager.getOiaStatusSysAvail(theme));
|
insertStatus.setForeground(ThemeManager.getOiaStatusSysAvail(theme));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -86,6 +86,32 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
private boolean resizeGuard = false;
|
private boolean resizeGuard = false;
|
||||||
private StatusBar statusBar = null;
|
private StatusBar statusBar = null;
|
||||||
|
|
||||||
|
// ========== Mode and Listener State ==========
|
||||||
|
private boolean insertMode = false;
|
||||||
|
|
||||||
|
public interface ModeChangeListener {
|
||||||
|
void onModeChanged();
|
||||||
|
}
|
||||||
|
private final java.util.List<ModeChangeListener> modeChangeListeners = new java.util.concurrent.CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
public void addModeChangeListener(ModeChangeListener listener) {
|
||||||
|
if (listener != null && !modeChangeListeners.contains(listener)) {
|
||||||
|
modeChangeListeners.add(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeModeChangeListener(ModeChangeListener listener) {
|
||||||
|
modeChangeListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fireModeChanged() {
|
||||||
|
for (ModeChangeListener l : modeChangeListeners) {
|
||||||
|
try {
|
||||||
|
l.onModeChanged();
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the horizontal render offset to center the grid within the panel.
|
* Compute the horizontal render offset to center the grid within the panel.
|
||||||
*/
|
*/
|
||||||
@@ -462,13 +488,23 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
try {
|
try {
|
||||||
String text = (String) Toolkit.getDefaultToolkit().getSystemClipboard()
|
String text = (String) Toolkit.getDefaultToolkit().getSystemClipboard()
|
||||||
.getData(DataFlavor.stringFlavor);
|
.getData(DataFlavor.stringFlavor);
|
||||||
if (text != null) {
|
if (text != null && !text.isEmpty()) {
|
||||||
for (char ch : text.toCharArray()) {
|
ScreenBuffer sb = client.getScreenBuffer();
|
||||||
if (ch == '\n' || ch == '\r') {
|
int curPos = sb != null ? sb.getDisplayCursorAddress() : 0;
|
||||||
continue;
|
int cols = sb != null ? sb.getDisplayCols() : 80;
|
||||||
}
|
int curRow = cols > 0 ? curPos / cols : 0;
|
||||||
if (ch >= 0x20 && ch != 0x7F) {
|
int curCol = cols > 0 ? curPos % cols : 0;
|
||||||
client.typeCharacter(ch);
|
|
||||||
|
if (client.getPS() != null && (text.contains("\n") || text.contains("\r"))) {
|
||||||
|
client.getPS().pasteString(text, curRow, curCol);
|
||||||
|
} else {
|
||||||
|
for (char ch : text.toCharArray()) {
|
||||||
|
if (ch == '\n' || ch == '\r') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch >= 0x20 && ch != 0x7F) {
|
||||||
|
client.typeCharacter(ch);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refreshScreen();
|
refreshScreen();
|
||||||
@@ -570,7 +606,11 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
resizeGuard = true;
|
resizeGuard = true;
|
||||||
revalidate();
|
revalidate();
|
||||||
Container top = getTopLevelAncestor();
|
Container top = getTopLevelAncestor();
|
||||||
if (top instanceof java.awt.Window) {
|
if (top instanceof java.awt.Frame) {
|
||||||
|
if ((((java.awt.Frame) top).getExtendedState() & java.awt.Frame.MAXIMIZED_BOTH) == 0) {
|
||||||
|
((java.awt.Window) top).pack();
|
||||||
|
}
|
||||||
|
} else if (top instanceof java.awt.Window) {
|
||||||
((java.awt.Window) top).pack();
|
((java.awt.Window) top).pack();
|
||||||
}
|
}
|
||||||
SwingUtilities.invokeLater(() -> resizeGuard = false);
|
SwingUtilities.invokeLater(() -> resizeGuard = false);
|
||||||
@@ -603,7 +643,8 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
"PAGE_UP", "PAGE_DOWN", "HOME", "END", "ENTER",
|
"PAGE_UP", "PAGE_DOWN", "HOME", "END", "ENTER",
|
||||||
"ESCAPE", "INSERT", "DELETE", "BACK_SPACE" };
|
"ESCAPE", "INSERT", "DELETE", "BACK_SPACE" };
|
||||||
for (String key : navKeys) {
|
for (String key : navKeys) {
|
||||||
String binding = haus.nightmare.j3270.config.Settings.getKeyBinding(key, key);
|
String defBinding = haus.nightmare.j3270.config.Settings.getDefaultBinding(key);
|
||||||
|
String binding = haus.nightmare.j3270.config.Settings.getKeyBinding(key, defBinding);
|
||||||
bindKeyToMap(im, key, binding);
|
bindKeyToMap(im, key, binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -637,11 +678,16 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
bindKeyToMap(im, "APL", haus.nightmare.j3270.config.Settings.getKeyBinding("APL", "alt F3"));
|
bindKeyToMap(im, "APL", haus.nightmare.j3270.config.Settings.getKeyBinding("APL", "alt F3"));
|
||||||
|
|
||||||
// Copy/Paste/Lightpen bindings
|
// Copy/Paste/Lightpen bindings
|
||||||
int shortcutMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx();
|
String defCopy = haus.nightmare.j3270.config.Settings.getDefaultBinding("COPY");
|
||||||
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, shortcutMask), "j3270-COPY");
|
String defPaste = haus.nightmare.j3270.config.Settings.getDefaultBinding("PASTE");
|
||||||
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, shortcutMask), "j3270-PASTE");
|
String defSelectAll = haus.nightmare.j3270.config.Settings.getDefaultBinding("SELECTALL");
|
||||||
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, shortcutMask), "j3270-SELECTALL");
|
bindKeyToMap(im, "COPY", haus.nightmare.j3270.config.Settings.getKeyBinding("COPY", defCopy));
|
||||||
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_L, java.awt.event.InputEvent.ALT_DOWN_MASK), "j3270-LIGHTPEN");
|
bindKeyToMap(im, "PASTE", haus.nightmare.j3270.config.Settings.getKeyBinding("PASTE", defPaste));
|
||||||
|
bindKeyToMap(im, "SELECTALL", haus.nightmare.j3270.config.Settings.getKeyBinding("SELECTALL", defSelectAll));
|
||||||
|
bindKeyToMap(im, "LIGHTPEN", haus.nightmare.j3270.config.Settings.getKeyBinding("LIGHTPEN", "alt L"));
|
||||||
|
|
||||||
|
String defStatusBar = haus.nightmare.j3270.config.Settings.getDefaultBinding("STATUS_BAR");
|
||||||
|
bindKeyToMap(im, "STATUS_BAR", haus.nightmare.j3270.config.Settings.getKeyBinding("STATUS_BAR", defStatusBar));
|
||||||
|
|
||||||
// Action map implementations
|
// Action map implementations
|
||||||
am.put("j3270-ENTER", createAction(this::handleEnter));
|
am.put("j3270-ENTER", createAction(this::handleEnter));
|
||||||
@@ -661,7 +707,7 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
am.put("j3270-END", createAction(this::handleEraseEOF));
|
am.put("j3270-END", createAction(this::handleEraseEOF));
|
||||||
am.put("j3270-DELETE", createAction(this::handleDelete));
|
am.put("j3270-DELETE", createAction(this::handleDelete));
|
||||||
am.put("j3270-BACK_SPACE", createAction(this::handleBackspace));
|
am.put("j3270-BACK_SPACE", createAction(this::handleBackspace));
|
||||||
am.put("j3270-INSERT", createAction(this::handleInsert));
|
am.put("j3270-INSERT", createAction(this::toggleInsertMode));
|
||||||
am.put("j3270-CLEAR", createAction(this::handleClear));
|
am.put("j3270-CLEAR", createAction(this::handleClear));
|
||||||
am.put("j3270-ERASE_INPUT", createAction(this::handleEraseInput));
|
am.put("j3270-ERASE_INPUT", createAction(this::handleEraseInput));
|
||||||
am.put("j3270-NEWLINE", createAction(this::handleNewline));
|
am.put("j3270-NEWLINE", createAction(this::handleNewline));
|
||||||
@@ -675,6 +721,7 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
am.put("j3270-PASTE", createAction(this::pasteClipboard));
|
am.put("j3270-PASTE", createAction(this::pasteClipboard));
|
||||||
am.put("j3270-SELECTALL", createAction(this::selectAll));
|
am.put("j3270-SELECTALL", createAction(this::selectAll));
|
||||||
am.put("j3270-LIGHTPEN", createAction(this::toggleLightPen));
|
am.put("j3270-LIGHTPEN", createAction(this::toggleLightPen));
|
||||||
|
am.put("j3270-STATUS_BAR", createAction(this::toggleStatusBar));
|
||||||
|
|
||||||
for (int i = 1; i <= 24; i++) {
|
for (int i = 1; i <= 24; i++) {
|
||||||
final int pf = i;
|
final int pf = i;
|
||||||
@@ -789,6 +836,8 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
} else if (state.isFullSession()) {
|
} else if (state.isFullSession()) {
|
||||||
client.sendEnter();
|
client.sendEnter();
|
||||||
refreshScreen();
|
refreshScreen();
|
||||||
|
fireModeChanged();
|
||||||
|
if (statusBar != null) statusBar.updateStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -805,6 +854,8 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
clearSelection();
|
clearSelection();
|
||||||
client.reset();
|
client.reset();
|
||||||
refreshScreen();
|
refreshScreen();
|
||||||
|
fireModeChanged();
|
||||||
|
if (statusBar != null) statusBar.updateStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -873,6 +924,8 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
}
|
}
|
||||||
client.sendPF(n);
|
client.sendPF(n);
|
||||||
refreshScreen();
|
refreshScreen();
|
||||||
|
fireModeChanged();
|
||||||
|
if (statusBar != null) statusBar.updateStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -898,6 +951,8 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
if (client != null && client.getConnectionState().isFullSession()) {
|
if (client != null && client.getConnectionState().isFullSession()) {
|
||||||
client.sendPA(n);
|
client.sendPA(n);
|
||||||
refreshScreen();
|
refreshScreen();
|
||||||
|
fireModeChanged();
|
||||||
|
if (statusBar != null) statusBar.updateStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -941,13 +996,24 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleInsert() {
|
public void toggleInsertMode() {
|
||||||
if (client != null) {
|
if (client != null && client.getInputProcessor() != null) {
|
||||||
var ip = client.getInputProcessor();
|
var ip = client.getInputProcessor();
|
||||||
ip.setInsertMode(!ip.isInsertMode());
|
ip.setInsertMode(!ip.isInsertMode());
|
||||||
refreshScreen();
|
insertMode = ip.isInsertMode();
|
||||||
if (statusBar != null) statusBar.updateStatus();
|
} else {
|
||||||
|
insertMode = !insertMode;
|
||||||
}
|
}
|
||||||
|
refreshScreen();
|
||||||
|
if (statusBar != null) statusBar.updateStatus();
|
||||||
|
fireModeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInsertMode() {
|
||||||
|
if (client != null && client.getInputProcessor() != null) {
|
||||||
|
return client.getInputProcessor().isInsertMode();
|
||||||
|
}
|
||||||
|
return insertMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void toggleDocMode() {
|
public void toggleDocMode() {
|
||||||
@@ -956,6 +1022,7 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
haus.nightmare.j3270.config.Settings.setEntryAssistDocMode(client.isDocMode());
|
haus.nightmare.j3270.config.Settings.setEntryAssistDocMode(client.isDocMode());
|
||||||
refreshScreen();
|
refreshScreen();
|
||||||
if (statusBar != null) statusBar.updateStatus();
|
if (statusBar != null) statusBar.updateStatus();
|
||||||
|
fireModeChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -965,6 +1032,7 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
haus.nightmare.j3270.config.Settings.setEntryAssistWordWrap(client.isWordWrap());
|
haus.nightmare.j3270.config.Settings.setEntryAssistWordWrap(client.isWordWrap());
|
||||||
refreshScreen();
|
refreshScreen();
|
||||||
if (statusBar != null) statusBar.updateStatus();
|
if (statusBar != null) statusBar.updateStatus();
|
||||||
|
fireModeChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -973,6 +1041,7 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
client.toggleAplMode();
|
client.toggleAplMode();
|
||||||
refreshScreen();
|
refreshScreen();
|
||||||
if (statusBar != null) statusBar.updateStatus();
|
if (statusBar != null) statusBar.updateStatus();
|
||||||
|
fireModeChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -998,6 +1067,8 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
}
|
}
|
||||||
client.sendClear();
|
client.sendClear();
|
||||||
refreshScreen();
|
refreshScreen();
|
||||||
|
fireModeChanged();
|
||||||
|
if (statusBar != null) statusBar.updateStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1061,6 +1132,23 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
return statusBar;
|
return statusBar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Runnable statusBarToggleCallback;
|
||||||
|
|
||||||
|
public void setStatusBarToggleCallback(Runnable callback) {
|
||||||
|
this.statusBarToggleCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toggleStatusBar() {
|
||||||
|
if (statusBarToggleCallback != null) {
|
||||||
|
statusBarToggleCallback.run();
|
||||||
|
} else if (statusBar != null) {
|
||||||
|
boolean visible = !statusBar.isVisible();
|
||||||
|
statusBar.setVisible(visible);
|
||||||
|
haus.nightmare.j3270.config.Settings.setStatusBarVisible(visible);
|
||||||
|
guardedPack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void applyModeSettings() {
|
public void applyModeSettings() {
|
||||||
if (client != null) {
|
if (client != null) {
|
||||||
ScreenBuffer sb = client.getScreenBuffer();
|
ScreenBuffer sb = client.getScreenBuffer();
|
||||||
@@ -1184,6 +1272,7 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
applyModeSettings();
|
applyModeSettings();
|
||||||
|
|
||||||
if (client.getInputProcessor() != null) {
|
if (client.getInputProcessor() != null) {
|
||||||
|
client.getInputProcessor().setInsertMode(insertMode);
|
||||||
client.getInputProcessor().setBellListener(() -> {
|
client.getInputProcessor().setBellListener(() -> {
|
||||||
SwingUtilities.invokeLater(() -> Toolkit.getDefaultToolkit().beep());
|
SwingUtilities.invokeLater(() -> Toolkit.getDefaultToolkit().beep());
|
||||||
});
|
});
|
||||||
@@ -1220,6 +1309,8 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
fireModeChanged();
|
||||||
|
if (statusBar != null) statusBar.updateStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupGraphicsPlaneRenderer() {
|
private void setupGraphicsPlaneRenderer() {
|
||||||
|
|||||||
@@ -0,0 +1,337 @@
|
|||||||
|
package haus.nightmare.j3270.ui;
|
||||||
|
|
||||||
|
import haus.nightmare.j3270.J3270App;
|
||||||
|
import haus.nightmare.j3270.config.Settings;
|
||||||
|
import haus.nightmare.lib3270j.ConnectionConfig;
|
||||||
|
import haus.nightmare.lib3270j.Telnet3270Client;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.HeadlessException;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.InputEvent;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class KeyBindingsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultBindingsContainRequiredKeys() {
|
||||||
|
String insertBindings = Settings.getDefaultBinding("INSERT");
|
||||||
|
assertNotNull(insertBindings);
|
||||||
|
assertTrue(insertBindings.contains("INSERT"), "Default INSERT binding should contain INSERT key");
|
||||||
|
if (Settings.isMac()) {
|
||||||
|
assertTrue(insertBindings.contains("HELP"), "macOS INSERT binding should contain HELP key");
|
||||||
|
assertTrue(insertBindings.contains("alt I"), "macOS INSERT binding should contain alt I fallback");
|
||||||
|
} else {
|
||||||
|
assertTrue(insertBindings.contains("ctrl I"), "Non-macOS INSERT binding should contain ctrl I fallback");
|
||||||
|
}
|
||||||
|
|
||||||
|
String copyBindings = Settings.getDefaultBinding("COPY");
|
||||||
|
assertNotNull(copyBindings);
|
||||||
|
assertTrue(copyBindings.contains("ctrl INSERT"), "Default COPY binding should support 3270 Ctrl+Insert");
|
||||||
|
assertTrue(copyBindings.contains("ctrl C"), "Default COPY binding should support Ctrl+C");
|
||||||
|
assertTrue(copyBindings.contains("ctrl shift C"), "Default COPY binding should support Ctrl+Shift+C");
|
||||||
|
if (Settings.isMac()) {
|
||||||
|
assertTrue(copyBindings.contains("meta C"), "macOS COPY binding should support Cmd+C");
|
||||||
|
}
|
||||||
|
|
||||||
|
String pasteBindings = Settings.getDefaultBinding("PASTE");
|
||||||
|
assertNotNull(pasteBindings);
|
||||||
|
assertTrue(pasteBindings.contains("shift INSERT"), "Default PASTE binding should support 3270 Shift+Insert");
|
||||||
|
assertTrue(pasteBindings.contains("ctrl V"), "Default PASTE binding should support Ctrl+V");
|
||||||
|
assertTrue(pasteBindings.contains("ctrl shift V"), "Default PASTE binding should support Ctrl+Shift+V");
|
||||||
|
if (Settings.isMac()) {
|
||||||
|
assertTrue(pasteBindings.contains("meta V"), "macOS PASTE binding should support Cmd+V");
|
||||||
|
}
|
||||||
|
|
||||||
|
String selectAllBindings = Settings.getDefaultBinding("SELECTALL");
|
||||||
|
assertNotNull(selectAllBindings);
|
||||||
|
assertTrue(selectAllBindings.contains("ctrl A"), "Default SELECTALL binding should support Ctrl+A");
|
||||||
|
if (Settings.isMac()) {
|
||||||
|
assertTrue(selectAllBindings.contains("meta A"), "macOS SELECTALL binding should support Cmd+A");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTerminalPanelInputMapAndAction() {
|
||||||
|
TerminalPanel panel = new TerminalPanel();
|
||||||
|
InputMap im = panel.getInputMap(JComponent.WHEN_FOCUSED);
|
||||||
|
ActionMap am = panel.getActionMap();
|
||||||
|
|
||||||
|
// Check INSERT key mappings
|
||||||
|
KeyStroke ksInsert = KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0);
|
||||||
|
assertEquals("j3270-INSERT", im.get(ksInsert), "VK_INSERT must map to j3270-INSERT");
|
||||||
|
|
||||||
|
if (Settings.isMac()) {
|
||||||
|
KeyStroke ksHelp = KeyStroke.getKeyStroke(KeyEvent.VK_HELP, 0);
|
||||||
|
assertEquals("j3270-INSERT", im.get(ksHelp), "VK_HELP must map to j3270-INSERT on Mac");
|
||||||
|
KeyStroke ksAltI = KeyStroke.getKeyStroke("alt I");
|
||||||
|
assertEquals("j3270-INSERT", im.get(ksAltI), "alt I must map to j3270-INSERT on Mac");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check COPY key mappings
|
||||||
|
KeyStroke ksCtrlIns = KeyStroke.getKeyStroke("ctrl INSERT");
|
||||||
|
assertEquals("j3270-COPY", im.get(ksCtrlIns), "ctrl INSERT must map to j3270-COPY");
|
||||||
|
KeyStroke ksCtrlC = KeyStroke.getKeyStroke("ctrl C");
|
||||||
|
assertEquals("j3270-COPY", im.get(ksCtrlC), "ctrl C must map to j3270-COPY");
|
||||||
|
if (Settings.isMac()) {
|
||||||
|
KeyStroke ksMetaC = KeyStroke.getKeyStroke("meta C");
|
||||||
|
assertEquals("j3270-COPY", im.get(ksMetaC), "meta C must map to j3270-COPY on Mac");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check PASTE key mappings
|
||||||
|
KeyStroke ksShiftIns = KeyStroke.getKeyStroke("shift INSERT");
|
||||||
|
assertEquals("j3270-PASTE", im.get(ksShiftIns), "shift INSERT must map to j3270-PASTE");
|
||||||
|
KeyStroke ksCtrlV = KeyStroke.getKeyStroke("ctrl V");
|
||||||
|
assertEquals("j3270-PASTE", im.get(ksCtrlV), "ctrl V must map to j3270-PASTE");
|
||||||
|
if (Settings.isMac()) {
|
||||||
|
KeyStroke ksMetaV = KeyStroke.getKeyStroke("meta V");
|
||||||
|
assertEquals("j3270-PASTE", im.get(ksMetaV), "meta V must map to j3270-PASTE on Mac");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check SELECTALL key mappings
|
||||||
|
KeyStroke ksCtrlA = KeyStroke.getKeyStroke("ctrl A");
|
||||||
|
assertEquals("j3270-SELECTALL", im.get(ksCtrlA), "ctrl A must map to j3270-SELECTALL");
|
||||||
|
if (Settings.isMac()) {
|
||||||
|
KeyStroke ksMetaA = KeyStroke.getKeyStroke("meta A");
|
||||||
|
assertEquals("j3270-SELECTALL", im.get(ksMetaA), "meta A must map to j3270-SELECTALL on Mac");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test insert mode toggle action
|
||||||
|
assertFalse(panel.isInsertMode(), "Initial insertMode should be false");
|
||||||
|
Action insertAction = am.get("j3270-INSERT");
|
||||||
|
assertNotNull(insertAction, "j3270-INSERT action must exist");
|
||||||
|
|
||||||
|
AtomicBoolean modeChangedFired = new AtomicBoolean(false);
|
||||||
|
panel.addModeChangeListener(() -> modeChangedFired.set(true));
|
||||||
|
|
||||||
|
insertAction.actionPerformed(new ActionEvent(panel, ActionEvent.ACTION_PERFORMED, ""));
|
||||||
|
assertTrue(panel.isInsertMode(), "insertMode should be true after invoking action");
|
||||||
|
assertTrue(modeChangedFired.get(), "ModeChangeListener should have fired");
|
||||||
|
|
||||||
|
modeChangedFired.set(false);
|
||||||
|
insertAction.actionPerformed(new ActionEvent(panel, ActionEvent.ACTION_PERFORMED, ""));
|
||||||
|
assertFalse(panel.isInsertMode(), "insertMode should be false after invoking action again");
|
||||||
|
assertTrue(modeChangedFired.get(), "ModeChangeListener should have fired again");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInsertModeSyncWithClient() {
|
||||||
|
TerminalPanel panel = new TerminalPanel();
|
||||||
|
panel.toggleInsertMode();
|
||||||
|
assertTrue(panel.isInsertMode());
|
||||||
|
|
||||||
|
Telnet3270Client client = new Telnet3270Client(new ConnectionConfig("localhost", 23));
|
||||||
|
panel.setClient(client);
|
||||||
|
|
||||||
|
// When client is attached, client's inputProcessor should receive current insertMode
|
||||||
|
assertNotNull(client.getInputProcessor());
|
||||||
|
assertTrue(client.getInputProcessor().isInsertMode(), "Client inputProcessor should inherit insertMode");
|
||||||
|
assertTrue(panel.isInsertMode());
|
||||||
|
|
||||||
|
// Toggling on panel updates client
|
||||||
|
panel.toggleInsertMode();
|
||||||
|
assertFalse(client.getInputProcessor().isInsertMode());
|
||||||
|
assertFalse(panel.isInsertMode());
|
||||||
|
|
||||||
|
panel.toggleInsertMode();
|
||||||
|
assertTrue(client.getInputProcessor().isInsertMode());
|
||||||
|
assertTrue(panel.isInsertMode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJ3270AppModesMenuSync() {
|
||||||
|
J3270App app;
|
||||||
|
try {
|
||||||
|
app = new J3270App();
|
||||||
|
} catch (HeadlessException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
JMenuBar mb = app.getJMenuBar();
|
||||||
|
assertNotNull(mb);
|
||||||
|
|
||||||
|
JMenu modesMenu = null;
|
||||||
|
for (int i = 0; i < mb.getMenuCount(); i++) {
|
||||||
|
JMenu m = mb.getMenu(i);
|
||||||
|
if (m != null && "Modes".equals(m.getText())) {
|
||||||
|
modesMenu = m;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertNotNull(modesMenu, "Modes menu should exist");
|
||||||
|
|
||||||
|
JCheckBoxMenuItem insertItem = null;
|
||||||
|
for (int i = 0; i < modesMenu.getItemCount(); i++) {
|
||||||
|
JMenuItem item = modesMenu.getItem(i);
|
||||||
|
if (item instanceof JCheckBoxMenuItem && "Insert Mode".equals(item.getText())) {
|
||||||
|
insertItem = (JCheckBoxMenuItem) item;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertNotNull(insertItem, "Insert Mode menu item should exist");
|
||||||
|
assertFalse(insertItem.isSelected(), "Initial Insert Mode item should not be selected");
|
||||||
|
|
||||||
|
// Find terminal panel and toggle
|
||||||
|
TerminalPanel panel = null;
|
||||||
|
for (java.awt.Component c : app.getContentPane().getComponents()) {
|
||||||
|
if (c instanceof TerminalPanel) {
|
||||||
|
panel = (TerminalPanel) c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertNotNull(panel, "TerminalPanel should be found");
|
||||||
|
|
||||||
|
// Toggling via panel updates menu item
|
||||||
|
panel.toggleInsertMode();
|
||||||
|
assertTrue(panel.isInsertMode());
|
||||||
|
assertTrue(insertItem.isSelected(), "Menu item should reflect true insert mode");
|
||||||
|
|
||||||
|
panel.toggleInsertMode();
|
||||||
|
assertFalse(panel.isInsertMode());
|
||||||
|
assertFalse(insertItem.isSelected(), "Menu item should reflect false insert mode");
|
||||||
|
|
||||||
|
// Clicking menu item toggles insert mode on panel
|
||||||
|
insertItem.doClick();
|
||||||
|
assertTrue(panel.isInsertMode(), "Panel should be in insert mode after clicking menu item");
|
||||||
|
assertTrue(insertItem.isSelected(), "Menu item should be selected after click");
|
||||||
|
|
||||||
|
insertItem.doClick();
|
||||||
|
assertFalse(panel.isInsertMode(), "Panel should exit insert mode after clicking menu item");
|
||||||
|
assertFalse(insertItem.isSelected(), "Menu item should be deselected after click");
|
||||||
|
} finally {
|
||||||
|
app.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCopyAndPasteActions() {
|
||||||
|
try {
|
||||||
|
java.awt.Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||||
|
} catch (HeadlessException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalPanel panel = new TerminalPanel();
|
||||||
|
Telnet3270Client client = new Telnet3270Client(new ConnectionConfig("localhost", 23));
|
||||||
|
panel.setClient(client);
|
||||||
|
|
||||||
|
var sb = client.getScreenBuffer();
|
||||||
|
sb.setChar(0, 0, 'J');
|
||||||
|
sb.setChar(0, 1, '3');
|
||||||
|
sb.setChar(0, 2, '2');
|
||||||
|
sb.setChar(0, 3, '7');
|
||||||
|
sb.setChar(0, 4, '0');
|
||||||
|
|
||||||
|
panel.setSelectionRange(0, 4);
|
||||||
|
assertTrue(panel.hasSelection());
|
||||||
|
assertEquals("J3270", panel.getSelectedText());
|
||||||
|
|
||||||
|
Action copyAction = panel.getActionMap().get("j3270-COPY");
|
||||||
|
assertNotNull(copyAction);
|
||||||
|
copyAction.actionPerformed(new ActionEvent(panel, ActionEvent.ACTION_PERFORMED, ""));
|
||||||
|
|
||||||
|
try {
|
||||||
|
String clip = (String) java.awt.Toolkit.getDefaultToolkit().getSystemClipboard()
|
||||||
|
.getData(java.awt.datatransfer.DataFlavor.stringFlavor);
|
||||||
|
assertEquals("J3270", clip);
|
||||||
|
} catch (Exception e) {
|
||||||
|
fail("Clipboard read failed: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test multiline paste handling
|
||||||
|
try {
|
||||||
|
java.awt.Toolkit.getDefaultToolkit().getSystemClipboard().setContents(
|
||||||
|
new java.awt.datatransfer.StringSelection("LINE1\nLINE2"), null);
|
||||||
|
Action pasteAction = panel.getActionMap().get("j3270-PASTE");
|
||||||
|
assertNotNull(pasteAction);
|
||||||
|
pasteAction.actionPerformed(new ActionEvent(panel, ActionEvent.ACTION_PERFORMED, ""));
|
||||||
|
// Shouldn't throw any exceptions
|
||||||
|
} catch (Exception e) {
|
||||||
|
fail("Paste action failed: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToggleStatusBar() {
|
||||||
|
assertEquals("alt B", Settings.getDefaultBinding("STATUS_BAR"));
|
||||||
|
|
||||||
|
TerminalPanel panel = new TerminalPanel();
|
||||||
|
InputMap im = panel.getInputMap(JComponent.WHEN_FOCUSED);
|
||||||
|
ActionMap am = panel.getActionMap();
|
||||||
|
|
||||||
|
KeyStroke ksAltB = KeyStroke.getKeyStroke("alt B");
|
||||||
|
assertEquals("j3270-STATUS_BAR", im.get(ksAltB), "alt B must map to j3270-STATUS_BAR");
|
||||||
|
assertNotNull(am.get("j3270-STATUS_BAR"), "j3270-STATUS_BAR action must exist");
|
||||||
|
|
||||||
|
J3270App app;
|
||||||
|
try {
|
||||||
|
app = new J3270App();
|
||||||
|
} catch (HeadlessException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
JMenuBar mb = app.getJMenuBar();
|
||||||
|
assertNotNull(mb);
|
||||||
|
|
||||||
|
JMenu viewMenu = null;
|
||||||
|
for (int i = 0; i < mb.getMenuCount(); i++) {
|
||||||
|
JMenu m = mb.getMenu(i);
|
||||||
|
if (m != null && "View".equals(m.getText())) {
|
||||||
|
viewMenu = m;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertNotNull(viewMenu);
|
||||||
|
|
||||||
|
JCheckBoxMenuItem statusBarItem = null;
|
||||||
|
for (int i = 0; i < viewMenu.getItemCount(); i++) {
|
||||||
|
JMenuItem item = viewMenu.getItem(i);
|
||||||
|
if (item instanceof JCheckBoxMenuItem && "Status Bar".equals(item.getText())) {
|
||||||
|
statusBarItem = (JCheckBoxMenuItem) item;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertNotNull(statusBarItem, "Status Bar menu item must exist in View menu");
|
||||||
|
assertTrue(statusBarItem.isSelected(), "Status bar should be selected by default");
|
||||||
|
|
||||||
|
StatusBar bar = null;
|
||||||
|
for (java.awt.Component c : app.getContentPane().getComponents()) {
|
||||||
|
if (c instanceof StatusBar) {
|
||||||
|
bar = (StatusBar) c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertNotNull(bar, "StatusBar component should exist in content pane");
|
||||||
|
assertTrue(bar.isVisible(), "StatusBar should be visible by default");
|
||||||
|
|
||||||
|
// Test toggling via toggleStatusBar()
|
||||||
|
app.toggleStatusBar();
|
||||||
|
assertFalse(bar.isVisible(), "StatusBar should be hidden after toggle");
|
||||||
|
assertFalse(statusBarItem.isSelected(), "Menu item should be unchecked");
|
||||||
|
assertFalse(Settings.getStatusBarVisible());
|
||||||
|
|
||||||
|
app.toggleStatusBar();
|
||||||
|
assertTrue(bar.isVisible(), "StatusBar should be shown after second toggle");
|
||||||
|
assertTrue(statusBarItem.isSelected(), "Menu item should be checked");
|
||||||
|
assertTrue(Settings.getStatusBarVisible());
|
||||||
|
|
||||||
|
// Test toggling via menu item click
|
||||||
|
statusBarItem.doClick();
|
||||||
|
assertFalse(bar.isVisible(), "StatusBar should be hidden after menu click");
|
||||||
|
assertFalse(statusBarItem.isSelected(), "Menu item should be unchecked");
|
||||||
|
|
||||||
|
statusBarItem.doClick();
|
||||||
|
assertTrue(bar.isVisible(), "StatusBar should be shown after menu click");
|
||||||
|
assertTrue(statusBarItem.isSelected(), "Menu item should be checked");
|
||||||
|
} finally {
|
||||||
|
app.dispose();
|
||||||
|
Settings.setStatusBarVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -63,6 +63,7 @@ public class MenuBarShortcutsTest {
|
|||||||
assertAcceleratorUsesAlt(viewItems.get("Font Size +"), KeyEvent.VK_EQUALS);
|
assertAcceleratorUsesAlt(viewItems.get("Font Size +"), KeyEvent.VK_EQUALS);
|
||||||
assertAcceleratorUsesAlt(viewItems.get("Font Size -"), KeyEvent.VK_MINUS);
|
assertAcceleratorUsesAlt(viewItems.get("Font Size -"), KeyEvent.VK_MINUS);
|
||||||
assertAcceleratorUsesAlt(viewItems.get("Reset Font"), KeyEvent.VK_0);
|
assertAcceleratorUsesAlt(viewItems.get("Reset Font"), KeyEvent.VK_0);
|
||||||
|
assertAcceleratorUsesAlt(viewItems.get("Status Bar"), KeyEvent.VK_B);
|
||||||
|
|
||||||
// Crosshair Ruler must use Alt+Shift+R
|
// Crosshair Ruler must use Alt+Shift+R
|
||||||
JMenuItem rulerItem = viewItems.get("Crosshair Ruler");
|
JMenuItem rulerItem = viewItems.get("Crosshair Ruler");
|
||||||
|
|||||||
Reference in New Issue
Block a user