Compare commits
16 Commits
ff5dfda343
...
v1.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
6296cf4341
|
|||
|
db6043cb39
|
|||
|
161364a95f
|
|||
|
a56aa5f059
|
|||
|
7b5bc7ec11
|
|||
|
a9956298e2
|
|||
|
a43969da08
|
|||
|
e18d2f436f
|
|||
|
27976dd31f
|
|||
|
a3c4b95379
|
|||
|
3a576b79b4
|
|||
|
40ebd40fe2
|
|||
|
f310d39fc1
|
|||
|
a44ea00d3a
|
|||
|
037ad1f941
|
|||
|
79258c0c44
|
+2
-4
@@ -118,7 +118,7 @@ public class TestRunner {
|
||||
public static void main(String[] args) {
|
||||
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
|
||||
.selectors(
|
||||
selectPackage("haus.nightmare.lib3270j")
|
||||
selectPackage("haus.nightmare")
|
||||
)
|
||||
.build();
|
||||
|
||||
@@ -131,9 +131,7 @@ public class TestRunner {
|
||||
summary.printTo(new PrintWriter(System.out));
|
||||
summary.printFailuresTo(new PrintWriter(System.err));
|
||||
|
||||
if (summary.getTotalFailureCount() > 0) {
|
||||
System.exit(1);
|
||||
}
|
||||
System.exit(summary.getTotalFailureCount() > 0 ? 1 : 0);
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
package haus.nightmare.j3270;
|
||||
|
||||
import haus.nightmare.lib3270j.*;
|
||||
import haus.nightmare.lib3270j.ecl.ECLConstants;
|
||||
import haus.nightmare.lib3270j.graphics.GraphicsMode;
|
||||
import haus.nightmare.lib3270j.listener.ConnectionListener;
|
||||
import haus.nightmare.lib3270j.listener.ScreenUpdateListener;
|
||||
import haus.nightmare.j3270.ui.ConnectDialog;
|
||||
import haus.nightmare.j3270.ui.StatusBar;
|
||||
import haus.nightmare.j3270.ui.TerminalPanel;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
import haus.nightmare.j3270.ui.*;
|
||||
import haus.nightmare.j3270.ft.FileTransfer;
|
||||
import haus.nightmare.j3270.ft.FileTransferDialog;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.event.*;
|
||||
import java.awt.print.PrinterJob;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.*;
|
||||
|
||||
@@ -36,14 +39,22 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
private FileTransfer fileTransfer;
|
||||
private final java.util.concurrent.atomic.AtomicBoolean screenUpdatePending = new java.util.concurrent.atomic.AtomicBoolean(false);
|
||||
|
||||
// Dialog instances
|
||||
private FindDialog findDialog;
|
||||
private ScriptDialog scriptDialog;
|
||||
private FieldInspectorDialog fieldInspectorDialog;
|
||||
private PrinterSessionDialog printerSessionDialog;
|
||||
|
||||
public J3270App() {
|
||||
super("j3270 — Java TN3270 Terminal Emulator");
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setBackground(new Color(10, 10, 10));
|
||||
setBackground(Color.BLACK);
|
||||
|
||||
buildUI();
|
||||
buildMenuBar();
|
||||
|
||||
ThemeManager.addThemeChangeListener(this::onThemeChanged);
|
||||
|
||||
pack();
|
||||
setLocationRelativeTo(null);
|
||||
setMinimumSize(new Dimension(640, 400));
|
||||
@@ -52,7 +63,6 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
refreshTimer = new Timer(100, e -> {
|
||||
if (client != null) {
|
||||
statusBar.updateStatus();
|
||||
// Keep focus on terminal panel when window is active
|
||||
if (isActive() && !terminalPanel.hasFocus()) {
|
||||
terminalPanel.requestFocusInWindow();
|
||||
}
|
||||
@@ -66,12 +76,14 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
disconnect();
|
||||
if (refreshTimer != null)
|
||||
refreshTimer.stop();
|
||||
if (printerSessionDialog != null) {
|
||||
printerSessionDialog.dispose();
|
||||
}
|
||||
terminalPanel.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowActivated(WindowEvent e) {
|
||||
// When window gains focus, push to terminal panel
|
||||
terminalPanel.requestFocusInWindow();
|
||||
}
|
||||
});
|
||||
@@ -79,27 +91,36 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
|
||||
private void buildUI() {
|
||||
terminalPanel = new TerminalPanel();
|
||||
|
||||
// Status bar
|
||||
statusBar = new StatusBar();
|
||||
|
||||
// Layout
|
||||
getContentPane().setLayout(new BorderLayout());
|
||||
getContentPane().setBackground(new Color(10, 10, 10));
|
||||
getContentPane().setBackground(Color.BLACK);
|
||||
getContentPane().add(terminalPanel, BorderLayout.CENTER);
|
||||
getContentPane().add(statusBar, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
private void onThemeChanged(UITheme theme) {
|
||||
buildMenuBar();
|
||||
statusBar.applyTheme(theme);
|
||||
ThemeManager.applyThemeToWindow(this);
|
||||
terminalPanel.repaint();
|
||||
}
|
||||
|
||||
private void buildMenuBar() {
|
||||
JMenuBar menuBar = new JMenuBar();
|
||||
menuBar.setBackground(new Color(30, 30, 30));
|
||||
menuBar.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, new Color(50, 50, 50)));
|
||||
ThemeManager.styleMenuBar(menuBar);
|
||||
|
||||
// File menu
|
||||
int shortcutMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx();
|
||||
|
||||
// 1. File menu
|
||||
JMenu fileMenu = createMenu("File");
|
||||
fileMenu.add(createMenuItem("Connect...", KeyEvent.VK_N, this::showConnectDialog));
|
||||
fileMenu.add(createMenuItem("Disconnect", KeyEvent.VK_D, this::disconnect));
|
||||
fileMenu.addSeparator();
|
||||
fileMenu.add(createMenuItem("Save Screen As...", -1, () -> ScreenExporter.showExportDialog(this, client, terminalPanel)));
|
||||
fileMenu.add(createMenuItem("3287 Printer Session...", -1, this::showPrinterSessionDialog));
|
||||
fileMenu.add(createMenuItem("Print Screen...", KeyEvent.VK_P, this::printScreen));
|
||||
fileMenu.addSeparator();
|
||||
fileMenu.add(createMenuItem("Settings...", -1, this::showSettingsDialog));
|
||||
fileMenu.addSeparator();
|
||||
fileMenu.add(createMenuItem("Quit", KeyEvent.VK_Q, () -> {
|
||||
@@ -108,39 +129,189 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
}));
|
||||
menuBar.add(fileMenu);
|
||||
|
||||
// View menu
|
||||
// 2. Edit menu
|
||||
JMenu editMenu = createMenu("Edit");
|
||||
editMenu.add(createMenuItem("Copy", KeyEvent.VK_C, () -> terminalPanel.copySelection()));
|
||||
editMenu.add(createMenuItem("Paste", KeyEvent.VK_V, () -> terminalPanel.pasteClipboard()));
|
||||
editMenu.add(createMenuItem("Paste with Line Wrap...", -1, this::showPasteLineWrapDialog));
|
||||
editMenu.addSeparator();
|
||||
editMenu.add(createMenuItem("Select All", KeyEvent.VK_A, () -> terminalPanel.selectAll()));
|
||||
editMenu.add(createMenuItem("Clear Selection", -1, () -> terminalPanel.clearSelection()));
|
||||
editMenu.addSeparator();
|
||||
editMenu.add(createMenuItem("Find on Screen...", KeyEvent.VK_F, this::showFindDialog));
|
||||
editMenu.add(createMenuItem("Find Next", KeyEvent.VK_G, this::findNext));
|
||||
editMenu.add(createMenuItem("Find Previous", KeyEvent.VK_G, this::findPrevious, true));
|
||||
menuBar.add(editMenu);
|
||||
|
||||
// 3. View menu
|
||||
JMenu viewMenu = createMenu("View");
|
||||
viewMenu.add(createMenuItem("Font Size +", KeyEvent.VK_EQUALS, () -> adjustFontSize(2)));
|
||||
viewMenu.add(createMenuItem("Font Size -", KeyEvent.VK_MINUS, () -> adjustFontSize(-2)));
|
||||
viewMenu.add(createMenuItem("Font Size +", KeyEvent.VK_EQUALS, () -> adjustFontSize(2), true));
|
||||
viewMenu.add(createMenuItem("Font Size -", KeyEvent.VK_MINUS, () -> adjustFontSize(-2), true));
|
||||
viewMenu.add(createMenuItem("Reset Font", KeyEvent.VK_0, () -> {
|
||||
terminalPanel.setFontSize(16);
|
||||
terminalPanel.guardedPack();
|
||||
}));
|
||||
}, true));
|
||||
viewMenu.addSeparator();
|
||||
|
||||
// UI Theme Submenu
|
||||
JMenu themeMenu = createMenu("UI Theme");
|
||||
ButtonGroup themeGroup = new ButtonGroup();
|
||||
|
||||
JRadioButtonMenuItem darkThemeItem = new JRadioButtonMenuItem("Dark Mode", ThemeManager.isDark());
|
||||
ThemeManager.styleMenuItem(darkThemeItem);
|
||||
darkThemeItem.addActionListener(e -> {
|
||||
haus.nightmare.j3270.config.Settings.setJavaUiTheme(UITheme.DARK);
|
||||
ThemeManager.setTheme(UITheme.DARK);
|
||||
});
|
||||
themeGroup.add(darkThemeItem);
|
||||
themeMenu.add(darkThemeItem);
|
||||
|
||||
JRadioButtonMenuItem lightThemeItem = new JRadioButtonMenuItem("Light Mode", ThemeManager.isLight());
|
||||
ThemeManager.styleMenuItem(lightThemeItem);
|
||||
lightThemeItem.addActionListener(e -> {
|
||||
haus.nightmare.j3270.config.Settings.setJavaUiTheme(UITheme.LIGHT);
|
||||
ThemeManager.setTheme(UITheme.LIGHT);
|
||||
});
|
||||
themeGroup.add(lightThemeItem);
|
||||
themeMenu.add(lightThemeItem);
|
||||
|
||||
viewMenu.add(themeMenu);
|
||||
viewMenu.addSeparator();
|
||||
|
||||
JCheckBoxMenuItem rulerItem = new JCheckBoxMenuItem("Crosshair Ruler", terminalPanel.isCrosshairRulerEnabled());
|
||||
ThemeManager.styleMenuItem(rulerItem);
|
||||
rulerItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.ALT_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
|
||||
rulerItem.addActionListener(e -> {
|
||||
boolean en = rulerItem.isSelected();
|
||||
terminalPanel.setCrosshairRulerEnabled(en);
|
||||
haus.nightmare.j3270.config.Settings.setCrosshairRuler(en);
|
||||
});
|
||||
viewMenu.add(rulerItem);
|
||||
|
||||
JMenu cursorMenu = createMenu("Cursor Style");
|
||||
ButtonGroup cursorGroup = new ButtonGroup();
|
||||
JRadioButtonMenuItem blockCursorItem = new JRadioButtonMenuItem("Block Cursor", terminalPanel.getCursorStyle() == TerminalPanel.CursorStyle.BLOCK);
|
||||
ThemeManager.styleMenuItem(blockCursorItem);
|
||||
blockCursorItem.addActionListener(e -> {
|
||||
terminalPanel.setCursorStyle(TerminalPanel.CursorStyle.BLOCK);
|
||||
haus.nightmare.j3270.config.Settings.setCursorStyle("BLOCK");
|
||||
});
|
||||
cursorGroup.add(blockCursorItem);
|
||||
cursorMenu.add(blockCursorItem);
|
||||
|
||||
JRadioButtonMenuItem underlineCursorItem = new JRadioButtonMenuItem("Underline Cursor", terminalPanel.getCursorStyle() == TerminalPanel.CursorStyle.UNDERLINE);
|
||||
ThemeManager.styleMenuItem(underlineCursorItem);
|
||||
underlineCursorItem.addActionListener(e -> {
|
||||
terminalPanel.setCursorStyle(TerminalPanel.CursorStyle.UNDERLINE);
|
||||
haus.nightmare.j3270.config.Settings.setCursorStyle("UNDERLINE");
|
||||
});
|
||||
cursorGroup.add(underlineCursorItem);
|
||||
cursorMenu.add(underlineCursorItem);
|
||||
viewMenu.add(cursorMenu);
|
||||
viewMenu.addSeparator();
|
||||
|
||||
// CodePage Submenu
|
||||
JMenu cpMenu = createMenu("Code Page");
|
||||
String[] codePages = {
|
||||
"037 - US / Canada / Brazil",
|
||||
"1047 - IBM Open Systems / z/OS Unix",
|
||||
"500 - International Latin-1",
|
||||
"273 - Germany / Austria",
|
||||
"277 - Denmark / Norway",
|
||||
"278 - Sweden / Finland",
|
||||
"280 - Italy",
|
||||
"284 - Spain / Latin America",
|
||||
"285 - United Kingdom",
|
||||
"297 - France",
|
||||
"870 - Eastern Europe / Latin-2",
|
||||
"871 - Iceland",
|
||||
"875 - Greece (Greek)",
|
||||
"1026 - Turkey (Turkish)",
|
||||
"1140 - US / Canada (Euro \u20AC)",
|
||||
"1141 - Germany / Austria (Euro \u20AC)",
|
||||
"1148 - International (Euro \u20AC)",
|
||||
"930 - Japanese Katakana Mixed DBCS",
|
||||
"939 - Japanese Latin Mixed DBCS",
|
||||
"935 - Simplified Chinese Mixed DBCS",
|
||||
"937 - Traditional Chinese Mixed DBCS",
|
||||
"933 - Korean Mixed DBCS"
|
||||
};
|
||||
for (String cpItem : codePages) {
|
||||
String cpId = cpItem.split(" -")[0].trim();
|
||||
JMenuItem cpMi = new JMenuItem(cpItem);
|
||||
ThemeManager.styleMenuItem(cpMi);
|
||||
cpMi.addActionListener(e -> changeCodePage(cpId));
|
||||
cpMenu.add(cpMi);
|
||||
}
|
||||
viewMenu.add(cpMenu);
|
||||
|
||||
// Graphics Mode Submenu
|
||||
JMenu gfxMenu = createMenu("Graphics Mode");
|
||||
for (GraphicsMode gm : GraphicsMode.values()) {
|
||||
JMenuItem gmMi = new JMenuItem(gm.name());
|
||||
ThemeManager.styleMenuItem(gmMi);
|
||||
gmMi.addActionListener(e -> changeGraphicsMode(gm));
|
||||
gfxMenu.add(gmMi);
|
||||
}
|
||||
viewMenu.add(gfxMenu);
|
||||
|
||||
viewMenu.addSeparator();
|
||||
viewMenu.add(createMenuItem("Field Inspector...", -1, this::showFieldInspectorDialog));
|
||||
menuBar.add(viewMenu);
|
||||
|
||||
// Actions menu
|
||||
// 4. Actions menu
|
||||
JMenu actionsMenu = createMenu("Actions");
|
||||
actionsMenu.add(createMenuItem("Send Enter", KeyEvent.VK_ENTER, () -> {
|
||||
if (client != null && client.getConnectionState().isFullSession()) {
|
||||
client.sendEnter();
|
||||
terminalPanel.repaint();
|
||||
}
|
||||
}, true));
|
||||
actionsMenu.add(createMenuItem("Clear", KeyEvent.VK_K, () -> {
|
||||
if (client != null)
|
||||
client.sendClear();
|
||||
terminalPanel.repaint();
|
||||
}));
|
||||
}, true));
|
||||
actionsMenu.add(createMenuItem("Reset", KeyEvent.VK_R, () -> {
|
||||
if (client != null)
|
||||
client.reset();
|
||||
terminalPanel.repaint();
|
||||
}));
|
||||
}, true));
|
||||
actionsMenu.add(createMenuItem("Erase Input", KeyEvent.VK_E, () -> {
|
||||
if (client != null && client.getConnectionState().isFullSession()) {
|
||||
client.eraseInput();
|
||||
terminalPanel.repaint();
|
||||
}
|
||||
}, true));
|
||||
actionsMenu.add(createMenuItem("Attention", KeyEvent.VK_A, () -> {
|
||||
if (client != null && client.isConnected()) client.attn();
|
||||
}, true));
|
||||
actionsMenu.add(createMenuItem("System Request", KeyEvent.VK_S, () -> {
|
||||
if (client != null && client.isConnected()) {
|
||||
client.sysReq();
|
||||
terminalPanel.repaint();
|
||||
}
|
||||
}, true));
|
||||
actionsMenu.add(createMenuItem("Cursor Select", KeyEvent.VK_Q, () -> {
|
||||
if (client != null && client.getConnectionState().isFullSession()) {
|
||||
client.cursorSelect();
|
||||
terminalPanel.repaint();
|
||||
}
|
||||
}, true));
|
||||
actionsMenu.addSeparator();
|
||||
actionsMenu.add(createMenuItem("Toggle Light Pen (Alt+L)", KeyEvent.VK_L, () -> {
|
||||
terminalPanel.toggleLightPen();
|
||||
}, true));
|
||||
actionsMenu.addSeparator();
|
||||
actionsMenu.add(createMenuItem("File Transfer...", KeyEvent.VK_T, this::showFileTransferDialog));
|
||||
actionsMenu.add(createMenuItem("Run Keystrokes / Script...", -1, this::showScriptDialog));
|
||||
actionsMenu.add(createMenuItem("File Transfer...", KeyEvent.VK_T, this::showFileTransferDialog, true));
|
||||
menuBar.add(actionsMenu);
|
||||
|
||||
// Help menu
|
||||
// 5. Help menu
|
||||
JMenu helpMenu = createMenu("Help");
|
||||
helpMenu.add(createMenuItem("Key Mappings", -1, this::showKeyMappings));
|
||||
helpMenu.add(createMenuItem("Mnemonic Keystroke Reference", -1, this::showMnemonicHelp));
|
||||
helpMenu.addSeparator();
|
||||
helpMenu.add(createMenuItem("About", -1, this::showAbout));
|
||||
menuBar.add(helpMenu);
|
||||
|
||||
@@ -149,14 +320,12 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
|
||||
private JMenu createMenu(String name) {
|
||||
JMenu menu = new JMenu(name);
|
||||
menu.setForeground(new Color(200, 200, 200));
|
||||
return menu;
|
||||
return ThemeManager.styleMenu(menu);
|
||||
}
|
||||
|
||||
private JMenuItem createMenuItem(String name, int mnemonic, Runnable action, boolean useAlt) {
|
||||
JMenuItem item = new JMenuItem(name);
|
||||
item.setBackground(new Color(40, 40, 40));
|
||||
item.setForeground(new Color(200, 200, 200));
|
||||
ThemeManager.styleMenuItem(item);
|
||||
if (mnemonic > 0) {
|
||||
int modifier = useAlt ? KeyEvent.ALT_DOWN_MASK : Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx();
|
||||
item.setAccelerator(KeyStroke.getKeyStroke(mnemonic, modifier));
|
||||
@@ -169,6 +338,164 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
return createMenuItem(name, mnemonic, action, false);
|
||||
}
|
||||
|
||||
// ========== Dialog Openers ==========
|
||||
|
||||
private void showFindDialog() {
|
||||
if (client == null) {
|
||||
JOptionPane.showMessageDialog(this, "Connect to a host before using Find.", "Not Connected", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
if (findDialog == null) {
|
||||
findDialog = new FindDialog(this, client, terminalPanel);
|
||||
}
|
||||
findDialog.setVisible(true);
|
||||
}
|
||||
|
||||
private void findNext() {
|
||||
if (client == null) return;
|
||||
String last = FindDialog.getLastSearchText();
|
||||
if (last == null || last.isEmpty()) {
|
||||
showFindDialog();
|
||||
} else {
|
||||
ScreenBuffer sb = client.getScreenBuffer();
|
||||
int curAddr = sb.getCursorAddress();
|
||||
int cols = sb.getDisplayCols();
|
||||
int curRow = curAddr / cols;
|
||||
int curCol = (curAddr % cols + 1) % cols;
|
||||
if (curCol == 0) curRow = (curRow + 1) % sb.getDisplayRows();
|
||||
|
||||
int found = client.getPS().searchString(last, curRow, curCol, ECLConstants.SEARCH_FORWARD, !FindDialog.getLastMatchCase());
|
||||
if (found >= 0) {
|
||||
sb.setCursorAddress(found);
|
||||
terminalPanel.setSearchHighlight(found, last.length());
|
||||
terminalPanel.repaint();
|
||||
} else {
|
||||
Toolkit.getDefaultToolkit().beep();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void findPrevious() {
|
||||
if (client == null) return;
|
||||
String last = FindDialog.getLastSearchText();
|
||||
if (last == null || last.isEmpty()) {
|
||||
showFindDialog();
|
||||
} else {
|
||||
ScreenBuffer sb = client.getScreenBuffer();
|
||||
int curAddr = sb.getCursorAddress();
|
||||
int cols = sb.getDisplayCols();
|
||||
int curRow = curAddr / cols;
|
||||
int curCol = (curAddr % cols - 1 + cols) % cols;
|
||||
if (curCol == cols - 1) curRow = (curRow - 1 + sb.getDisplayRows()) % sb.getDisplayRows();
|
||||
|
||||
int found = client.getPS().searchString(last, curRow, curCol, ECLConstants.SEARCH_BACKWARD, !FindDialog.getLastMatchCase());
|
||||
if (found >= 0) {
|
||||
sb.setCursorAddress(found);
|
||||
terminalPanel.setSearchHighlight(found, last.length());
|
||||
terminalPanel.repaint();
|
||||
} else {
|
||||
Toolkit.getDefaultToolkit().beep();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showScriptDialog() {
|
||||
if (client == null) {
|
||||
JOptionPane.showMessageDialog(this, "Connect to a host before running scripts.", "Not Connected", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
if (scriptDialog == null) {
|
||||
scriptDialog = new ScriptDialog(this, client, terminalPanel);
|
||||
}
|
||||
scriptDialog.setVisible(true);
|
||||
}
|
||||
|
||||
private void showFieldInspectorDialog() {
|
||||
if (client == null) {
|
||||
JOptionPane.showMessageDialog(this, "Connect to a host before inspecting fields.", "Not Connected", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
if (fieldInspectorDialog == null) {
|
||||
fieldInspectorDialog = new FieldInspectorDialog(this, client, terminalPanel);
|
||||
} else {
|
||||
fieldInspectorDialog.refreshFields();
|
||||
}
|
||||
fieldInspectorDialog.setVisible(true);
|
||||
}
|
||||
|
||||
private void showPrinterSessionDialog() {
|
||||
if (printerSessionDialog == null) {
|
||||
String host = (client != null) ? client.getConfig().getHost() : lastHost;
|
||||
int port = (client != null) ? client.getConfig().getPort() : lastPort;
|
||||
boolean tls = (client != null) ? client.getConfig().isUseTls() : false;
|
||||
printerSessionDialog = new PrinterSessionDialog(this, host, port, tls);
|
||||
}
|
||||
printerSessionDialog.setVisible(true);
|
||||
}
|
||||
|
||||
private void showPasteLineWrapDialog() {
|
||||
if (client == null || !client.getConnectionState().isFullSession()) {
|
||||
JOptionPane.showMessageDialog(this, "Connect to a 3270 session before pasting.", "Not Connected", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String text = (String) Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor);
|
||||
if (text == null || text.isEmpty()) {
|
||||
JOptionPane.showMessageDialog(this, "Clipboard is empty.", "Clipboard", JOptionPane.INFORMATION_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
int cols = client.getScreenBuffer().getDisplayCols();
|
||||
String endColStr = JOptionPane.showInputDialog(this, "Enter right margin column (1-" + cols + ") for wrapping [0 = no right margin]:", "80");
|
||||
if (endColStr != null) {
|
||||
int endCol = 0;
|
||||
try {
|
||||
endCol = Integer.parseInt(endColStr.trim());
|
||||
} catch (NumberFormatException ignored) {}
|
||||
terminalPanel.pasteLineWrap(text, endCol, true);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(this, "Could not read clipboard: " + ex.getMessage(), "Paste Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private void changeCodePage(String cpId) {
|
||||
if (client != null) {
|
||||
client.setCodePage(cpId);
|
||||
client.getScreenBuffer().translateToUnicode();
|
||||
terminalPanel.refreshScreen();
|
||||
statusBar.updateStatus();
|
||||
}
|
||||
haus.nightmare.j3270.config.Settings.setCodePage(cpId);
|
||||
}
|
||||
|
||||
private void changeGraphicsMode(GraphicsMode mode) {
|
||||
if (client != null && client.getDataStreamProcessor() != null) {
|
||||
client.getDataStreamProcessor().getQueryReplyBuilder().setGraphicsMode(mode);
|
||||
terminalPanel.repaint();
|
||||
}
|
||||
haus.nightmare.j3270.config.Settings.setGraphicsMode(mode);
|
||||
}
|
||||
|
||||
private void printScreen() {
|
||||
if (client == null) {
|
||||
JOptionPane.showMessageDialog(this, "Connect to a host before printing screen.", "Not Connected", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
PrinterJob job = PrinterJob.getPrinterJob();
|
||||
job.setJobName("j3270 Screen Capture");
|
||||
job.setPrintable(terminalPanel);
|
||||
if (job.printDialog()) {
|
||||
try {
|
||||
job.print();
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(this, "Print error: " + ex.getMessage(), "Print Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showFileTransferDialog() {
|
||||
if (client == null) {
|
||||
JOptionPane.showMessageDialog(this, "Connect to a host before using File Transfer.",
|
||||
@@ -187,7 +514,6 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
monitor.setMillisToDecideToPopup(0);
|
||||
monitor.setMillisToPopup(0);
|
||||
|
||||
// Start a timer to check for manual cancellation
|
||||
Timer cancelCheckTimer = new Timer(500, e -> {
|
||||
if (monitor != null && monitor.isCanceled() && fileTransfer != null) {
|
||||
fileTransfer.cancel();
|
||||
@@ -208,7 +534,6 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
public void onBytesTransferred(long bytes) {
|
||||
if (monitor != null) {
|
||||
monitor.setNote(bytes + " bytes transferred");
|
||||
// We don't always know max size in IND$FILE, so we just pulse/update note
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,6 +567,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
dialog.setInitialPort(initialPort);
|
||||
dialog.setInitialTls(haus.nightmare.j3270.config.Settings.getAutoConnectTls());
|
||||
dialog.setInitialVerifyCert(haus.nightmare.j3270.config.Settings.getAutoConnectVerifyCert());
|
||||
dialog.setInitialCodePage(haus.nightmare.j3270.config.Settings.getCodePage());
|
||||
dialog.setVisible(true);
|
||||
|
||||
if (dialog.isConfirmed()) {
|
||||
@@ -265,16 +591,14 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
lastHost = config.getHost();
|
||||
lastPort = config.getPort();
|
||||
|
||||
// Save for auto-connect
|
||||
haus.nightmare.j3270.config.Settings.setAutoConnectHost(config.getHost());
|
||||
haus.nightmare.j3270.config.Settings.setAutoConnectPort(config.getPort());
|
||||
haus.nightmare.j3270.config.Settings.setAutoConnectTls(config.isUseTls());
|
||||
haus.nightmare.j3270.config.Settings.setAutoConnectVerifyCert(config.isTlsVerifyCert());
|
||||
haus.nightmare.j3270.config.Settings.setCodePage(config.getCodePage());
|
||||
|
||||
// Disconnect existing connection
|
||||
disconnect();
|
||||
|
||||
// Wire interactive certificate verifier if none configured
|
||||
if (config.getCertificateVerifier() == null) {
|
||||
config.setCertificateVerifier((chain, authType, exception) ->
|
||||
haus.nightmare.j3270.ui.UntrustedCertificateDialog.showPrompt(this, config.getHost(), config.getPort(), chain, exception)
|
||||
@@ -291,7 +615,6 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
String tlsIndicator = config.isUseTls() ? (config.isTlsVerifyCert() ? " [TLS]" : " [TLS/Unverified]") : "";
|
||||
setTitle("j3270 — " + config.getHost() + ":" + config.getPort() + tlsIndicator);
|
||||
|
||||
// Resize window to match model
|
||||
terminalPanel.guardedPack();
|
||||
|
||||
new Thread(() -> {
|
||||
@@ -307,7 +630,6 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
}
|
||||
}, "Connect-Thread").start();
|
||||
|
||||
// Focus the terminal panel
|
||||
terminalPanel.requestFocusInWindow();
|
||||
}
|
||||
|
||||
@@ -330,8 +652,6 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
int current = terminalPanel.getFontSize();
|
||||
int newSize = Math.max(8, Math.min(72, current + delta));
|
||||
terminalPanel.setFontSize(newSize);
|
||||
// Pack after font change — setFontSize sets the resize guard
|
||||
// to prevent the componentResized from re-triggering autoFitFont
|
||||
terminalPanel.guardedPack();
|
||||
}
|
||||
|
||||
@@ -344,7 +664,6 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
statusBar.updateStatus();
|
||||
terminalPanel.repaint();
|
||||
|
||||
// Auto-resize and focus on first full session
|
||||
if (newState.isFullSession() && !oldState.isFullSession()) {
|
||||
terminalPanel.guardedPack();
|
||||
terminalPanel.requestFocusInWindow();
|
||||
@@ -365,6 +684,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
if (deviceName != null) {
|
||||
setTitle("j3270 — " + lastHost + ":" + lastPort + " [" + deviceName + "]");
|
||||
}
|
||||
statusBar.updateStatus();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -375,10 +695,6 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
if (screenUpdatePending.compareAndSet(false, true)) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
screenUpdatePending.set(false);
|
||||
// During an active file transfer, let the CUT/DFT handler drive
|
||||
// keyboard state. In x3270, ft_cut_data() runs before WCC
|
||||
// keyboard-restore is applied — the keyboard stays locked for the
|
||||
// entire CUT transfer.
|
||||
if (fileTransfer != null) {
|
||||
fileTransfer.onScreenUpdated();
|
||||
}
|
||||
@@ -400,6 +716,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
public void onScreenSizeChanged(int rows, int cols) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
terminalPanel.guardedPack();
|
||||
statusBar.updateStatus();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -419,32 +736,78 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
"Delete — Delete character\n" +
|
||||
"Backspace — Backspace\n" +
|
||||
"Insert — Toggle insert mode\n" +
|
||||
"Escape — Reset\n" +
|
||||
"Escape / Alt+R — Reset\n" +
|
||||
"PageUp/Down — PF7/PF8\n" +
|
||||
"Alt+C — Clear\n" +
|
||||
"Alt+1/2/3 — PA1/PA2/PA3\n" +
|
||||
"Cmd+D — Disconnect\n" +
|
||||
"Cmd+Q — Quit\n" +
|
||||
"Cmd+=/-/0 — Font size +/-/reset";
|
||||
"Alt+C / Alt+K — Clear\n" +
|
||||
"Alt+E — Erase Input\n" +
|
||||
"Alt+A — Attention\n" +
|
||||
"Alt+S — System Request\n" +
|
||||
"Alt+Q — Cursor Select\n" +
|
||||
"Alt+L — Toggle Light Pen\n" +
|
||||
"Alt+T — File Transfer\n" +
|
||||
"Alt+Shift+R — Crosshair Ruler\n" +
|
||||
"Alt+=/-/0 — Font size +/-/reset\n" +
|
||||
"Cmd/Ctrl+F — Find on Screen\n" +
|
||||
"Cmd/Ctrl+G / F3— Find Next\n" +
|
||||
"Cmd/Ctrl+D — Disconnect\n" +
|
||||
"Cmd/Ctrl+Q — Quit";
|
||||
|
||||
JTextArea area = new JTextArea(text);
|
||||
area.setEditable(false);
|
||||
area.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 13));
|
||||
area.setBackground(new Color(30, 30, 30));
|
||||
area.setForeground(new Color(200, 200, 200));
|
||||
ThemeManager.styleTextArea(area);
|
||||
JScrollPane sp = new JScrollPane(area);
|
||||
sp.setPreferredSize(new Dimension(360, 400));
|
||||
ThemeManager.styleScrollPane(sp);
|
||||
sp.setPreferredSize(new Dimension(380, 430));
|
||||
JOptionPane.showMessageDialog(this, sp, "Key Mappings", JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
|
||||
private void showMnemonicHelp() {
|
||||
String text = "IBM Host On-Demand ECL Mnemonic Tokens:\n\n" +
|
||||
"[enter] — Send Enter key\n" +
|
||||
"[tab] — Tab to next field\n" +
|
||||
"[backtab] — Back-tab to previous field\n" +
|
||||
"[clear] — Clear screen\n" +
|
||||
"[reset] — Reset keyboard lock\n" +
|
||||
"[eraseeof] — Erase to End-of-Field\n" +
|
||||
"[eraseinput] — Erase all unprotected fields\n" +
|
||||
"[newline] — Advance to next line\n" +
|
||||
"[dup] — Insert Duplicate order (0x1C)\n" +
|
||||
"[fm] — Insert Field Mark order (0x1E)\n" +
|
||||
"[attn] — Send Attention signal\n" +
|
||||
"[sysreq] — System Request\n" +
|
||||
"[cursel] — Cursor Select\n" +
|
||||
"[pf1] - [pf24] — PF1 through PF24 keys\n" +
|
||||
"[pa1] - [pa3] — PA1 through PA3 keys\n" +
|
||||
"[up]/[down] — Cursor up / down\n" +
|
||||
"[left]/[right] — Cursor left / right\n" +
|
||||
"[home] — Cursor to home\n\n" +
|
||||
"Example Script:\n" +
|
||||
"LOGON APPLID(TSO)[enter]USER[tab]PASSWORD[enter][pf3]";
|
||||
|
||||
JTextArea area = new JTextArea(text);
|
||||
area.setEditable(false);
|
||||
area.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 13));
|
||||
ThemeManager.styleTextArea(area);
|
||||
JScrollPane sp = new JScrollPane(area);
|
||||
ThemeManager.styleScrollPane(sp);
|
||||
sp.setPreferredSize(new Dimension(440, 430));
|
||||
JOptionPane.showMessageDialog(this, sp, "Mnemonic Keystroke Reference", JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
|
||||
private void showAbout() {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"j3270 — Java TN3270 Terminal Emulator\n\n" +
|
||||
"A Java reimplementation of x3270.\n" +
|
||||
"lib3270j v0.1.0\n\n" +
|
||||
"Supports: TN3270, TN3270E (RFC 2355)\n" +
|
||||
"Models: IBM 3278/3279 Models 2-5\n" +
|
||||
"Colors, Extended Attributes, Query Replies",
|
||||
"A pure Java reimplementation of x3270 & IBM Host On-Demand ECL.\n" +
|
||||
"lib3270j v0.2.0\n\n" +
|
||||
"Features:\n" +
|
||||
"• TN3270 & TN3270E (RFC 2355, SSCP-LU, NVT)\n" +
|
||||
"• Models 2-5 & Dynamic Geometry\n" +
|
||||
"• IBM 3287/3286 Printer Session Emulation (SCS & LU3)\n" +
|
||||
"• IND$FILE CUT & DFT File Transfer + Host Catalog Query\n" +
|
||||
"• GDDM/GOCA Vector Graphics & Programmed Symbols\n" +
|
||||
"• 22+ Pluggable EBCDIC Codepages & DBCS Support\n" +
|
||||
"• ECL Presentation Space Search, Inspector & Scripting",
|
||||
"About j3270", JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
|
||||
@@ -455,48 +818,49 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
boolean cliTls = false;
|
||||
boolean cliNoVerifyCert = false;
|
||||
Boolean cliTn3270e = null;
|
||||
haus.nightmare.lib3270j.graphics.GraphicsMode cliGraphicsMode = null;
|
||||
GraphicsMode cliGraphicsMode = null;
|
||||
String configFile = null;
|
||||
|
||||
java.util.List<String> remainingArgs = new java.util.ArrayList<>();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if ("--debug".equals(args[i]) || "-d".equals(args[i])) {
|
||||
String arg = args[i];
|
||||
if ("-d".equals(arg) || "--debug".equals(arg)) {
|
||||
debug = true;
|
||||
} else if ("--tls".equals(args[i]) || "--ssl".equals(args[i]) || "-s".equals(args[i])) {
|
||||
} else if ("-s".equals(arg) || "--tls".equals(arg) || "--ssl".equals(arg)) {
|
||||
cliTls = true;
|
||||
} else if ("--no-verify-cert".equals(args[i]) || "--insecure".equals(args[i]) || "-k".equals(args[i])) {
|
||||
} else if ("--no-verify-cert".equals(arg) || "--insecure".equals(arg) || "-k".equals(arg)) {
|
||||
cliNoVerifyCert = true;
|
||||
} else if ("--no-tn3270e".equals(args[i]) || "--plain-tn3270".equals(args[i]) || "--plain".equals(args[i]) || "-P".equals(args[i]) || "-p".equals(args[i]) || "--non-e".equals(args[i])) {
|
||||
} else if ("--no-tn3270e".equals(arg) || "--plain-tn3270".equals(arg) || "--plain".equals(arg) || "-P".equals(arg) || "-p".equals(arg) || "--non-e".equals(arg)) {
|
||||
cliTn3270e = false;
|
||||
} else if ("--tn3270e".equals(args[i])) {
|
||||
} else if ("--tn3270e".equals(arg)) {
|
||||
cliTn3270e = true;
|
||||
} else if (args[i].startsWith("--graphics=")) {
|
||||
cliGraphicsMode = haus.nightmare.lib3270j.graphics.GraphicsMode.fromString(args[i].substring(11));
|
||||
} else if (("--graphics".equals(args[i]) || "-g".equals(args[i])) && i + 1 < args.length && !args[i + 1].startsWith("-")) {
|
||||
cliGraphicsMode = haus.nightmare.lib3270j.graphics.GraphicsMode.fromString(args[++i]);
|
||||
} else if ("--no-graphics".equals(args[i])) {
|
||||
cliGraphicsMode = haus.nightmare.lib3270j.graphics.GraphicsMode.NONE;
|
||||
} else if (("-c".equals(args[i]) || "--config".equals(args[i])) && i + 1 < args.length) {
|
||||
} else if (arg.startsWith("--graphics=")) {
|
||||
cliGraphicsMode = GraphicsMode.fromString(arg.substring(11));
|
||||
} else if (("--graphics".equals(arg) || "-g".equals(arg)) && i + 1 < args.length && !args[i + 1].startsWith("-")) {
|
||||
cliGraphicsMode = GraphicsMode.fromString(args[++i]);
|
||||
} else if ("--no-graphics".equals(arg)) {
|
||||
cliGraphicsMode = GraphicsMode.NONE;
|
||||
} else if (("-c".equals(arg) || "--config".equals(arg)) && i + 1 < args.length) {
|
||||
configFile = args[++i];
|
||||
} else if (arg.startsWith("-")) {
|
||||
System.err.println("Unknown option: " + arg);
|
||||
} else {
|
||||
remainingArgs.add(args[i]);
|
||||
remainingArgs.add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
// Configure logging for application packages
|
||||
Level logLevel = debug ? Level.ALL : Level.INFO;
|
||||
Level logLevel = debug ? Level.FINE : Level.INFO;
|
||||
|
||||
Logger globalRoot = Logger.getLogger("");
|
||||
for (java.util.logging.Handler h : globalRoot.getHandlers()) {
|
||||
globalRoot.removeHandler(h);
|
||||
}
|
||||
globalRoot.setLevel(Level.ALL);
|
||||
|
||||
java.util.logging.Filter appFilter = record -> {
|
||||
String name = record.getLoggerName();
|
||||
return name != null && (name.startsWith("haus.nightmare.lib3270j") || name.startsWith("haus.nightmare.j3270"));
|
||||
};
|
||||
java.util.logging.Filter appFilter = record -> record.getLoggerName() != null &&
|
||||
(record.getLoggerName().startsWith("haus.nightmare") || record.getLoggerName().startsWith("org.pubvm"));
|
||||
|
||||
ConsoleHandler consoleHandler = new ConsoleHandler();
|
||||
consoleHandler.setLevel(logLevel);
|
||||
consoleHandler.setLevel(Level.ALL);
|
||||
consoleHandler.setFormatter(new SimpleFormatter());
|
||||
consoleHandler.setFilter(appFilter);
|
||||
globalRoot.addHandler(consoleHandler);
|
||||
@@ -522,7 +886,6 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
System.err.println("Could not create j3270.log: " + e.getMessage());
|
||||
}
|
||||
|
||||
// Load INI config file if specified
|
||||
if (configFile != null) {
|
||||
try {
|
||||
haus.nightmare.j3270.config.Settings.loadFromIniFile(configFile);
|
||||
@@ -533,42 +896,44 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
||||
}
|
||||
}
|
||||
|
||||
// Dark look and feel
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (Exception e) {
|
||||
log.fine("Could not set system look and feel");
|
||||
}
|
||||
ThemeManager.applyUIManagerDefaults(haus.nightmare.j3270.config.Settings.getJavaUiTheme());
|
||||
ThemeManager.setTheme(haus.nightmare.j3270.config.Settings.getJavaUiTheme());
|
||||
|
||||
// Use native macOS menu bar if applicable
|
||||
System.setProperty("apple.laf.useScreenMenuBar", "true");
|
||||
System.setProperty("apple.awt.application.name", "j3270");
|
||||
|
||||
final boolean finalTls = cliTls;
|
||||
final boolean finalNoVerify = cliNoVerifyCert;
|
||||
final Boolean finalTn3270e = cliTn3270e;
|
||||
final haus.nightmare.lib3270j.graphics.GraphicsMode finalGraphicsMode = cliGraphicsMode;
|
||||
final GraphicsMode finalGraphicsMode = cliGraphicsMode;
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
J3270App app = new J3270App();
|
||||
app.setVisible(true);
|
||||
|
||||
// If host:port given on command line, connect directly
|
||||
if (!remainingArgs.isEmpty()) {
|
||||
String hostArg = remainingArgs.get(0);
|
||||
int port = finalTls ? 992 : 23;
|
||||
if (remainingArgs.size() >= 2) {
|
||||
try {
|
||||
port = Integer.parseInt(remainingArgs.get(1));
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
TerminalModel model = TerminalModel.IBM_3279_4;
|
||||
if (remainingArgs.size() >= 3) {
|
||||
try {
|
||||
int modelNum = Integer.parseInt(remainingArgs.get(2));
|
||||
model = TerminalModel.forModel(modelNum, true);
|
||||
} catch (Exception ignored) {
|
||||
String mArg = remainingArgs.get(2).trim();
|
||||
if (mArg.equalsIgnoreCase("dynamic") || mArg.equals("0")) {
|
||||
model = TerminalModel.IBM_DYNAMIC;
|
||||
} else {
|
||||
try {
|
||||
int modelNum = Integer.parseInt(mArg);
|
||||
model = TerminalModel.forModel(modelNum, true);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
ConnectionConfig config = ConnectionConfig.parseHostString(hostArg, port, model);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package haus.nightmare.j3270.config;
|
||||
|
||||
import haus.nightmare.j3270.ui.UITheme;
|
||||
import java.util.prefs.Preferences;
|
||||
import java.awt.Color;
|
||||
import java.io.*;
|
||||
@@ -17,6 +18,15 @@ public class Settings {
|
||||
AUTO_CONNECT
|
||||
}
|
||||
|
||||
public static UITheme getJavaUiTheme() {
|
||||
String themeStr = prefs.get("javaUiTheme", UITheme.DARK.name());
|
||||
return UITheme.fromString(themeStr);
|
||||
}
|
||||
|
||||
public static void setJavaUiTheme(UITheme theme) {
|
||||
prefs.put("javaUiTheme", (theme != null ? theme : UITheme.DARK).name());
|
||||
}
|
||||
|
||||
public static String getFontFamily() {
|
||||
return prefs.get("fontFamily", "Monospaced");
|
||||
}
|
||||
@@ -95,6 +105,30 @@ public class Settings {
|
||||
prefs.put("graphicsMode", (mode != null ? mode : haus.nightmare.lib3270j.graphics.GraphicsMode.BOTH).name());
|
||||
}
|
||||
|
||||
public static String getCodePage() {
|
||||
return prefs.get("codePage", "037");
|
||||
}
|
||||
|
||||
public static void setCodePage(String codePage) {
|
||||
prefs.put("codePage", (codePage != null && !codePage.trim().isEmpty()) ? codePage.trim() : "037");
|
||||
}
|
||||
|
||||
public static int getDynamicRows() {
|
||||
return prefs.getInt("dynamicRows", 62);
|
||||
}
|
||||
|
||||
public static void setDynamicRows(int rows) {
|
||||
prefs.putInt("dynamicRows", Math.max(1, rows));
|
||||
}
|
||||
|
||||
public static int getDynamicCols() {
|
||||
return prefs.getInt("dynamicCols", 160);
|
||||
}
|
||||
|
||||
public static void setDynamicCols(int cols) {
|
||||
prefs.putInt("dynamicCols", Math.max(1, cols));
|
||||
}
|
||||
|
||||
public static Color getColorOverride(int index, Color defaultColor) {
|
||||
String hex = prefs.get("color_" + index, null);
|
||||
try {
|
||||
@@ -215,12 +249,44 @@ public class Settings {
|
||||
prefs.putBoolean("blockSelectMode", block);
|
||||
}
|
||||
|
||||
// ========== Crosshair Ruler ==========
|
||||
|
||||
public static boolean getCrosshairRuler() {
|
||||
return prefs.getBoolean("crosshairRuler", false);
|
||||
}
|
||||
|
||||
public static void setCrosshairRuler(boolean enabled) {
|
||||
prefs.putBoolean("crosshairRuler", enabled);
|
||||
}
|
||||
|
||||
// ========== Cursor Style ==========
|
||||
|
||||
public static String getCursorStyle() {
|
||||
return prefs.get("cursorStyle", "BLOCK");
|
||||
}
|
||||
|
||||
public static void setCursorStyle(String style) {
|
||||
prefs.put("cursorStyle", style != null ? style.toUpperCase() : "BLOCK");
|
||||
}
|
||||
|
||||
private static void applyConfigEntry(String section, String key, String value) {
|
||||
switch (section) {
|
||||
case "appearance":
|
||||
switch (key) {
|
||||
case "fontFamily": setFontFamily(value); break;
|
||||
case "fontSize": setFontSize(Integer.parseInt(value)); break;
|
||||
case "javaUiTheme":
|
||||
case "theme":
|
||||
case "uiTheme":
|
||||
setJavaUiTheme(UITheme.fromString(value));
|
||||
break;
|
||||
case "crosshairRuler":
|
||||
case "ruler":
|
||||
setCrosshairRuler(Boolean.parseBoolean(value));
|
||||
break;
|
||||
case "cursorStyle":
|
||||
setCursorStyle(value);
|
||||
break;
|
||||
default:
|
||||
log.warning("Unknown appearance key: " + key);
|
||||
}
|
||||
@@ -256,6 +322,19 @@ public class Settings {
|
||||
case "enableTn3270e":
|
||||
setAutoConnectTn3270e(Boolean.parseBoolean(value));
|
||||
break;
|
||||
case "codePage":
|
||||
case "codepage":
|
||||
case "charset":
|
||||
setCodePage(value);
|
||||
break;
|
||||
case "dynamicRows":
|
||||
case "dynamic_rows":
|
||||
setDynamicRows(Integer.parseInt(value));
|
||||
break;
|
||||
case "dynamicCols":
|
||||
case "dynamic_cols":
|
||||
setDynamicCols(Integer.parseInt(value));
|
||||
break;
|
||||
case "blockSelectMode": setBlockSelectMode(Boolean.parseBoolean(value)); break;
|
||||
default:
|
||||
log.warning("Unknown behavior/connection key: " + key);
|
||||
@@ -309,8 +388,11 @@ public class Settings {
|
||||
|
||||
// [appearance]
|
||||
w.println("[appearance]");
|
||||
w.println("javaUiTheme = " + getJavaUiTheme().name());
|
||||
w.println("fontFamily = " + getFontFamily());
|
||||
w.println("fontSize = " + getFontSize());
|
||||
w.println("crosshairRuler = " + getCrosshairRuler());
|
||||
w.println("cursorStyle = " + getCursorStyle());
|
||||
w.println();
|
||||
|
||||
// [behavior]
|
||||
@@ -324,6 +406,9 @@ public class Settings {
|
||||
w.println("autoConnectVerifyCert = " + getAutoConnectVerifyCert());
|
||||
w.println("autoConnectTn3270e = " + getAutoConnectTn3270e());
|
||||
}
|
||||
w.println("codePage = " + getCodePage());
|
||||
w.println("dynamicRows = " + getDynamicRows());
|
||||
w.println("dynamicCols = " + getDynamicCols());
|
||||
w.println("blockSelectMode = " + getBlockSelectMode());
|
||||
w.println();
|
||||
|
||||
|
||||
@@ -53,6 +53,10 @@ public class FileTransfer implements FTCut.FTCutListener, FTDft.FTDftListener {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public Telnet3270Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new file transfer with the given configuration.
|
||||
* @return null if started successfully, error message otherwise.
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package haus.nightmare.j3270.ft;
|
||||
|
||||
import haus.nightmare.j3270.ui.HostDirectoryDialog;
|
||||
import haus.nightmare.j3270.ui.ThemeManager;
|
||||
import haus.nightmare.lib3270j.ft.FTConfig;
|
||||
import haus.nightmare.lib3270j.ft.FTConstants;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
@@ -18,6 +21,8 @@ public class FileTransferDialog extends JDialog {
|
||||
private JTextField localFileField;
|
||||
private JButton browseLocalButton;
|
||||
private JTextField hostFileField;
|
||||
private JButton browseHostButton;
|
||||
private JComboBox<Integer> mtuCombo;
|
||||
|
||||
// Options
|
||||
private JRadioButton asciiRadio;
|
||||
@@ -40,7 +45,7 @@ public class FileTransferDialog extends JDialog {
|
||||
private JButton cancelButton;
|
||||
|
||||
public FileTransferDialog(Frame owner, FileTransfer coordinator) {
|
||||
super(owner, "File Transfer (IND$FILE)", false); // non-modal to see progress
|
||||
super(owner, "File Transfer (IND$FILE)", false);
|
||||
this.owner = owner;
|
||||
this.coordinator = coordinator;
|
||||
|
||||
@@ -76,7 +81,6 @@ public class FileTransferDialog extends JDialog {
|
||||
dirPanel.add(Box.createRigidArea(new Dimension(15, 0)));
|
||||
dirPanel.add(sendRadio);
|
||||
|
||||
// Disable append/overwrite when sending
|
||||
sendRadio.addActionListener(e -> updateOptionStates());
|
||||
receiveRadio.addActionListener(e -> updateOptionStates());
|
||||
|
||||
@@ -85,6 +89,7 @@ public class FileTransferDialog extends JDialog {
|
||||
// Local File
|
||||
localFileField = new JTextField(20);
|
||||
browseLocalButton = new JButton("Browse...");
|
||||
ThemeManager.styleButton(browseLocalButton, ThemeManager.ButtonVariant.DEFAULT);
|
||||
browseLocalButton.addActionListener(e -> browseLocalFile());
|
||||
JPanel localPanel = new JPanel(new BorderLayout(5, 0));
|
||||
localPanel.setOpaque(false);
|
||||
@@ -94,7 +99,14 @@ public class FileTransferDialog extends JDialog {
|
||||
|
||||
// Host File
|
||||
hostFileField = new JTextField(20);
|
||||
formPanel.add(createRow("Host File:", hostFileField));
|
||||
browseHostButton = new JButton("Browse Host...");
|
||||
ThemeManager.styleButton(browseHostButton, ThemeManager.ButtonVariant.DEFAULT);
|
||||
browseHostButton.addActionListener(e -> browseHostDirectory());
|
||||
JPanel hostPanel = new JPanel(new BorderLayout(5, 0));
|
||||
hostPanel.setOpaque(false);
|
||||
hostPanel.add(hostFileField, BorderLayout.CENTER);
|
||||
hostPanel.add(browseHostButton, BorderLayout.EAST);
|
||||
formPanel.add(createRow("Host File:", hostPanel));
|
||||
|
||||
// Mode
|
||||
asciiRadio = new JRadioButton("ASCII");
|
||||
@@ -114,6 +126,11 @@ public class FileTransferDialog extends JDialog {
|
||||
|
||||
formPanel.add(createRow("Transfer Mode:", modePanel));
|
||||
|
||||
// MTU Buffer Size
|
||||
mtuCombo = new JComboBox<>(new Integer[]{2048, 4096, 8192, 16384, 32768});
|
||||
mtuCombo.setSelectedItem(FTConstants.DFT_BUF);
|
||||
formPanel.add(createRow("DFT Buffer Size:", mtuCombo));
|
||||
|
||||
// Options (checkboxes)
|
||||
crCheck = new JCheckBox("Add/Remove CR");
|
||||
crCheck.setSelected(true);
|
||||
@@ -140,10 +157,7 @@ public class FileTransferDialog extends JDialog {
|
||||
// Host-specific options panel (TSO dataset allocation)
|
||||
JPanel hostOptsPanel = new JPanel(new GridLayout(2, 4, 5, 5));
|
||||
hostOptsPanel.setOpaque(false);
|
||||
hostOptsPanel.setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createLineBorder(new Color(60, 60, 60)),
|
||||
"TSO Allocation Options (Send Only)"));
|
||||
((javax.swing.border.TitledBorder)hostOptsPanel.getBorder()).setTitleColor(new Color(180, 180, 180));
|
||||
hostOptsPanel.setBorder(ThemeManager.createTitledBorder("TSO Allocation Options (Send Only)"));
|
||||
|
||||
hostOptsPanel.add(new JLabel("RECFM:"));
|
||||
recfmField = new JTextField(5);
|
||||
@@ -167,10 +181,7 @@ public class FileTransferDialog extends JDialog {
|
||||
// CMS options
|
||||
JPanel vmOptsPanel = new JPanel(new BorderLayout(5, 0));
|
||||
vmOptsPanel.setOpaque(false);
|
||||
vmOptsPanel.setBorder(BorderFactory.createTitledBorder(
|
||||
BorderFactory.createLineBorder(new Color(60, 60, 60)),
|
||||
"CMS Options"));
|
||||
((javax.swing.border.TitledBorder)vmOptsPanel.getBorder()).setTitleColor(new Color(180, 180, 180));
|
||||
vmOptsPanel.setBorder(ThemeManager.createTitledBorder("CMS Options"));
|
||||
|
||||
optionsField = new JTextField(20);
|
||||
vmOptsPanel.add(new JLabel("Additional Options: "), BorderLayout.WEST);
|
||||
@@ -186,9 +197,11 @@ public class FileTransferDialog extends JDialog {
|
||||
buttonPanel.setOpaque(false);
|
||||
|
||||
transferButton = new JButton("Start Transfer");
|
||||
ThemeManager.styleButton(transferButton, ThemeManager.ButtonVariant.PRIMARY);
|
||||
transferButton.addActionListener(e -> startTransfer());
|
||||
|
||||
cancelButton = new JButton("Close");
|
||||
ThemeManager.styleButton(cancelButton, ThemeManager.ButtonVariant.CANCEL);
|
||||
cancelButton.addActionListener(e -> dispose());
|
||||
|
||||
buttonPanel.add(cancelButton);
|
||||
@@ -198,10 +211,7 @@ public class FileTransferDialog extends JDialog {
|
||||
|
||||
setContentPane(mainPanel);
|
||||
|
||||
// Theme styling for components
|
||||
applyTheme(mainPanel);
|
||||
|
||||
// Initialize state
|
||||
ThemeManager.applyThemeToWindow(this);
|
||||
updateOptionStates();
|
||||
}
|
||||
|
||||
@@ -236,14 +246,29 @@ public class FileTransferDialog extends JDialog {
|
||||
JFileChooser chooser = new JFileChooser();
|
||||
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
|
||||
localFileField.setText(chooser.getSelectedFile().getAbsolutePath());
|
||||
|
||||
// Auto-fill host file if empty
|
||||
if (hostFileField.getText().trim().isEmpty()) {
|
||||
hostFileField.setText(chooser.getSelectedFile().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void browseHostDirectory() {
|
||||
FTConfig.HostType hostType = (FTConfig.HostType) hostTypeCombo.getSelectedItem();
|
||||
String initialQuery = hostFileField.getText().trim();
|
||||
HostDirectoryDialog dialog = new HostDirectoryDialog(this, coordinator.getClient(), hostType, initialQuery);
|
||||
dialog.setVisible(true);
|
||||
|
||||
if (dialog.isConfirmed() && dialog.getSelectedHostFile() != null) {
|
||||
hostFileField.setText(dialog.getSelectedHostFile());
|
||||
if (localFileField.getText().trim().isEmpty()) {
|
||||
String cleanName = dialog.getSelectedHostFile().replace("'", "").replace("\"", "");
|
||||
int lastDot = cleanName.lastIndexOf('.');
|
||||
if (lastDot > 0) cleanName = cleanName.substring(lastDot + 1);
|
||||
localFileField.setText(cleanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void startTransfer() {
|
||||
if (localFileField.getText().trim().isEmpty() || hostFileField.getText().trim().isEmpty()) {
|
||||
JOptionPane.showMessageDialog(this, "Local and Host filenames are required.",
|
||||
@@ -262,6 +287,11 @@ public class FileTransferDialog extends JDialog {
|
||||
config.setAppend(appendCheck.isSelected());
|
||||
config.setOverwrite(overwriteCheck.isSelected());
|
||||
|
||||
Integer mtu = (Integer) mtuCombo.getSelectedItem();
|
||||
if (mtu != null) {
|
||||
config.setDftBufferSize(mtu);
|
||||
}
|
||||
|
||||
config.setRecfm(recfmField.getText().trim());
|
||||
config.setLrecl(lreclField.getText().trim());
|
||||
config.setBlksize(blksizeField.getText().trim());
|
||||
@@ -272,7 +302,6 @@ public class FileTransferDialog extends JDialog {
|
||||
if (error != null) {
|
||||
JOptionPane.showMessageDialog(this, error, "Transfer Error", JOptionPane.ERROR_MESSAGE);
|
||||
} else {
|
||||
// Success, close dialog (progress will be shown separately)
|
||||
dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,12 @@ public class ConnectDialog extends JDialog {
|
||||
private JTextField hostField;
|
||||
private JTextField portField;
|
||||
private JComboBox<TerminalModel> modelCombo;
|
||||
private JLabel dynamicDimLabel;
|
||||
private JPanel dynamicDimPanel;
|
||||
private JSpinner dynamicRowsSpinner;
|
||||
private JSpinner dynamicColsSpinner;
|
||||
private JComboBox<haus.nightmare.lib3270j.graphics.GraphicsMode> graphicsCombo;
|
||||
private JComboBox<String> codePageCombo;
|
||||
private JTextField luField;
|
||||
private JCheckBox tlsCheckBox;
|
||||
private JCheckBox verifyCertCheckBox;
|
||||
@@ -33,24 +38,21 @@ public class ConnectDialog extends JDialog {
|
||||
private void buildUI() {
|
||||
JPanel mainPanel = new JPanel(new GridBagLayout());
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(16, 16, 16, 16));
|
||||
mainPanel.setBackground(new Color(30, 30, 30));
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(4, 4, 4, 4);
|
||||
gbc.insets = new Insets(5, 6, 5, 6);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
Font labelFont = new Font(Font.SANS_SERIF, Font.PLAIN, 14);
|
||||
Color fg = new Color(200, 200, 200);
|
||||
Font labelFont = new Font(Font.SANS_SERIF, Font.PLAIN, 13);
|
||||
|
||||
// Host
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
JLabel hostLabel = new JLabel("Host:");
|
||||
hostLabel.setForeground(fg);
|
||||
hostLabel.setFont(labelFont);
|
||||
mainPanel.add(hostLabel, gbc);
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
hostField = createDarkField(20);
|
||||
hostField = createField(20);
|
||||
mainPanel.add(hostField, gbc);
|
||||
|
||||
// Port
|
||||
@@ -58,12 +60,11 @@ public class ConnectDialog extends JDialog {
|
||||
gbc.gridy = 1;
|
||||
gbc.weightx = 0;
|
||||
JLabel portLabel = new JLabel("Port:");
|
||||
portLabel.setForeground(fg);
|
||||
portLabel.setFont(labelFont);
|
||||
mainPanel.add(portLabel, gbc);
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
portField = createDarkField(6);
|
||||
portField = createField(6);
|
||||
portField.setText("23");
|
||||
mainPanel.add(portField, gbc);
|
||||
|
||||
@@ -72,57 +73,148 @@ public class ConnectDialog extends JDialog {
|
||||
gbc.gridy = 2;
|
||||
gbc.weightx = 0;
|
||||
JLabel modelLabel = new JLabel("Model:");
|
||||
modelLabel.setForeground(fg);
|
||||
modelLabel.setFont(labelFont);
|
||||
mainPanel.add(modelLabel, gbc);
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
modelCombo = new JComboBox<>(TerminalModel.values());
|
||||
modelCombo.setSelectedItem(TerminalModel.IBM_3279_4);
|
||||
modelCombo.setBackground(new Color(45, 45, 45));
|
||||
modelCombo.setForeground(fg);
|
||||
modelCombo.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 13));
|
||||
ThemeManager.styleComboBox(modelCombo);
|
||||
mainPanel.add(modelCombo, gbc);
|
||||
|
||||
// LU Name
|
||||
// Dynamic Dimensions (shown when IBM-DYNAMIC is selected)
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 3;
|
||||
gbc.weightx = 0;
|
||||
dynamicDimLabel = new JLabel("Screen Size:");
|
||||
dynamicDimLabel.setFont(labelFont);
|
||||
mainPanel.add(dynamicDimLabel, gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
dynamicDimPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 6, 0));
|
||||
dynamicDimPanel.setOpaque(false);
|
||||
|
||||
JLabel rowsLabel = new JLabel("Rows:");
|
||||
rowsLabel.setFont(labelFont);
|
||||
dynamicRowsSpinner = new JSpinner(new SpinnerNumberModel(haus.nightmare.j3270.config.Settings.getDynamicRows(), 24, 255, 1));
|
||||
ThemeManager.styleSpinner(dynamicRowsSpinner);
|
||||
|
||||
JLabel colsLabel = new JLabel("Cols:");
|
||||
colsLabel.setFont(labelFont);
|
||||
dynamicColsSpinner = new JSpinner(new SpinnerNumberModel(haus.nightmare.j3270.config.Settings.getDynamicCols(), 80, 255, 1));
|
||||
ThemeManager.styleSpinner(dynamicColsSpinner);
|
||||
|
||||
dynamicDimPanel.add(rowsLabel);
|
||||
dynamicDimPanel.add(dynamicRowsSpinner);
|
||||
dynamicDimPanel.add(colsLabel);
|
||||
dynamicDimPanel.add(dynamicColsSpinner);
|
||||
mainPanel.add(dynamicDimPanel, gbc);
|
||||
|
||||
Runnable updateDynamicVisibility = () -> {
|
||||
TerminalModel m = (TerminalModel) modelCombo.getSelectedItem();
|
||||
boolean isDyn = (m != null && m.isDynamic());
|
||||
dynamicDimLabel.setVisible(isDyn);
|
||||
dynamicDimPanel.setVisible(isDyn);
|
||||
pack();
|
||||
};
|
||||
modelCombo.addActionListener(e -> updateDynamicVisibility.run());
|
||||
updateDynamicVisibility.run();
|
||||
|
||||
// LU Name
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 4;
|
||||
gbc.weightx = 0;
|
||||
JLabel luLabel = new JLabel("LU Name:");
|
||||
luLabel.setForeground(fg);
|
||||
luLabel.setFont(labelFont);
|
||||
mainPanel.add(luLabel, gbc);
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
luField = createDarkField(12);
|
||||
luField = createField(12);
|
||||
mainPanel.add(luField, gbc);
|
||||
|
||||
// Graphics Mode
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 4;
|
||||
gbc.gridy = 5;
|
||||
gbc.weightx = 0;
|
||||
JLabel graphicsLabel = new JLabel("Graphics:");
|
||||
graphicsLabel.setForeground(fg);
|
||||
graphicsLabel.setFont(labelFont);
|
||||
mainPanel.add(graphicsLabel, gbc);
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
graphicsCombo = new JComboBox<>(haus.nightmare.lib3270j.graphics.GraphicsMode.values());
|
||||
graphicsCombo.setSelectedItem(haus.nightmare.j3270.config.Settings.getGraphicsMode());
|
||||
graphicsCombo.setBackground(new Color(45, 45, 45));
|
||||
graphicsCombo.setForeground(fg);
|
||||
graphicsCombo.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 13));
|
||||
ThemeManager.styleComboBox(graphicsCombo);
|
||||
mainPanel.add(graphicsCombo, gbc);
|
||||
|
||||
// Code Page
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 6;
|
||||
gbc.weightx = 0;
|
||||
JLabel cpLabel = new JLabel("Code Page:");
|
||||
cpLabel.setFont(labelFont);
|
||||
mainPanel.add(cpLabel, gbc);
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
String[] commonCodePages = {
|
||||
"037 - US / Canada / Brazil",
|
||||
"1047 - IBM Open Systems / z/OS Unix",
|
||||
"500 - International Latin-1",
|
||||
"273 - Germany / Austria",
|
||||
"277 - Denmark / Norway",
|
||||
"278 - Sweden / Finland",
|
||||
"280 - Italy",
|
||||
"284 - Spain / Latin America",
|
||||
"285 - United Kingdom",
|
||||
"297 - France",
|
||||
"870 - Eastern Europe / Latin-2",
|
||||
"871 - Iceland",
|
||||
"875 - Greece (Greek)",
|
||||
"1026 - Turkey (Turkish Latin-5)",
|
||||
"1155 - Turkey (Turkish Latin-5 Euro \u20AC)",
|
||||
"905 - Turkey (Turkish Latin-3)",
|
||||
"420 - Arabic Bilingual",
|
||||
"424 - Hebrew (with Lowercase)",
|
||||
"803 - Hebrew Old / Standard",
|
||||
"838 - Thai",
|
||||
"1160 - Thai (Euro \u20AC)",
|
||||
"1025 - Cyrillic Multilingual",
|
||||
"1123 - Cyrillic Ukraine",
|
||||
"1154 - Cyrillic Multilingual (Euro \u20AC)",
|
||||
"880 - Cyrillic Russian",
|
||||
"1140 - US / Canada (Euro \u20AC)",
|
||||
"1141 - Germany / Austria (Euro \u20AC)",
|
||||
"1148 - International (Euro \u20AC)",
|
||||
"930 - Japanese Katakana Mixed DBCS",
|
||||
"939 - Japanese Latin Mixed DBCS",
|
||||
"935 - Simplified Chinese Mixed DBCS",
|
||||
"1388 - Simplified Chinese Extended Mixed DBCS",
|
||||
"937 - Traditional Chinese Mixed DBCS",
|
||||
"1371 - Traditional Chinese Extended Mixed DBCS",
|
||||
"933 - Korean Mixed DBCS"
|
||||
};
|
||||
codePageCombo = new JComboBox<>(commonCodePages);
|
||||
codePageCombo.setEditable(true);
|
||||
String currentCp = haus.nightmare.j3270.config.Settings.getCodePage();
|
||||
for (String item : commonCodePages) {
|
||||
if (item.startsWith(currentCp + " ") || item.equals(currentCp)) {
|
||||
codePageCombo.setSelectedItem(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
codePageCombo.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 13));
|
||||
ThemeManager.styleComboBox(codePageCombo);
|
||||
mainPanel.add(codePageCombo, gbc);
|
||||
|
||||
// TLS / SSL Checkbox
|
||||
gbc.gridx = 1;
|
||||
gbc.gridy = 5;
|
||||
gbc.gridy = 7;
|
||||
gbc.weightx = 1.0;
|
||||
tlsCheckBox = new JCheckBox("Enable TLS/SSL");
|
||||
tlsCheckBox.setBackground(new Color(30, 30, 30));
|
||||
tlsCheckBox.setForeground(fg);
|
||||
ThemeManager.styleCheckBox(tlsCheckBox);
|
||||
tlsCheckBox.setFont(labelFont);
|
||||
tlsCheckBox.setFocusPainted(false);
|
||||
tlsCheckBox.addActionListener(e -> {
|
||||
boolean isTls = tlsCheckBox.isSelected();
|
||||
verifyCertCheckBox.setEnabled(isTls);
|
||||
@@ -137,40 +229,33 @@ public class ConnectDialog extends JDialog {
|
||||
|
||||
// Verify Certificate Checkbox
|
||||
gbc.gridx = 1;
|
||||
gbc.gridy = 6;
|
||||
gbc.gridy = 8;
|
||||
verifyCertCheckBox = new JCheckBox("Verify Server Certificate");
|
||||
verifyCertCheckBox.setBackground(new Color(30, 30, 30));
|
||||
verifyCertCheckBox.setForeground(new Color(160, 160, 160));
|
||||
ThemeManager.styleCheckBox(verifyCertCheckBox);
|
||||
verifyCertCheckBox.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 13));
|
||||
verifyCertCheckBox.setSelected(true);
|
||||
verifyCertCheckBox.setEnabled(false);
|
||||
verifyCertCheckBox.setFocusPainted(false);
|
||||
mainPanel.add(verifyCertCheckBox, gbc);
|
||||
|
||||
// TN3270E Checkbox
|
||||
gbc.gridx = 1;
|
||||
gbc.gridy = 7;
|
||||
gbc.gridy = 9;
|
||||
tn3270eCheckBox = new JCheckBox("Enable TN3270E (Extended 3270)");
|
||||
tn3270eCheckBox.setBackground(new Color(30, 30, 30));
|
||||
tn3270eCheckBox.setForeground(fg);
|
||||
ThemeManager.styleCheckBox(tn3270eCheckBox);
|
||||
tn3270eCheckBox.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 13));
|
||||
tn3270eCheckBox.setSelected(haus.nightmare.j3270.config.Settings.getAutoConnectTn3270e());
|
||||
tn3270eCheckBox.setFocusPainted(false);
|
||||
mainPanel.add(tn3270eCheckBox, gbc);
|
||||
|
||||
// Buttons
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||
buttonPanel.setBackground(new Color(30, 30, 30));
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 8, 4));
|
||||
|
||||
JButton connectBtn = new JButton("Connect");
|
||||
connectBtn.setBackground(new Color(50, 120, 50));
|
||||
connectBtn.setForeground(Color.WHITE);
|
||||
ThemeManager.styleButton(connectBtn, ThemeManager.ButtonVariant.PRIMARY);
|
||||
connectBtn.setFont(labelFont);
|
||||
connectBtn.addActionListener(e -> onConnect());
|
||||
|
||||
JButton cancelBtn = new JButton("Cancel");
|
||||
cancelBtn.setBackground(new Color(60, 60, 60));
|
||||
cancelBtn.setForeground(fg);
|
||||
ThemeManager.styleButton(cancelBtn, ThemeManager.ButtonVariant.CANCEL);
|
||||
cancelBtn.setFont(labelFont);
|
||||
cancelBtn.addActionListener(e -> {
|
||||
confirmed = false;
|
||||
@@ -181,25 +266,21 @@ public class ConnectDialog extends JDialog {
|
||||
buttonPanel.add(connectBtn);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 8;
|
||||
gbc.gridy = 10;
|
||||
gbc.gridwidth = 2;
|
||||
mainPanel.add(buttonPanel, gbc);
|
||||
|
||||
setContentPane(mainPanel);
|
||||
ThemeManager.applyThemeToWindow(this);
|
||||
|
||||
// Enter key triggers connect
|
||||
getRootPane().setDefaultButton(connectBtn);
|
||||
}
|
||||
|
||||
private JTextField createDarkField(int cols) {
|
||||
private JTextField createField(int cols) {
|
||||
JTextField field = new JTextField(cols);
|
||||
field.setBackground(new Color(45, 45, 45));
|
||||
field.setForeground(new Color(200, 200, 200));
|
||||
field.setCaretColor(new Color(200, 200, 200));
|
||||
field.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 14));
|
||||
field.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createLineBorder(new Color(60, 60, 60)),
|
||||
BorderFactory.createEmptyBorder(4, 6, 4, 6)));
|
||||
ThemeManager.styleTextField(field);
|
||||
return field;
|
||||
}
|
||||
|
||||
@@ -218,12 +299,32 @@ public class ConnectDialog extends JDialog {
|
||||
return;
|
||||
}
|
||||
|
||||
result = new ConnectionConfig(host, port, (TerminalModel) modelCombo.getSelectedItem());
|
||||
TerminalModel selectedModel = (TerminalModel) modelCombo.getSelectedItem();
|
||||
result = new ConnectionConfig(host, port, selectedModel);
|
||||
if (selectedModel != null && selectedModel.isDynamic()) {
|
||||
int dRows = (Integer) dynamicRowsSpinner.getValue();
|
||||
int dCols = (Integer) dynamicColsSpinner.getValue();
|
||||
result.setDynamicDimensions(dRows, dCols);
|
||||
haus.nightmare.j3270.config.Settings.setDynamicRows(dRows);
|
||||
haus.nightmare.j3270.config.Settings.setDynamicCols(dCols);
|
||||
}
|
||||
String lu = luField.getText().trim();
|
||||
if (!lu.isEmpty()) {
|
||||
result.setLuName(lu);
|
||||
}
|
||||
result.setGraphicsMode((haus.nightmare.lib3270j.graphics.GraphicsMode) graphicsCombo.getSelectedItem());
|
||||
|
||||
Object cpSelection = codePageCombo.getSelectedItem();
|
||||
if (cpSelection != null) {
|
||||
String cpStr = cpSelection.toString().trim();
|
||||
int dash = cpStr.indexOf(" -");
|
||||
if (dash > 0) {
|
||||
cpStr = cpStr.substring(0, dash).trim();
|
||||
}
|
||||
result.setCodePage(cpStr);
|
||||
haus.nightmare.j3270.config.Settings.setCodePage(cpStr);
|
||||
}
|
||||
|
||||
result.setUseTls(tlsCheckBox.isSelected());
|
||||
result.setTlsVerifyCert(verifyCertCheckBox.isSelected());
|
||||
result.setTn3270eEnabled(tn3270eCheckBox.isSelected());
|
||||
@@ -260,4 +361,17 @@ public class ConnectDialog extends JDialog {
|
||||
public void setInitialTn3270e(boolean tn3270e) {
|
||||
tn3270eCheckBox.setSelected(tn3270e);
|
||||
}
|
||||
|
||||
public void setInitialCodePage(String cp) {
|
||||
if (cp != null && codePageCombo != null) {
|
||||
for (int i = 0; i < codePageCombo.getItemCount(); i++) {
|
||||
String item = codePageCombo.getItemAt(i);
|
||||
if (item.startsWith(cp + " ") || item.equals(cp)) {
|
||||
codePageCombo.setSelectedIndex(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
codePageCombo.setSelectedItem(cp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
package haus.nightmare.j3270.ui;
|
||||
|
||||
import haus.nightmare.lib3270j.Telnet3270Client;
|
||||
import haus.nightmare.lib3270j.ecl.ECLField;
|
||||
import haus.nightmare.lib3270j.ecl.ECLFieldList;
|
||||
import haus.nightmare.lib3270j.listener.ScreenUpdateListener;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.LineBorder;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import javax.swing.table.JTableHeader;
|
||||
import java.awt.*;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Live Field Inspector dialog displaying all fields in the 3270 Presentation Space (ECLFieldList).
|
||||
*/
|
||||
public class FieldInspectorDialog extends JDialog implements ScreenUpdateListener {
|
||||
|
||||
private final Telnet3270Client client;
|
||||
private final TerminalPanel terminalPanel;
|
||||
|
||||
private DefaultTableModel tableModel;
|
||||
private JTable table;
|
||||
private JLabel countLabel;
|
||||
|
||||
public FieldInspectorDialog(Frame parent, Telnet3270Client client, TerminalPanel terminalPanel) {
|
||||
super(parent, "3270 Presentation Space Field Inspector", false);
|
||||
this.client = client;
|
||||
this.terminalPanel = terminalPanel;
|
||||
|
||||
buildUI();
|
||||
setSize(850, 420);
|
||||
setLocationRelativeTo(parent);
|
||||
|
||||
if (client != null) {
|
||||
client.addScreenUpdateListener(this);
|
||||
refreshFields();
|
||||
}
|
||||
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
if (client != null) {
|
||||
// Screen update listener removed on close
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void buildUI() {
|
||||
JPanel mainPanel = new JPanel(new BorderLayout(8, 8));
|
||||
mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||
|
||||
// Top info bar
|
||||
JPanel topPanel = new JPanel(new BorderLayout());
|
||||
topPanel.setOpaque(false);
|
||||
countLabel = new JLabel("0 fields detected on screen");
|
||||
countLabel.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 13));
|
||||
topPanel.add(countLabel, BorderLayout.WEST);
|
||||
|
||||
JButton refreshBtn = new JButton("Refresh");
|
||||
ThemeManager.styleButton(refreshBtn, ThemeManager.ButtonVariant.DEFAULT);
|
||||
refreshBtn.addActionListener(e -> refreshFields());
|
||||
topPanel.add(refreshBtn, BorderLayout.EAST);
|
||||
mainPanel.add(topPanel, BorderLayout.NORTH);
|
||||
|
||||
// Table
|
||||
String[] columns = {
|
||||
"#", "Pos", "Start", "End", "Len", "Prot", "MDT", "Num", "Hi-Int", "Hidden", "Pen-Sel", "Content Text"
|
||||
};
|
||||
|
||||
tableModel = new DefaultTableModel(columns, 0) {
|
||||
@Override
|
||||
public boolean isCellEditable(int row, int column) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
table = new JTable(tableModel);
|
||||
ThemeManager.styleTable(table);
|
||||
table.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
|
||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
table.setRowHeight(20);
|
||||
|
||||
JTableHeader header = table.getTableHeader();
|
||||
header.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 12));
|
||||
|
||||
// Column widths
|
||||
int[] widths = {35, 45, 60, 60, 40, 45, 45, 45, 50, 55, 60, 260};
|
||||
for (int i = 0; i < widths.length && i < table.getColumnCount(); i++) {
|
||||
table.getColumnModel().getColumn(i).setPreferredWidth(widths[i]);
|
||||
}
|
||||
|
||||
// Center align boolean columns
|
||||
DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
|
||||
centerRenderer.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
for (int i = 5; i <= 10; i++) {
|
||||
table.getColumnModel().getColumn(i).setCellRenderer(centerRenderer);
|
||||
}
|
||||
|
||||
table.getSelectionModel().addListSelectionListener(e -> {
|
||||
if (!e.getValueIsAdjusting() && table.getSelectedRow() >= 0) {
|
||||
selectFieldOnTerminal(table.getSelectedRow());
|
||||
}
|
||||
});
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(table);
|
||||
ThemeManager.styleScrollPane(scrollPane);
|
||||
mainPanel.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
// Bottom panel
|
||||
JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 6, 0));
|
||||
bottomPanel.setOpaque(false);
|
||||
|
||||
JButton jumpBtn = new JButton("Jump to Selected Field");
|
||||
ThemeManager.styleButton(jumpBtn, ThemeManager.ButtonVariant.PRIMARY);
|
||||
jumpBtn.addActionListener(e -> {
|
||||
int row = table.getSelectedRow();
|
||||
if (row >= 0) {
|
||||
selectFieldOnTerminal(row);
|
||||
}
|
||||
});
|
||||
|
||||
JButton closeBtn = new JButton("Close");
|
||||
ThemeManager.styleButton(closeBtn, ThemeManager.ButtonVariant.CANCEL);
|
||||
closeBtn.addActionListener(e -> dispose());
|
||||
|
||||
bottomPanel.add(jumpBtn);
|
||||
bottomPanel.add(closeBtn);
|
||||
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
|
||||
|
||||
setContentPane(mainPanel);
|
||||
ThemeManager.applyThemeToWindow(this);
|
||||
}
|
||||
|
||||
public synchronized void refreshFields() {
|
||||
if (client == null || !client.getConnectionState().isFullSession()) {
|
||||
tableModel.setRowCount(0);
|
||||
countLabel.setText("Not connected or screen unformatted");
|
||||
return;
|
||||
}
|
||||
|
||||
ECLFieldList fieldList = client.getFieldList();
|
||||
List<ECLField> fields = fieldList.getFields();
|
||||
|
||||
int selectedRow = table.getSelectedRow();
|
||||
tableModel.setRowCount(0);
|
||||
|
||||
int cols = client.getScreenBuffer().getDisplayCols();
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
ECLField f = fields.get(i);
|
||||
int startRow = f.getStartRow() + 1;
|
||||
int startCol = f.getStartCol() + 1;
|
||||
int endRow = f.getEndRow() + 1;
|
||||
int endCol = f.getEndCol() + 1;
|
||||
|
||||
String text = f.getText();
|
||||
if (f.isHidden()) {
|
||||
text = "••••••••";
|
||||
}
|
||||
|
||||
tableModel.addRow(new Object[]{
|
||||
(i + 1),
|
||||
f.getStart(),
|
||||
String.format("%02d/%02d", startRow, startCol),
|
||||
String.format("%02d/%02d", endRow, endCol),
|
||||
f.getLength(),
|
||||
f.isProtected() ? "YES" : "-",
|
||||
f.isModified() ? "YES" : "-",
|
||||
f.isNumeric() ? "YES" : "-",
|
||||
f.isHighIntensity() ? "YES" : "-",
|
||||
f.isHidden() ? "YES" : "-",
|
||||
f.isPenSelectable() ? "YES" : "-",
|
||||
text
|
||||
});
|
||||
}
|
||||
|
||||
countLabel.setText(fields.size() + " fields detected on screen (" + client.getScreenBuffer().getDisplayRows() + "x" + cols + ")");
|
||||
if (selectedRow >= 0 && selectedRow < tableModel.getRowCount()) {
|
||||
table.setRowSelectionInterval(selectedRow, selectedRow);
|
||||
}
|
||||
}
|
||||
|
||||
private void selectFieldOnTerminal(int rowIndex) {
|
||||
if (client == null || rowIndex < 0) return;
|
||||
List<ECLField> fields = client.getFieldList().getFields();
|
||||
if (rowIndex < fields.size()) {
|
||||
ECLField f = fields.get(rowIndex);
|
||||
client.getScreenBuffer().setCursorAddress(f.getDataStart());
|
||||
if (terminalPanel != null) {
|
||||
terminalPanel.setSelectionRange(f.getDataStart(), f.getEnd());
|
||||
terminalPanel.repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== ScreenUpdateListener ==========
|
||||
|
||||
@Override
|
||||
public void onScreenUpdated() {
|
||||
SwingUtilities.invokeLater(this::refreshFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScreenSizeChanged(int rows, int cols) {
|
||||
SwingUtilities.invokeLater(this::refreshFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSoundAlarm() {}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package haus.nightmare.j3270.ui;
|
||||
|
||||
import haus.nightmare.lib3270j.Telnet3270Client;
|
||||
import haus.nightmare.lib3270j.ecl.ECLConstants;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
/**
|
||||
* Find dialog for searching text in the 3270 Presentation Space using ECLPS.
|
||||
*/
|
||||
public class FindDialog extends JDialog {
|
||||
|
||||
private final Telnet3270Client client;
|
||||
private final TerminalPanel terminalPanel;
|
||||
|
||||
private JTextField searchField;
|
||||
private JCheckBox matchCaseCheck;
|
||||
private JRadioButton forwardRadio;
|
||||
private JRadioButton backwardRadio;
|
||||
private JLabel statusLabel;
|
||||
|
||||
private static String lastSearchText = "";
|
||||
private static boolean lastMatchCase = false;
|
||||
private static boolean lastForward = true;
|
||||
|
||||
public FindDialog(Frame parent, Telnet3270Client client, TerminalPanel terminalPanel) {
|
||||
super(parent, "Find on Screen", false);
|
||||
this.client = client;
|
||||
this.terminalPanel = terminalPanel;
|
||||
|
||||
buildUI();
|
||||
pack();
|
||||
setLocationRelativeTo(parent);
|
||||
setResizable(false);
|
||||
}
|
||||
|
||||
private void buildUI() {
|
||||
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
|
||||
mainPanel.setBorder(new EmptyBorder(12, 14, 12, 14));
|
||||
|
||||
// Form
|
||||
JPanel formPanel = new JPanel(new GridBagLayout());
|
||||
formPanel.setOpaque(false);
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(4, 4, 4, 4);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
Font labelFont = new Font(Font.SANS_SERIF, Font.PLAIN, 13);
|
||||
|
||||
// Search text
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
JLabel findLabel = new JLabel("Find what:");
|
||||
findLabel.setFont(labelFont);
|
||||
formPanel.add(findLabel, gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
searchField = new JTextField(20);
|
||||
searchField.setText(lastSearchText);
|
||||
searchField.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 13));
|
||||
ThemeManager.styleTextField(searchField);
|
||||
formPanel.add(searchField, gbc);
|
||||
|
||||
// Options
|
||||
gbc.gridx = 1;
|
||||
gbc.gridy = 1;
|
||||
matchCaseCheck = new JCheckBox("Match case", lastMatchCase);
|
||||
ThemeManager.styleCheckBox(matchCaseCheck);
|
||||
matchCaseCheck.setFont(labelFont);
|
||||
formPanel.add(matchCaseCheck, gbc);
|
||||
|
||||
// Direction
|
||||
gbc.gridx = 1;
|
||||
gbc.gridy = 2;
|
||||
JPanel dirPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
|
||||
dirPanel.setOpaque(false);
|
||||
JLabel dirLabel = new JLabel("Direction: ");
|
||||
dirLabel.setFont(labelFont);
|
||||
forwardRadio = new JRadioButton("Down", lastForward);
|
||||
backwardRadio = new JRadioButton("Up", !lastForward);
|
||||
ThemeManager.styleRadioButton(forwardRadio);
|
||||
ThemeManager.styleRadioButton(backwardRadio);
|
||||
|
||||
ButtonGroup bg = new ButtonGroup();
|
||||
bg.add(forwardRadio);
|
||||
bg.add(backwardRadio);
|
||||
dirPanel.add(dirLabel);
|
||||
dirPanel.add(forwardRadio);
|
||||
dirPanel.add(Box.createHorizontalStrut(10));
|
||||
dirPanel.add(backwardRadio);
|
||||
formPanel.add(dirPanel, gbc);
|
||||
|
||||
// Status
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 3;
|
||||
gbc.gridwidth = 2;
|
||||
statusLabel = new JLabel(" ");
|
||||
statusLabel.setForeground(ThemeManager.getOiaFgAlert());
|
||||
statusLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 12));
|
||||
formPanel.add(statusLabel, gbc);
|
||||
|
||||
mainPanel.add(formPanel, BorderLayout.CENTER);
|
||||
|
||||
// Buttons
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 6, 0));
|
||||
buttonPanel.setOpaque(false);
|
||||
|
||||
JButton findNextBtn = new JButton("Find Next");
|
||||
ThemeManager.styleButton(findNextBtn, ThemeManager.ButtonVariant.PRIMARY);
|
||||
findNextBtn.setFont(labelFont);
|
||||
findNextBtn.addActionListener(e -> findNext());
|
||||
|
||||
JButton closeBtn = new JButton("Close");
|
||||
ThemeManager.styleButton(closeBtn, ThemeManager.ButtonVariant.CANCEL);
|
||||
closeBtn.setFont(labelFont);
|
||||
closeBtn.addActionListener(e -> dispose());
|
||||
|
||||
buttonPanel.add(findNextBtn);
|
||||
buttonPanel.add(closeBtn);
|
||||
|
||||
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
|
||||
|
||||
setContentPane(mainPanel);
|
||||
ThemeManager.applyThemeToWindow(this);
|
||||
getRootPane().setDefaultButton(findNextBtn);
|
||||
|
||||
searchField.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||
findNext();
|
||||
} else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
|
||||
dispose();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void findNext() {
|
||||
find(forwardRadio.isSelected());
|
||||
}
|
||||
|
||||
public void findPrevious() {
|
||||
find(!forwardRadio.isSelected());
|
||||
}
|
||||
|
||||
private void find(boolean forward) {
|
||||
String target = searchField.getText();
|
||||
if (target == null || target.isEmpty()) {
|
||||
statusLabel.setText("Please enter search text.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client == null || !client.getConnectionState().isFullSession()) {
|
||||
statusLabel.setText("Terminal is not connected.");
|
||||
return;
|
||||
}
|
||||
|
||||
lastSearchText = target;
|
||||
lastMatchCase = matchCaseCheck.isSelected();
|
||||
lastForward = forwardRadio.isSelected();
|
||||
|
||||
ScreenBuffer sb = client.getScreenBuffer();
|
||||
int curAddr = sb.getCursorAddress();
|
||||
int cols = sb.getDisplayCols();
|
||||
int curRow = curAddr / cols;
|
||||
int curCol = curAddr % cols;
|
||||
|
||||
// Offset search start position past current character so repeat search moves to next occurrence
|
||||
int startRow = curRow;
|
||||
int startCol = curCol;
|
||||
if (forward) {
|
||||
startCol = (curCol + 1) % cols;
|
||||
if (startCol == 0) startRow = (curRow + 1) % sb.getDisplayRows();
|
||||
} else {
|
||||
startCol = (curCol - 1 + cols) % cols;
|
||||
if (startCol == cols - 1) startRow = (curRow - 1 + sb.getDisplayRows()) % sb.getDisplayRows();
|
||||
}
|
||||
|
||||
int dir = forward ? ECLConstants.SEARCH_FORWARD : ECLConstants.SEARCH_BACKWARD;
|
||||
int foundPos = client.getPS().searchString(target, startRow, startCol, dir, !matchCaseCheck.isSelected());
|
||||
|
||||
if (foundPos >= 0) {
|
||||
statusLabel.setText("Found at position " + (foundPos + 1) + " (" + (foundPos / cols + 1) + "/" + (foundPos % cols + 1) + ")");
|
||||
statusLabel.setForeground(new Color(100, 255, 100));
|
||||
sb.setCursorAddress(foundPos);
|
||||
if (terminalPanel != null) {
|
||||
terminalPanel.setSearchHighlight(foundPos, target.length());
|
||||
terminalPanel.repaint();
|
||||
}
|
||||
} else {
|
||||
statusLabel.setText("Text not found: \"" + target + "\"");
|
||||
statusLabel.setForeground(new Color(255, 120, 120));
|
||||
if (terminalPanel != null) {
|
||||
terminalPanel.clearSearchHighlight();
|
||||
terminalPanel.repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String getLastSearchText() {
|
||||
return lastSearchText;
|
||||
}
|
||||
|
||||
public static boolean getLastMatchCase() {
|
||||
return lastMatchCase;
|
||||
}
|
||||
|
||||
public static boolean getLastForward() {
|
||||
return lastForward;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
package haus.nightmare.j3270.ui;
|
||||
|
||||
import haus.nightmare.lib3270j.Telnet3270Client;
|
||||
import haus.nightmare.lib3270j.ft.FTConfig;
|
||||
import haus.nightmare.lib3270j.ft.dir.CMSDirectoryEntry;
|
||||
import haus.nightmare.lib3270j.ft.dir.HostDirectoryEntry;
|
||||
import haus.nightmare.lib3270j.ft.dir.TSODirectoryEntry;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.LineBorder;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import javax.swing.table.JTableHeader;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Host Directory and Dataset Catalog browser dialog for TSO and VM/CMS environments.
|
||||
*/
|
||||
public class HostDirectoryDialog extends JDialog {
|
||||
|
||||
private final Telnet3270Client client;
|
||||
private final FTConfig.HostType initialHostType;
|
||||
|
||||
private JComboBox<FTConfig.HostType> hostTypeCombo;
|
||||
private JTextField queryField;
|
||||
private JTable table;
|
||||
private DefaultTableModel tableModel;
|
||||
private JLabel statusLabel;
|
||||
|
||||
private String selectedHostFile = null;
|
||||
private boolean confirmed = false;
|
||||
|
||||
private final List<HostDirectoryEntry> currentEntries = new ArrayList<>();
|
||||
|
||||
public HostDirectoryDialog(Dialog parent, Telnet3270Client client, FTConfig.HostType hostType, String initialQuery) {
|
||||
super(parent, "Host Catalog & Directory Browser", true);
|
||||
this.client = client;
|
||||
this.initialHostType = hostType;
|
||||
|
||||
buildUI(initialQuery);
|
||||
setSize(700, 420);
|
||||
setLocationRelativeTo(parent);
|
||||
}
|
||||
|
||||
private void buildUI(String initialQuery) {
|
||||
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
|
||||
mainPanel.setBorder(new EmptyBorder(12, 12, 12, 12));
|
||||
|
||||
Font font = new Font(Font.SANS_SERIF, Font.PLAIN, 13);
|
||||
|
||||
// Top Query bar
|
||||
JPanel topPanel = new JPanel(new GridBagLayout());
|
||||
topPanel.setOpaque(false);
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(3, 4, 3, 4);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
JLabel envLabel = new JLabel("System:");
|
||||
envLabel.setFont(font);
|
||||
topPanel.add(envLabel, gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
hostTypeCombo = new JComboBox<>(new FTConfig.HostType[]{FTConfig.HostType.TSO, FTConfig.HostType.CMS});
|
||||
if (initialHostType == FTConfig.HostType.CMS) hostTypeCombo.setSelectedItem(FTConfig.HostType.CMS);
|
||||
else hostTypeCombo.setSelectedItem(FTConfig.HostType.TSO);
|
||||
ThemeManager.styleComboBox(hostTypeCombo);
|
||||
topPanel.add(hostTypeCombo, gbc);
|
||||
|
||||
gbc.gridx = 2;
|
||||
JLabel queryLabel = new JLabel("Query Pattern / Text:");
|
||||
queryLabel.setFont(font);
|
||||
topPanel.add(queryLabel, gbc);
|
||||
|
||||
gbc.gridx = 3;
|
||||
gbc.weightx = 1.0;
|
||||
queryField = new JTextField(initialQuery != null ? initialQuery : "", 16);
|
||||
queryField.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 13));
|
||||
ThemeManager.styleTextField(queryField);
|
||||
topPanel.add(queryField, gbc);
|
||||
|
||||
gbc.gridx = 4;
|
||||
gbc.weightx = 0;
|
||||
JButton parseBtn = new JButton("Query / Parse");
|
||||
ThemeManager.styleButton(parseBtn, ThemeManager.ButtonVariant.DEFAULT);
|
||||
parseBtn.setFont(font);
|
||||
parseBtn.addActionListener(e -> runQuery());
|
||||
topPanel.add(parseBtn, gbc);
|
||||
|
||||
mainPanel.add(topPanel, BorderLayout.NORTH);
|
||||
|
||||
// Table
|
||||
tableModel = new DefaultTableModel();
|
||||
table = new JTable(tableModel);
|
||||
ThemeManager.styleTable(table);
|
||||
table.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
|
||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
table.setRowHeight(20);
|
||||
|
||||
JTableHeader header = table.getTableHeader();
|
||||
header.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 12));
|
||||
|
||||
table.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getClickCount() == 2 && table.getSelectedRow() >= 0) {
|
||||
onConfirmSelection();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(table);
|
||||
ThemeManager.styleScrollPane(scrollPane);
|
||||
mainPanel.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
// Bottom
|
||||
JPanel bottomPanel = new JPanel(new BorderLayout(5, 5));
|
||||
bottomPanel.setOpaque(false);
|
||||
|
||||
statusLabel = new JLabel("Enter a dataset pattern or parse active screen.");
|
||||
statusLabel.setForeground(ThemeManager.getFgMuted());
|
||||
statusLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 12));
|
||||
bottomPanel.add(statusLabel, BorderLayout.WEST);
|
||||
|
||||
JPanel btnPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 6, 0));
|
||||
btnPanel.setOpaque(false);
|
||||
|
||||
JButton pasteScreenBtn = new JButton("Parse Current Screen");
|
||||
ThemeManager.styleButton(pasteScreenBtn, ThemeManager.ButtonVariant.DEFAULT);
|
||||
pasteScreenBtn.setFont(font);
|
||||
pasteScreenBtn.addActionListener(e -> parseCurrentScreen());
|
||||
|
||||
JButton selectBtn = new JButton("Select Dataset");
|
||||
ThemeManager.styleButton(selectBtn, ThemeManager.ButtonVariant.PRIMARY);
|
||||
selectBtn.setFont(font);
|
||||
selectBtn.addActionListener(e -> onConfirmSelection());
|
||||
|
||||
JButton cancelBtn = new JButton("Cancel");
|
||||
ThemeManager.styleButton(cancelBtn, ThemeManager.ButtonVariant.CANCEL);
|
||||
cancelBtn.setFont(font);
|
||||
cancelBtn.addActionListener(e -> {
|
||||
confirmed = false;
|
||||
dispose();
|
||||
});
|
||||
|
||||
btnPanel.add(pasteScreenBtn);
|
||||
btnPanel.add(Box.createHorizontalStrut(10));
|
||||
btnPanel.add(selectBtn);
|
||||
btnPanel.add(cancelBtn);
|
||||
|
||||
bottomPanel.add(btnPanel, BorderLayout.EAST);
|
||||
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
|
||||
|
||||
setContentPane(mainPanel);
|
||||
ThemeManager.applyThemeToWindow(this);
|
||||
|
||||
// Initial setup
|
||||
setupTableColumns();
|
||||
if (initialQuery != null && !initialQuery.isEmpty()) {
|
||||
runQuery();
|
||||
} else {
|
||||
parseCurrentScreen();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupTableColumns() {
|
||||
FTConfig.HostType hostType = (FTConfig.HostType) hostTypeCombo.getSelectedItem();
|
||||
tableModel.setRowCount(0);
|
||||
if (hostType == FTConfig.HostType.CMS) {
|
||||
tableModel.setColumnIdentifiers(new Object[]{
|
||||
"Filename", "Filetype", "Filemode", "Format", "Lrecl", "Records", "Date / Time"
|
||||
});
|
||||
} else {
|
||||
tableModel.setColumnIdentifiers(new Object[]{
|
||||
"Dataset Name", "Volume", "Recfm", "Lrecl", "Blksize", "Tracks", "Extents"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void runQuery() {
|
||||
setupTableColumns();
|
||||
currentEntries.clear();
|
||||
String query = queryField.getText().trim();
|
||||
FTConfig.HostType hostType = (FTConfig.HostType) hostTypeCombo.getSelectedItem();
|
||||
|
||||
if (client != null && client.getXfer() != null) {
|
||||
if (hostType == FTConfig.HostType.CMS) {
|
||||
List<CMSDirectoryEntry> cmsEntries = client.getXfer().getCmsDirectory(query);
|
||||
currentEntries.addAll(cmsEntries);
|
||||
for (CMSDirectoryEntry entry : cmsEntries) {
|
||||
tableModel.addRow(new Object[]{
|
||||
entry.getFilename(), entry.getFiletype(), entry.getFilemode(),
|
||||
entry.getRecfm(), entry.getLrecl(), entry.getNumRecords(), entry.getDate() + " " + entry.getTime()
|
||||
});
|
||||
}
|
||||
} else {
|
||||
List<TSODirectoryEntry> tsoEntries = client.getXfer().getTsoDirectory(query);
|
||||
currentEntries.addAll(tsoEntries);
|
||||
for (TSODirectoryEntry entry : tsoEntries) {
|
||||
tableModel.addRow(new Object[]{
|
||||
entry.getDatasetName(), entry.getVolume(), entry.getRecfm(),
|
||||
entry.getLrecl(), entry.getBlksize(), entry.getTracksAllocated(), entry.getExtents()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
statusLabel.setText(currentEntries.size() + " entries found.");
|
||||
}
|
||||
|
||||
private void parseCurrentScreen() {
|
||||
if (client == null || !client.getConnectionState().isFullSession()) {
|
||||
statusLabel.setText("No active session to parse.");
|
||||
return;
|
||||
}
|
||||
|
||||
setupTableColumns();
|
||||
currentEntries.clear();
|
||||
FTConfig.HostType hostType = (FTConfig.HostType) hostTypeCombo.getSelectedItem();
|
||||
String screenText = client.getPS().getString(0, client.getPS().getSize());
|
||||
|
||||
if (hostType == FTConfig.HostType.CMS) {
|
||||
List<CMSDirectoryEntry> cmsEntries = client.getXfer().getCmsDirectory(screenText);
|
||||
currentEntries.addAll(cmsEntries);
|
||||
for (CMSDirectoryEntry entry : cmsEntries) {
|
||||
tableModel.addRow(new Object[]{
|
||||
entry.getFilename(), entry.getFiletype(), entry.getFilemode(),
|
||||
entry.getRecfm(), entry.getLrecl(), entry.getNumRecords(), entry.getDate() + " " + entry.getTime()
|
||||
});
|
||||
}
|
||||
} else {
|
||||
List<TSODirectoryEntry> tsoEntries = client.getXfer().getTsoDirectory(screenText);
|
||||
currentEntries.addAll(tsoEntries);
|
||||
for (TSODirectoryEntry entry : tsoEntries) {
|
||||
tableModel.addRow(new Object[]{
|
||||
entry.getDatasetName(), entry.getVolume(), entry.getRecfm(),
|
||||
entry.getLrecl(), entry.getBlksize(), entry.getTracksAllocated(), entry.getExtents()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
statusLabel.setText("Parsed " + currentEntries.size() + " entries from presentation space.");
|
||||
}
|
||||
|
||||
private void onConfirmSelection() {
|
||||
int row = table.getSelectedRow();
|
||||
if (row < 0 || row >= currentEntries.size()) {
|
||||
JOptionPane.showMessageDialog(this, "Please select an entry from the table.",
|
||||
"No Selection", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
HostDirectoryEntry entry = currentEntries.get(row);
|
||||
selectedHostFile = entry.getName();
|
||||
confirmed = true;
|
||||
dispose();
|
||||
}
|
||||
|
||||
public boolean isConfirmed() {
|
||||
return confirmed;
|
||||
}
|
||||
|
||||
public String getSelectedHostFile() {
|
||||
return selectedHostFile;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
package haus.nightmare.j3270.ui;
|
||||
|
||||
import haus.nightmare.lib3270j.printer.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.LineBorder;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import java.awt.*;
|
||||
import java.awt.print.PrinterJob;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* IBM 3287 / 3286 TN3270E Printer Session Manager and Spool Viewer.
|
||||
* Integrates directly with lib3270j.printer.Telnet3270EP and PD3270.
|
||||
*/
|
||||
public class PrinterSessionDialog extends JDialog implements PrintSessionListener {
|
||||
|
||||
private final Frame parent;
|
||||
private PrinterConfig config;
|
||||
private Telnet3270EP printerSession;
|
||||
|
||||
// Configuration Fields
|
||||
private JTextField hostField;
|
||||
private JTextField portField;
|
||||
private JTextField printerLuField;
|
||||
private JTextField displayLuField;
|
||||
private JComboBox<String> codePageCombo;
|
||||
private JCheckBox tlsCheck;
|
||||
private JCheckBox verifyCertCheck;
|
||||
private JComboBox<PrinterConfig.DestinationType> destinationCombo;
|
||||
private JTextField targetField;
|
||||
|
||||
// Status Fields
|
||||
private JLabel statusLabel;
|
||||
private JLabel sessionTypeLabel;
|
||||
private JLabel pagesLabel;
|
||||
private JLabel bytesLabel;
|
||||
private JButton connectBtn;
|
||||
private JButton disconnectBtn;
|
||||
|
||||
// Spool Display
|
||||
private JTextArea spoolArea;
|
||||
|
||||
public PrinterSessionDialog(Frame parent, String defaultHost, int defaultPort, boolean defaultTls) {
|
||||
super(parent, "IBM 3287 Printer Session Manager", false);
|
||||
this.parent = parent;
|
||||
|
||||
config = new PrinterConfig(defaultHost, defaultPort);
|
||||
config.setUseTls(defaultTls);
|
||||
config.setDestinationType(PrinterConfig.DestinationType.MEMORY);
|
||||
|
||||
buildUI();
|
||||
setSize(780, 560);
|
||||
setLocationRelativeTo(parent);
|
||||
}
|
||||
|
||||
private void buildUI() {
|
||||
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
|
||||
mainPanel.setBorder(new EmptyBorder(12, 14, 12, 14));
|
||||
|
||||
// Top: Configuration Panel
|
||||
JPanel topPanel = new JPanel(new GridBagLayout());
|
||||
topPanel.setOpaque(false);
|
||||
topPanel.setBorder(ThemeManager.createTitledBorder("Printer Session Configuration"));
|
||||
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.insets = new Insets(3, 4, 3, 4);
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
Font labelFont = new Font(Font.SANS_SERIF, Font.PLAIN, 12);
|
||||
|
||||
// Host / Port
|
||||
gbc.gridx = 0; gbc.gridy = 0;
|
||||
JLabel hLbl = new JLabel("Host:");
|
||||
hLbl.setFont(labelFont);
|
||||
topPanel.add(hLbl, gbc);
|
||||
|
||||
gbc.gridx = 1; gbc.weightx = 1.0;
|
||||
hostField = createField(config.getHost() != null ? config.getHost() : "localhost", 14);
|
||||
topPanel.add(hostField, gbc);
|
||||
|
||||
gbc.gridx = 2; gbc.weightx = 0;
|
||||
JLabel pLbl = new JLabel("Port:");
|
||||
pLbl.setFont(labelFont);
|
||||
topPanel.add(pLbl, gbc);
|
||||
|
||||
gbc.gridx = 3; gbc.weightx = 0.5;
|
||||
portField = createField(String.valueOf(config.getPort()), 5);
|
||||
topPanel.add(portField, gbc);
|
||||
|
||||
// Printer LU / Display LU
|
||||
gbc.gridx = 0; gbc.gridy = 1; gbc.weightx = 0;
|
||||
JLabel pluLbl = new JLabel("Printer LU:");
|
||||
pluLbl.setFont(labelFont);
|
||||
topPanel.add(pluLbl, gbc);
|
||||
|
||||
gbc.gridx = 1; gbc.weightx = 1.0;
|
||||
printerLuField = createField("", 10);
|
||||
topPanel.add(printerLuField, gbc);
|
||||
|
||||
gbc.gridx = 2; gbc.weightx = 0;
|
||||
JLabel assocLbl = new JLabel("Assoc LU:");
|
||||
assocLbl.setFont(labelFont);
|
||||
topPanel.add(assocLbl, gbc);
|
||||
|
||||
gbc.gridx = 3; gbc.weightx = 0.5;
|
||||
displayLuField = createField("", 10);
|
||||
topPanel.add(displayLuField, gbc);
|
||||
|
||||
// CodePage & TLS
|
||||
gbc.gridx = 0; gbc.gridy = 2; gbc.weightx = 0;
|
||||
JLabel cpLbl = new JLabel("CodePage:");
|
||||
cpLbl.setFont(labelFont);
|
||||
topPanel.add(cpLbl, gbc);
|
||||
|
||||
gbc.gridx = 1; gbc.weightx = 1.0;
|
||||
codePageCombo = new JComboBox<>(new String[]{"037", "1047", "500", "273", "277", "278", "280", "284", "285", "297", "870", "1140"});
|
||||
ThemeManager.styleComboBox(codePageCombo);
|
||||
topPanel.add(codePageCombo, gbc);
|
||||
|
||||
gbc.gridx = 2; gbc.gridwidth = 2;
|
||||
JPanel tlsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 0));
|
||||
tlsPanel.setOpaque(false);
|
||||
tlsCheck = new JCheckBox("TLS/SSL", config.isUseTls());
|
||||
ThemeManager.styleCheckBox(tlsCheck);
|
||||
verifyCertCheck = new JCheckBox("Verify Cert", config.isTlsVerifyCert());
|
||||
ThemeManager.styleCheckBox(verifyCertCheck);
|
||||
tlsPanel.add(tlsCheck);
|
||||
tlsPanel.add(verifyCertCheck);
|
||||
topPanel.add(tlsPanel, gbc);
|
||||
|
||||
// Destination Type & Target
|
||||
gbc.gridx = 0; gbc.gridy = 3; gbc.gridwidth = 1;
|
||||
JLabel destLbl = new JLabel("Destination:");
|
||||
destLbl.setFont(labelFont);
|
||||
topPanel.add(destLbl, gbc);
|
||||
|
||||
gbc.gridx = 1;
|
||||
destinationCombo = new JComboBox<>(PrinterConfig.DestinationType.values());
|
||||
destinationCombo.setSelectedItem(PrinterConfig.DestinationType.MEMORY);
|
||||
ThemeManager.styleComboBox(destinationCombo);
|
||||
topPanel.add(destinationCombo, gbc);
|
||||
|
||||
gbc.gridx = 2;
|
||||
JLabel tgtLbl = new JLabel("Target Path:");
|
||||
tgtLbl.setFont(labelFont);
|
||||
topPanel.add(tgtLbl, gbc);
|
||||
|
||||
gbc.gridx = 3;
|
||||
targetField = createField("printer_output.txt", 12);
|
||||
topPanel.add(targetField, gbc);
|
||||
|
||||
// Connect / Disconnect Buttons
|
||||
gbc.gridx = 0; gbc.gridy = 4; gbc.gridwidth = 4;
|
||||
JPanel connBtnPan = new JPanel(new FlowLayout(FlowLayout.RIGHT, 6, 0));
|
||||
connBtnPan.setOpaque(false);
|
||||
|
||||
connectBtn = new JButton("Start Printer Session");
|
||||
ThemeManager.styleButton(connectBtn, ThemeManager.ButtonVariant.PRIMARY);
|
||||
connectBtn.addActionListener(e -> startPrinterSession());
|
||||
|
||||
disconnectBtn = new JButton("Stop Session");
|
||||
ThemeManager.styleButton(disconnectBtn, ThemeManager.ButtonVariant.DANGER);
|
||||
disconnectBtn.setEnabled(false);
|
||||
disconnectBtn.addActionListener(e -> stopPrinterSession());
|
||||
|
||||
connBtnPan.add(connectBtn);
|
||||
connBtnPan.add(disconnectBtn);
|
||||
topPanel.add(connBtnPan, gbc);
|
||||
|
||||
mainPanel.add(topPanel, BorderLayout.NORTH);
|
||||
|
||||
// Center: Spool Area & Status
|
||||
JPanel centerPanel = new JPanel(new BorderLayout(6, 6));
|
||||
centerPanel.setOpaque(false);
|
||||
centerPanel.setBorder(ThemeManager.createTitledBorder("Printer Spool & Status"));
|
||||
|
||||
// Status Header
|
||||
JPanel statusHeader = new JPanel(new GridLayout(1, 4, 10, 0));
|
||||
statusHeader.setOpaque(false);
|
||||
statusHeader.setBorder(new EmptyBorder(0, 4, 4, 4));
|
||||
|
||||
statusLabel = new JLabel("Status: Disconnected");
|
||||
statusLabel.setForeground(ThemeManager.getFgMuted());
|
||||
statusLabel.setFont(labelFont);
|
||||
|
||||
sessionTypeLabel = new JLabel("Session: -");
|
||||
sessionTypeLabel.setFont(labelFont);
|
||||
|
||||
pagesLabel = new JLabel("Pages: 0");
|
||||
pagesLabel.setFont(labelFont);
|
||||
|
||||
bytesLabel = new JLabel("Bytes: 0");
|
||||
bytesLabel.setFont(labelFont);
|
||||
|
||||
statusHeader.add(statusLabel);
|
||||
statusHeader.add(sessionTypeLabel);
|
||||
statusHeader.add(pagesLabel);
|
||||
statusHeader.add(bytesLabel);
|
||||
centerPanel.add(statusHeader, BorderLayout.NORTH);
|
||||
|
||||
// Spool text area
|
||||
spoolArea = new JTextArea();
|
||||
spoolArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
|
||||
spoolArea.setEditable(false);
|
||||
ThemeManager.styleTextArea(spoolArea);
|
||||
|
||||
JScrollPane spoolScroll = new JScrollPane(spoolArea);
|
||||
ThemeManager.styleScrollPane(spoolScroll);
|
||||
centerPanel.add(spoolScroll, BorderLayout.CENTER);
|
||||
|
||||
mainPanel.add(centerPanel, BorderLayout.CENTER);
|
||||
|
||||
// Bottom: Spool controls
|
||||
JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 6, 0));
|
||||
bottomPanel.setOpaque(false);
|
||||
|
||||
JButton clearSpoolBtn = new JButton("Clear Spool");
|
||||
ThemeManager.styleButton(clearSpoolBtn, ThemeManager.ButtonVariant.DEFAULT);
|
||||
clearSpoolBtn.addActionListener(e -> {
|
||||
spoolArea.setText("");
|
||||
if (printerSession != null && printerSession.getPD() != null) {
|
||||
printerSession.getPD().resetCapture();
|
||||
}
|
||||
pagesLabel.setText("Pages: 0");
|
||||
bytesLabel.setText("Bytes: 0");
|
||||
});
|
||||
|
||||
JButton saveSpoolBtn = new JButton("Save Spool As...");
|
||||
ThemeManager.styleButton(saveSpoolBtn, ThemeManager.ButtonVariant.DEFAULT);
|
||||
saveSpoolBtn.addActionListener(e -> saveSpool());
|
||||
|
||||
JButton printSpoolBtn = new JButton("Print Spool...");
|
||||
ThemeManager.styleButton(printSpoolBtn, ThemeManager.ButtonVariant.DEFAULT);
|
||||
printSpoolBtn.addActionListener(e -> printSpool());
|
||||
|
||||
JButton closeBtn = new JButton("Close");
|
||||
ThemeManager.styleButton(closeBtn, ThemeManager.ButtonVariant.CANCEL);
|
||||
closeBtn.addActionListener(e -> dispose());
|
||||
|
||||
bottomPanel.add(clearSpoolBtn);
|
||||
bottomPanel.add(saveSpoolBtn);
|
||||
bottomPanel.add(printSpoolBtn);
|
||||
bottomPanel.add(Box.createHorizontalStrut(10));
|
||||
bottomPanel.add(closeBtn);
|
||||
|
||||
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
|
||||
|
||||
setContentPane(mainPanel);
|
||||
ThemeManager.applyThemeToWindow(this);
|
||||
}
|
||||
|
||||
private JTextField createField(String text, int cols) {
|
||||
JTextField tf = new JTextField(text, cols);
|
||||
tf.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
|
||||
ThemeManager.styleTextField(tf);
|
||||
return tf;
|
||||
}
|
||||
|
||||
private void startPrinterSession() {
|
||||
String host = hostField.getText().trim();
|
||||
if (host.isEmpty()) {
|
||||
hostField.requestFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
int port = 23;
|
||||
try {
|
||||
port = Integer.parseInt(portField.getText().trim());
|
||||
} catch (NumberFormatException ignored) {}
|
||||
|
||||
config.setHost(host);
|
||||
config.setPort(port);
|
||||
config.setUseTls(tlsCheck.isSelected());
|
||||
config.setTlsVerifyCert(verifyCertCheck.isSelected());
|
||||
config.setCodePage((String) codePageCombo.getSelectedItem());
|
||||
|
||||
String plu = printerLuField.getText().trim();
|
||||
config.setPrinterLuName(plu.isEmpty() ? null : plu);
|
||||
|
||||
String dlu = displayLuField.getText().trim();
|
||||
config.setAssociatedDisplayLuName(dlu.isEmpty() ? null : dlu);
|
||||
|
||||
config.setDestinationType((PrinterConfig.DestinationType) destinationCombo.getSelectedItem());
|
||||
config.setDestinationTarget(targetField.getText().trim());
|
||||
|
||||
if (printerSession != null) {
|
||||
printerSession.close();
|
||||
}
|
||||
|
||||
printerSession = new Telnet3270EP(config);
|
||||
printerSession.addPrintListener(this);
|
||||
|
||||
connectBtn.setEnabled(false);
|
||||
disconnectBtn.setEnabled(true);
|
||||
statusLabel.setText("Status: Connecting...");
|
||||
statusLabel.setForeground(new Color(255, 200, 80));
|
||||
|
||||
new Thread(() -> {
|
||||
boolean success = printerSession.open();
|
||||
if (!success) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
connectBtn.setEnabled(true);
|
||||
disconnectBtn.setEnabled(false);
|
||||
statusLabel.setText("Status: Connection Failed");
|
||||
statusLabel.setForeground(new Color(255, 80, 80));
|
||||
});
|
||||
}
|
||||
}, "PrinterSession-Thread").start();
|
||||
}
|
||||
|
||||
private void stopPrinterSession() {
|
||||
if (printerSession != null) {
|
||||
printerSession.close();
|
||||
printerSession = null;
|
||||
}
|
||||
connectBtn.setEnabled(true);
|
||||
disconnectBtn.setEnabled(false);
|
||||
statusLabel.setText("Status: Disconnected");
|
||||
statusLabel.setForeground(new Color(180, 180, 180));
|
||||
sessionTypeLabel.setText("Session: -");
|
||||
}
|
||||
|
||||
private void saveSpool() {
|
||||
JFileChooser fc = new JFileChooser();
|
||||
fc.setDialogTitle("Save Printer Spool Output");
|
||||
fc.setSelectedFile(new File("spool_output.txt"));
|
||||
if (fc.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
|
||||
try (FileOutputStream fos = new FileOutputStream(fc.getSelectedFile())) {
|
||||
fos.write(spoolArea.getText().getBytes(StandardCharsets.UTF_8));
|
||||
JOptionPane.showMessageDialog(this, "Spool saved successfully to " + fc.getSelectedFile().getName(),
|
||||
"Saved", JOptionPane.INFORMATION_MESSAGE);
|
||||
} catch (IOException e) {
|
||||
JOptionPane.showMessageDialog(this, "Save error: " + e.getMessage(),
|
||||
"Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void printSpool() {
|
||||
String text = spoolArea.getText();
|
||||
if (text.isEmpty()) {
|
||||
JOptionPane.showMessageDialog(this, "Spool buffer is empty.", "Nothing to Print", JOptionPane.INFORMATION_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
PrinterJob job = PrinterJob.getPrinterJob();
|
||||
job.setJobName("j3270 3287 Print Spool");
|
||||
if (job.printDialog()) {
|
||||
try {
|
||||
spoolArea.print();
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(this, "Print error: " + ex.getMessage(), "Print Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== PrintSessionListener ==========
|
||||
|
||||
@Override
|
||||
public void onPrintJobStarted(PrintSessionEvent event) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
statusLabel.setText("Status: Printing...");
|
||||
statusLabel.setForeground(new Color(50, 205, 50));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrintJobData(PrintSessionEvent event) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (printerSession != null && printerSession.getPD() != null) {
|
||||
spoolArea.setText(printerSession.getPD().getCapturedText());
|
||||
spoolArea.setCaretPosition(spoolArea.getDocument().getLength());
|
||||
pagesLabel.setText("Pages: " + printerSession.getPD().getPageCount());
|
||||
bytesLabel.setText("Bytes: " + printerSession.getPD().getByteCount());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrintJobPageComplete(PrintSessionEvent event) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (printerSession != null && printerSession.getPD() != null) {
|
||||
spoolArea.setText(printerSession.getPD().getCapturedText());
|
||||
pagesLabel.setText("Pages: " + printerSession.getPD().getPageCount());
|
||||
bytesLabel.setText("Bytes: " + printerSession.getPD().getByteCount());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrintJobComplete(PrintSessionEvent event) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (printerSession != null && printerSession.getPD() != null) {
|
||||
spoolArea.setText(printerSession.getPD().getCapturedText());
|
||||
pagesLabel.setText("Pages: " + printerSession.getPD().getPageCount());
|
||||
bytesLabel.setText("Bytes: " + printerSession.getPD().getByteCount());
|
||||
}
|
||||
statusLabel.setText("Status: In Session (Idle)");
|
||||
statusLabel.setForeground(new Color(50, 205, 50));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrinterStatusChanged(PrintSessionEvent event) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
int code = event.getStatusCode();
|
||||
if (code == PrinterConstants.STATUS_CONNECTED || code == PrinterConstants.STATUS_PRINTER_READY || code == PrinterConstants.STATUS_PRINTING || code == PrinterConstants.STATUS_JOB_COMPLETE || code == PrinterConstants.STATUS_PAGE_COMPLETE) {
|
||||
String lu = printerSession != null ? printerSession.getAssignedLuName() : null;
|
||||
statusLabel.setText("Status: Connected" + (lu != null ? " (" + lu + ")" : ""));
|
||||
statusLabel.setForeground(new Color(50, 205, 50));
|
||||
connectBtn.setEnabled(false);
|
||||
disconnectBtn.setEnabled(true);
|
||||
|
||||
if (printerSession != null) {
|
||||
short luType = printerSession.getActiveLuType();
|
||||
if (luType == PrinterConstants.LU_TYPE_1_SCS) {
|
||||
sessionTypeLabel.setText("Session: LU1 (SCS)");
|
||||
} else if (luType == PrinterConstants.LU_TYPE_3_DS) {
|
||||
sessionTypeLabel.setText("Session: LU3 (3270 DS)");
|
||||
}
|
||||
}
|
||||
} else if (code == PrinterConstants.STATUS_DISCONNECTED) {
|
||||
statusLabel.setText("Status: Disconnected");
|
||||
statusLabel.setForeground(new Color(180, 180, 180));
|
||||
connectBtn.setEnabled(true);
|
||||
disconnectBtn.setEnabled(false);
|
||||
sessionTypeLabel.setText("Session: -");
|
||||
} else {
|
||||
statusLabel.setText("Status: " + event.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrinterError(PrintSessionEvent event) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
statusLabel.setText("Status: Error (" + event.getMessage() + ")");
|
||||
statusLabel.setForeground(new Color(255, 80, 80));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
stopPrinterSession();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package haus.nightmare.j3270.ui;
|
||||
|
||||
import haus.nightmare.lib3270j.Telnet3270Client;
|
||||
import haus.nightmare.lib3270j.screen.ExtendedAttribute;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
import static haus.nightmare.lib3270j.protocol.DS3270Constants.*;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Utility for exporting the 3270 Presentation Space to Plain Text, HTML, or PNG image.
|
||||
*/
|
||||
public class ScreenExporter {
|
||||
|
||||
public static void showExportDialog(Frame parent, Telnet3270Client client, TerminalPanel panel) {
|
||||
if (client == null) {
|
||||
JOptionPane.showMessageDialog(parent, "Connect to a host before exporting the screen.",
|
||||
"Not Connected", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
JFileChooser fc = new JFileChooser();
|
||||
fc.setDialogTitle("Save Screen As...");
|
||||
fc.addChoosableFileFilter(new FileNameExtensionFilter("PNG Image (*.png)", "png"));
|
||||
fc.addChoosableFileFilter(new FileNameExtensionFilter("HTML Document (*.html)", "html", "htm"));
|
||||
FileNameExtensionFilter txtFilter = new FileNameExtensionFilter("Plain Text (*.txt)", "txt");
|
||||
fc.addChoosableFileFilter(txtFilter);
|
||||
fc.setFileFilter(txtFilter);
|
||||
fc.setSelectedFile(new File("screen_capture.txt"));
|
||||
|
||||
int result = fc.showSaveDialog(parent);
|
||||
if (result == JFileChooser.APPROVE_OPTION) {
|
||||
File file = fc.getSelectedFile();
|
||||
String name = file.getName().toLowerCase();
|
||||
|
||||
try {
|
||||
if (name.endsWith(".png") || (fc.getFileFilter() != null && fc.getFileFilter().getDescription().contains("PNG"))) {
|
||||
if (!name.endsWith(".png")) file = new File(file.getAbsolutePath() + ".png");
|
||||
exportToPng(panel, file);
|
||||
} else if (name.endsWith(".html") || name.endsWith(".htm") || (fc.getFileFilter() != null && fc.getFileFilter().getDescription().contains("HTML"))) {
|
||||
if (!name.endsWith(".html") && !name.endsWith(".htm")) file = new File(file.getAbsolutePath() + ".html");
|
||||
exportToHtml(client, file);
|
||||
} else {
|
||||
if (!name.endsWith(".txt")) file = new File(file.getAbsolutePath() + ".txt");
|
||||
exportToText(client, file);
|
||||
}
|
||||
|
||||
JOptionPane.showMessageDialog(parent, "Screen saved successfully to:\n" + file.getAbsolutePath(),
|
||||
"Export Successful", JOptionPane.INFORMATION_MESSAGE);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(parent, "Failed to export screen: " + ex.getMessage(),
|
||||
"Export Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void exportToText(Telnet3270Client client, File file) throws IOException {
|
||||
ScreenBuffer sb = client.getScreenBuffer();
|
||||
int rows = sb.getDisplayRows();
|
||||
int cols = sb.getDisplayCols();
|
||||
|
||||
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) {
|
||||
for (int r = 0; r < rows; r++) {
|
||||
StringBuilder line = new StringBuilder();
|
||||
for (int c = 0; c < cols; c++) {
|
||||
ExtendedAttribute ea = sb.getCell(r * cols + c);
|
||||
char ch = ea.ucs4;
|
||||
if (ea.isFieldAttribute() || ch <= 0x20 || ch == 0xFF) {
|
||||
line.append(' ');
|
||||
} else {
|
||||
line.append(ch);
|
||||
}
|
||||
}
|
||||
// Strip trailing spaces for clean text output
|
||||
int lastNonSpace = line.length() - 1;
|
||||
while (lastNonSpace >= 0 && line.charAt(lastNonSpace) == ' ') {
|
||||
lastNonSpace--;
|
||||
}
|
||||
writer.write(line.substring(0, lastNonSpace + 1));
|
||||
writer.newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void exportToHtml(Telnet3270Client client, File file) throws IOException {
|
||||
ScreenBuffer sb = client.getScreenBuffer();
|
||||
int rows = sb.getDisplayRows();
|
||||
int cols = sb.getDisplayCols();
|
||||
|
||||
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) {
|
||||
writer.write("<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<title>3270 Screen Capture</title>\n");
|
||||
writer.write("<style>\n");
|
||||
writer.write("body { background-color: #0a0a0a; color: #32cd32; font-family: 'Courier New', Courier, monospace; font-size: 14px; margin: 20px; }\n");
|
||||
writer.write(".screen { background-color: #000; padding: 15px; border-radius: 4px; display: inline-block; box-shadow: 0 0 10px rgba(0,0,0,0.8); line-height: 1.2; }\n");
|
||||
writer.write(".c-blue { color: #7890f0; }\n");
|
||||
writer.write(".c-red { color: #ff0000; }\n");
|
||||
writer.write(".c-pink { color: #ff00ff; }\n");
|
||||
writer.write(".c-green { color: #00ff00; }\n");
|
||||
writer.write(".c-turq { color: #00ffff; }\n");
|
||||
writer.write(".c-yellow { color: #ffff00; }\n");
|
||||
writer.write(".c-white { color: #ffffff; }\n");
|
||||
writer.write(".c-black { color: #000000; }\n");
|
||||
writer.write(".c-deepblue { color: #000080; }\n");
|
||||
writer.write(".c-orange { color: #ffa200; }\n");
|
||||
writer.write(".c-purple { color: #800080; }\n");
|
||||
writer.write(".c-palegreen { color: #008000; }\n");
|
||||
writer.write(".c-paleturq { color: #008080; }\n");
|
||||
writer.write(".c-mustard { color: #a0a000; }\n");
|
||||
writer.write(".c-grey { color: #c0c0c0; }\n");
|
||||
writer.write(".bold { font-weight: bold; }\n");
|
||||
writer.write(".underline { text-decoration: underline; }\n");
|
||||
writer.write("</style>\n</head>\n<body>\n<div class=\"screen\"><pre>");
|
||||
|
||||
byte currentFA = 0;
|
||||
ExtendedAttribute currentFieldEa = null;
|
||||
|
||||
for (int r = 0; r < rows; r++) {
|
||||
String currentStyle = null;
|
||||
StringBuilder currentRun = new StringBuilder();
|
||||
|
||||
for (int c = 0; c < cols; c++) {
|
||||
int baddr = r * cols + c;
|
||||
ExtendedAttribute ea = sb.getCell(baddr);
|
||||
|
||||
if (ea.isFieldAttribute()) {
|
||||
currentFA = ea.fa;
|
||||
currentFieldEa = ea;
|
||||
if (currentRun.length() > 0) {
|
||||
writeHtmlSpan(writer, currentStyle, currentRun.toString());
|
||||
currentRun.setLength(0);
|
||||
currentStyle = null;
|
||||
}
|
||||
writer.write(' ');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (faIsZero(currentFA & 0xFF)) {
|
||||
if (currentRun.length() > 0) {
|
||||
writeHtmlSpan(writer, currentStyle, currentRun.toString());
|
||||
currentRun.setLength(0);
|
||||
currentStyle = null;
|
||||
}
|
||||
writer.write(' ');
|
||||
continue;
|
||||
}
|
||||
|
||||
char ch = ea.ucs4;
|
||||
if (ch == 0 || ch == 0xFF) {
|
||||
if (currentRun.length() > 0) {
|
||||
writeHtmlSpan(writer, currentStyle, currentRun.toString());
|
||||
currentRun.setLength(0);
|
||||
currentStyle = null;
|
||||
}
|
||||
writer.write(' ');
|
||||
continue;
|
||||
}
|
||||
|
||||
String colorClass = getHtmlColorClass(ea, currentFieldEa, currentFA);
|
||||
boolean bold = faIsHigh(currentFA & 0xFF) || (ea.gr & GR_INTENSIFY) != 0;
|
||||
boolean underline = (ea.gr & GR_UNDERLINE) != 0;
|
||||
|
||||
StringBuilder css = new StringBuilder();
|
||||
if (!colorClass.isEmpty()) css.append(colorClass).append(" ");
|
||||
if (bold) css.append("bold ");
|
||||
if (underline) css.append("underline ");
|
||||
String style = css.toString().trim();
|
||||
|
||||
if (currentStyle == null || !currentStyle.equals(style)) {
|
||||
if (currentRun.length() > 0) {
|
||||
writeHtmlSpan(writer, currentStyle, currentRun.toString());
|
||||
currentRun.setLength(0);
|
||||
}
|
||||
currentStyle = style;
|
||||
}
|
||||
currentRun.append(escapeHtml(ch));
|
||||
}
|
||||
|
||||
if (currentRun.length() > 0) {
|
||||
writeHtmlSpan(writer, currentStyle, currentRun.toString());
|
||||
}
|
||||
writer.newLine();
|
||||
}
|
||||
|
||||
writer.write("</pre></div>\n</body>\n</html>");
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeHtmlSpan(BufferedWriter writer, String style, String content) throws IOException {
|
||||
if (style != null && !style.isEmpty()) {
|
||||
writer.write("<span class=\"" + style + "\">" + content + "</span>");
|
||||
} else {
|
||||
writer.write(content);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getHtmlColorClass(ExtendedAttribute ea, ExtendedAttribute currentFieldEa, byte currentFA) {
|
||||
int fg = ea.fg != 0 ? (ea.fg & 0xFF) : (currentFieldEa != null && currentFieldEa.fg != 0 ? (currentFieldEa.fg & 0xFF) : 0);
|
||||
if (fg >= 0xf0 && fg <= 0xff) {
|
||||
switch (fg - 0xf0) {
|
||||
case 1: return "c-blue";
|
||||
case 2: return "c-red";
|
||||
case 3: return "c-pink";
|
||||
case 4: return "c-green";
|
||||
case 5: return "c-turq";
|
||||
case 6: return "c-yellow";
|
||||
case 7: return "c-white";
|
||||
case 8: return "c-black";
|
||||
case 9: return "c-deepblue";
|
||||
case 10: return "c-orange";
|
||||
case 11: return "c-purple";
|
||||
case 12: return "c-palegreen";
|
||||
case 13: return "c-paleturq";
|
||||
case 14: return "c-mustard";
|
||||
case 15: return "c-grey";
|
||||
}
|
||||
}
|
||||
if (faIsProtected(currentFA & 0xFF)) {
|
||||
return faIsHigh(currentFA & 0xFF) ? "c-white" : "c-turq";
|
||||
}
|
||||
return faIsHigh(currentFA & 0xFF) ? "c-red" : "c-green";
|
||||
}
|
||||
|
||||
private static String escapeHtml(char ch) {
|
||||
switch (ch) {
|
||||
case '<': return "<";
|
||||
case '>': return ">";
|
||||
case '&': return "&";
|
||||
case '"': return """;
|
||||
default: return String.valueOf(ch);
|
||||
}
|
||||
}
|
||||
|
||||
public static void exportToPng(TerminalPanel panel, File file) throws IOException {
|
||||
int w = panel.getWidth();
|
||||
int h = panel.getHeight();
|
||||
if (w <= 0 || h <= 0) {
|
||||
Dimension pref = panel.getPreferredSize();
|
||||
w = Math.max(pref.width, 800);
|
||||
h = Math.max(pref.height, 500);
|
||||
}
|
||||
|
||||
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g2 = image.createGraphics();
|
||||
panel.paint(g2);
|
||||
g2.dispose();
|
||||
|
||||
ImageIO.write(image, "PNG", file);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
package haus.nightmare.j3270.ui;
|
||||
|
||||
import haus.nightmare.lib3270j.Telnet3270Client;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.LineBorder;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import java.awt.*;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Dialog for composing and executing Host On-Demand ECL mnemonic keystroke scripts.
|
||||
*/
|
||||
public class ScriptDialog extends JDialog {
|
||||
|
||||
private final Telnet3270Client client;
|
||||
private final TerminalPanel terminalPanel;
|
||||
|
||||
private JTextArea scriptArea;
|
||||
private JLabel statusLabel;
|
||||
|
||||
public ScriptDialog(Frame parent, Telnet3270Client client, TerminalPanel terminalPanel) {
|
||||
super(parent, "Run Keystrokes / ECL Script", false);
|
||||
this.client = client;
|
||||
this.terminalPanel = terminalPanel;
|
||||
|
||||
buildUI();
|
||||
setSize(600, 480);
|
||||
setLocationRelativeTo(parent);
|
||||
}
|
||||
|
||||
private void buildUI() {
|
||||
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
|
||||
mainPanel.setBorder(new EmptyBorder(12, 14, 12, 14));
|
||||
|
||||
Font labelFont = new Font(Font.SANS_SERIF, Font.PLAIN, 13);
|
||||
|
||||
// Header
|
||||
JPanel topPanel = new JPanel(new BorderLayout(5, 5));
|
||||
topPanel.setOpaque(false);
|
||||
JLabel descLabel = new JLabel("<html>Compose keystrokes and ECL bracketed mnemonics to stream to the mainframe.<br>"
|
||||
+ "Example: <code>TSO[enter]USER[tab]PASSWORD[enter]</code> or <code>[pf3][clear]</code></html>");
|
||||
descLabel.setFont(labelFont);
|
||||
topPanel.add(descLabel, BorderLayout.CENTER);
|
||||
mainPanel.add(topPanel, BorderLayout.NORTH);
|
||||
|
||||
// Script area
|
||||
scriptArea = new JTextArea();
|
||||
scriptArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 13));
|
||||
scriptArea.setLineWrap(true);
|
||||
scriptArea.setWrapStyleWord(false);
|
||||
ThemeManager.styleTextArea(scriptArea);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(scriptArea);
|
||||
ThemeManager.styleScrollPane(scrollPane);
|
||||
|
||||
// Mnemonic helper buttons
|
||||
JPanel tokenPanel = new JPanel(new WrapLayout(FlowLayout.LEFT, 4, 4));
|
||||
tokenPanel.setOpaque(false);
|
||||
tokenPanel.setBorder(ThemeManager.createTitledBorder("Insert Keystroke Token"));
|
||||
|
||||
String[] tokens = {
|
||||
"[enter]", "[tab]", "[backtab]", "[clear]", "[reset]",
|
||||
"[eraseeof]", "[eraseinput]", "[newline]", "[dup]", "[fm]",
|
||||
"[attn]", "[sysreq]", "[cursel]",
|
||||
"[pf1]", "[pf2]", "[pf3]", "[pf4]", "[pf5]", "[pf6]",
|
||||
"[pf7]", "[pf8]", "[pf9]", "[pf10]", "[pf11]", "[pf12]",
|
||||
"[pf13]", "[pf14]", "[pf15]", "[pf16]", "[pf17]", "[pf18]",
|
||||
"[pf19]", "[pf20]", "[pf21]", "[pf22]", "[pf23]", "[pf24]",
|
||||
"[pa1]", "[pa2]", "[pa3]", "[up]", "[down]", "[left]", "[right]", "[home]"
|
||||
};
|
||||
|
||||
for (String token : tokens) {
|
||||
JButton btn = new JButton(token);
|
||||
btn.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 11));
|
||||
ThemeManager.styleButton(btn, ThemeManager.ButtonVariant.DEFAULT);
|
||||
btn.setFocusable(false);
|
||||
btn.setMargin(new Insets(2, 4, 2, 4));
|
||||
btn.addActionListener(e -> insertToken(token));
|
||||
tokenPanel.add(btn);
|
||||
}
|
||||
|
||||
JPanel centerPanel = new JPanel(new BorderLayout(6, 6));
|
||||
centerPanel.setOpaque(false);
|
||||
centerPanel.add(scrollPane, BorderLayout.CENTER);
|
||||
centerPanel.add(tokenPanel, BorderLayout.SOUTH);
|
||||
mainPanel.add(centerPanel, BorderLayout.CENTER);
|
||||
|
||||
// Bottom
|
||||
JPanel bottomPanel = new JPanel(new BorderLayout(5, 5));
|
||||
bottomPanel.setOpaque(false);
|
||||
|
||||
statusLabel = new JLabel("Ready");
|
||||
statusLabel.setForeground(ThemeManager.getFgMuted());
|
||||
statusLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 12));
|
||||
bottomPanel.add(statusLabel, BorderLayout.WEST);
|
||||
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 6, 0));
|
||||
buttonPanel.setOpaque(false);
|
||||
|
||||
JButton loadBtn = new JButton("Load Script...");
|
||||
ThemeManager.styleButton(loadBtn, ThemeManager.ButtonVariant.DEFAULT);
|
||||
loadBtn.setFont(labelFont);
|
||||
loadBtn.addActionListener(e -> loadScript());
|
||||
|
||||
JButton saveBtn = new JButton("Save Script...");
|
||||
ThemeManager.styleButton(saveBtn, ThemeManager.ButtonVariant.DEFAULT);
|
||||
saveBtn.setFont(labelFont);
|
||||
saveBtn.addActionListener(e -> saveScript());
|
||||
|
||||
JButton runBtn = new JButton("Execute");
|
||||
ThemeManager.styleButton(runBtn, ThemeManager.ButtonVariant.PRIMARY);
|
||||
runBtn.setFont(labelFont);
|
||||
runBtn.addActionListener(e -> executeScript());
|
||||
|
||||
JButton closeBtn = new JButton("Close");
|
||||
ThemeManager.styleButton(closeBtn, ThemeManager.ButtonVariant.CANCEL);
|
||||
closeBtn.setFont(labelFont);
|
||||
closeBtn.addActionListener(e -> dispose());
|
||||
|
||||
buttonPanel.add(loadBtn);
|
||||
buttonPanel.add(saveBtn);
|
||||
buttonPanel.add(Box.createHorizontalStrut(10));
|
||||
buttonPanel.add(runBtn);
|
||||
buttonPanel.add(closeBtn);
|
||||
|
||||
bottomPanel.add(buttonPanel, BorderLayout.EAST);
|
||||
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
|
||||
|
||||
setContentPane(mainPanel);
|
||||
ThemeManager.applyThemeToWindow(this);
|
||||
}
|
||||
|
||||
private void insertToken(String token) {
|
||||
scriptArea.replaceSelection(token);
|
||||
scriptArea.requestFocusInWindow();
|
||||
}
|
||||
|
||||
private void executeScript() {
|
||||
String script = scriptArea.getText();
|
||||
if (script == null || script.trim().isEmpty()) {
|
||||
statusLabel.setText("Script is empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client == null || !client.getConnectionState().isFullSession()) {
|
||||
statusLabel.setText("Not connected to host.");
|
||||
JOptionPane.showMessageDialog(this, "Connect to a 3270 session before executing scripts.",
|
||||
"Not Connected", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
statusLabel.setText("Executing keystrokes...");
|
||||
new Thread(() -> {
|
||||
try {
|
||||
// Split multi-line scripts or process sequentially
|
||||
String cleaned = script.replace("\r\n", "\n").replace("\r", "\n");
|
||||
String[] lines = cleaned.split("\n");
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
String line = lines[i].trim();
|
||||
if (!line.isEmpty() && !line.startsWith("#") && !line.startsWith("//")) {
|
||||
client.sendKeys(line);
|
||||
Thread.sleep(100);
|
||||
}
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
statusLabel.setText("Script execution completed successfully.");
|
||||
if (terminalPanel != null) {
|
||||
terminalPanel.repaint();
|
||||
}
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
statusLabel.setText("Error executing script: " + ex.getMessage());
|
||||
JOptionPane.showMessageDialog(this, "Execution error: " + ex.getMessage(),
|
||||
"Script Error", JOptionPane.ERROR_MESSAGE);
|
||||
});
|
||||
}
|
||||
}, "ScriptRunner").start();
|
||||
}
|
||||
|
||||
private void loadScript() {
|
||||
JFileChooser fc = new JFileChooser();
|
||||
fc.setDialogTitle("Open Script File");
|
||||
if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
|
||||
try {
|
||||
byte[] bytes = java.nio.file.Files.readAllBytes(fc.getSelectedFile().toPath());
|
||||
scriptArea.setText(new String(bytes, StandardCharsets.UTF_8));
|
||||
statusLabel.setText("Loaded script: " + fc.getSelectedFile().getName());
|
||||
} catch (IOException e) {
|
||||
JOptionPane.showMessageDialog(this, "Could not open file: " + e.getMessage(),
|
||||
"Open Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveScript() {
|
||||
JFileChooser fc = new JFileChooser();
|
||||
fc.setDialogTitle("Save Script File");
|
||||
if (fc.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
|
||||
try {
|
||||
java.nio.file.Files.write(fc.getSelectedFile().toPath(),
|
||||
scriptArea.getText().getBytes(StandardCharsets.UTF_8));
|
||||
statusLabel.setText("Saved script: " + fc.getSelectedFile().getName());
|
||||
} catch (IOException e) {
|
||||
JOptionPane.showMessageDialog(this, "Could not save file: " + e.getMessage(),
|
||||
"Save Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FlowLayout subclass that fully supports word wrapping inside JScrollPanes / dialogs.
|
||||
*/
|
||||
private static class WrapLayout extends FlowLayout {
|
||||
public WrapLayout(int align, int hgap, int vgap) {
|
||||
super(align, hgap, vgap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension preferredLayoutSize(Container target) {
|
||||
return layoutSize(target, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension minimumLayoutSize(Container target) {
|
||||
Dimension minimum = layoutSize(target, false);
|
||||
minimum.width -= (getHgap() + 1);
|
||||
return minimum;
|
||||
}
|
||||
|
||||
private Dimension layoutSize(Container target, boolean preferred) {
|
||||
synchronized (target.getTreeLock()) {
|
||||
int targetWidth = target.getWidth();
|
||||
if (targetWidth == 0) targetWidth = Integer.MAX_VALUE;
|
||||
|
||||
int hgap = getHgap();
|
||||
int vgap = getVgap();
|
||||
Insets insets = target.getInsets();
|
||||
int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2);
|
||||
int maxWidth = targetWidth - horizontalInsetsAndGap;
|
||||
|
||||
Dimension dim = new Dimension(0, 0);
|
||||
int rowWidth = 0;
|
||||
int rowHeight = 0;
|
||||
|
||||
int nmembers = target.getComponentCount();
|
||||
for (int i = 0; i < nmembers; i++) {
|
||||
Component m = target.getComponent(i);
|
||||
if (m.isVisible()) {
|
||||
Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();
|
||||
if (rowWidth + d.width > maxWidth) {
|
||||
addRow(dim, rowWidth, rowHeight);
|
||||
rowWidth = 0;
|
||||
rowHeight = 0;
|
||||
}
|
||||
if (rowWidth != 0) rowWidth += hgap;
|
||||
rowWidth += d.width;
|
||||
rowHeight = Math.max(rowHeight, d.height);
|
||||
}
|
||||
}
|
||||
addRow(dim, rowWidth, rowHeight);
|
||||
|
||||
dim.width += horizontalInsetsAndGap;
|
||||
dim.height += insets.top + insets.bottom + vgap * 2;
|
||||
return dim;
|
||||
}
|
||||
}
|
||||
|
||||
private void addRow(Dimension dim, int rowWidth, int rowHeight) {
|
||||
dim.width = Math.max(dim.width, rowWidth);
|
||||
if (dim.height > 0) dim.height += getVgap();
|
||||
dim.height += rowHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,16 +19,8 @@ public class SettingsDialog extends JDialog {
|
||||
|
||||
private final J3270App parentApp;
|
||||
|
||||
// Dark theme colors
|
||||
private static final Color DARK_BG = new Color(43, 43, 43);
|
||||
private static final Color DARK_BG_LIGHTER = new Color(55, 55, 55);
|
||||
private static final Color DARK_FG = new Color(224, 224, 224);
|
||||
private static final Color DARK_BORDER = new Color(70, 70, 70);
|
||||
private static final Color DARK_SELECTION = new Color(75, 110, 175);
|
||||
private static final Color DARK_BUTTON_BG = new Color(60, 63, 65);
|
||||
private static final Color DARK_FIELD_BG = new Color(50, 50, 50);
|
||||
|
||||
// Appearance tab
|
||||
private JComboBox<UITheme> uiThemeBox;
|
||||
private JComboBox<String> fontBox;
|
||||
private JSpinner fontSizeSpinner;
|
||||
private JComboBox<haus.nightmare.lib3270j.graphics.GraphicsMode> graphicsModeBox;
|
||||
@@ -39,6 +31,8 @@ public class SettingsDialog extends JDialog {
|
||||
private JTextField hostField;
|
||||
private JTextField portField;
|
||||
private JCheckBox blockSelectCheck;
|
||||
private JSpinner dynamicRowsSpinner;
|
||||
private JSpinner dynamicColsSpinner;
|
||||
|
||||
// Advanced tab state tracking
|
||||
private final Color[] tempHostColors = new Color[16];
|
||||
@@ -51,22 +45,30 @@ public class SettingsDialog extends JDialog {
|
||||
this.parentApp = parent;
|
||||
|
||||
initComponents();
|
||||
setSize(550, 450);
|
||||
setSize(560, 480);
|
||||
setLocationRelativeTo(parent);
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
JTabbedPane tabbedPane = new JTabbedPane();
|
||||
ThemeManager.styleTabbedPane(tabbedPane);
|
||||
|
||||
tabbedPane.addTab("Appearance", createAppearancePanel());
|
||||
tabbedPane.addTab("Behavior", createBehaviorPanel());
|
||||
tabbedPane.addTab("Advanced", createAdvancedPanel());
|
||||
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 8, 8));
|
||||
JButton btnExport = new JButton("Export Config...");
|
||||
JButton btnOk = new JButton("OK");
|
||||
ThemeManager.styleButton(btnExport, ThemeManager.ButtonVariant.DEFAULT);
|
||||
|
||||
JButton btnApply = new JButton("Apply");
|
||||
ThemeManager.styleButton(btnApply, ThemeManager.ButtonVariant.DEFAULT);
|
||||
|
||||
JButton btnCancel = new JButton("Cancel");
|
||||
ThemeManager.styleButton(btnCancel, ThemeManager.ButtonVariant.CANCEL);
|
||||
|
||||
JButton btnOk = new JButton("OK");
|
||||
ThemeManager.styleButton(btnOk, ThemeManager.ButtonVariant.PRIMARY);
|
||||
|
||||
btnExport.addActionListener((ActionEvent e) -> {
|
||||
exportConfig();
|
||||
@@ -96,132 +98,11 @@ public class SettingsDialog extends JDialog {
|
||||
getContentPane().add(tabbedPane, BorderLayout.CENTER);
|
||||
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
|
||||
|
||||
// Apply dark theme to all components for cross-platform readability
|
||||
applyDarkTheme(getContentPane());
|
||||
applyDarkTheme(tabbedPane);
|
||||
applyDarkTheme(buttonPanel);
|
||||
getContentPane().setBackground(DARK_BG);
|
||||
ThemeManager.applyThemeToWindow(this);
|
||||
}
|
||||
|
||||
// ========== Dark Theme Utility ==========
|
||||
|
||||
/**
|
||||
* Recursively apply dark theme to a component and all its children.
|
||||
* Ensures the Settings dialog is readable on Windows, Linux, and macOS.
|
||||
*/
|
||||
private void applyDarkTheme(Component comp) {
|
||||
if (comp instanceof JTabbedPane) {
|
||||
JTabbedPane tp = (JTabbedPane) comp;
|
||||
tp.setBackground(DARK_BG);
|
||||
tp.setForeground(DARK_FG);
|
||||
for (int i = 0; i < tp.getTabCount(); i++) {
|
||||
applyDarkTheme(tp.getComponentAt(i));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JTable) {
|
||||
JTable table = (JTable) comp;
|
||||
table.setBackground(DARK_FIELD_BG);
|
||||
table.setForeground(DARK_FG);
|
||||
table.setSelectionBackground(DARK_SELECTION);
|
||||
table.setSelectionForeground(Color.WHITE);
|
||||
table.setGridColor(DARK_BORDER);
|
||||
JTableHeader header = table.getTableHeader();
|
||||
if (header != null) {
|
||||
header.setBackground(DARK_BG_LIGHTER);
|
||||
header.setForeground(DARK_FG);
|
||||
DefaultTableCellRenderer headerRenderer = new DefaultTableCellRenderer();
|
||||
headerRenderer.setBackground(DARK_BG_LIGHTER);
|
||||
headerRenderer.setForeground(DARK_FG);
|
||||
header.setDefaultRenderer(headerRenderer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JScrollPane) {
|
||||
JScrollPane sp = (JScrollPane) comp;
|
||||
sp.setBackground(DARK_BG);
|
||||
sp.getViewport().setBackground(DARK_FIELD_BG);
|
||||
applyDarkTheme(sp.getViewport().getView());
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JButton) {
|
||||
JButton btn = (JButton) comp;
|
||||
btn.setBackground(DARK_BUTTON_BG);
|
||||
btn.setForeground(DARK_FG);
|
||||
btn.setFocusPainted(false);
|
||||
btn.setBorder(BorderFactory.createCompoundBorder(
|
||||
new LineBorder(DARK_BORDER, 1),
|
||||
BorderFactory.createEmptyBorder(3, 10, 3, 10)));
|
||||
btn.setOpaque(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JComboBox) {
|
||||
JComboBox<?> cb = (JComboBox<?>) comp;
|
||||
cb.setBackground(DARK_FIELD_BG);
|
||||
cb.setForeground(DARK_FG);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JSpinner) {
|
||||
JSpinner sp = (JSpinner) comp;
|
||||
sp.setBackground(DARK_FIELD_BG);
|
||||
sp.setForeground(DARK_FG);
|
||||
JComponent editor = sp.getEditor();
|
||||
if (editor instanceof JSpinner.DefaultEditor) {
|
||||
JTextField tf = ((JSpinner.DefaultEditor) editor).getTextField();
|
||||
tf.setBackground(DARK_FIELD_BG);
|
||||
tf.setForeground(DARK_FG);
|
||||
tf.setCaretColor(DARK_FG);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JTextField) {
|
||||
JTextField tf = (JTextField) comp;
|
||||
tf.setBackground(DARK_FIELD_BG);
|
||||
tf.setForeground(DARK_FG);
|
||||
tf.setCaretColor(DARK_FG);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JCheckBox) {
|
||||
JCheckBox cb = (JCheckBox) comp;
|
||||
cb.setBackground(DARK_BG);
|
||||
cb.setForeground(DARK_FG);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JLabel) {
|
||||
comp.setForeground(DARK_FG);
|
||||
return;
|
||||
}
|
||||
|
||||
// Generic panel / container
|
||||
// Skip color swatch panels — their background IS the color
|
||||
if (comp instanceof JPanel && "colorSwatch".equals(comp.getName())) {
|
||||
return;
|
||||
}
|
||||
comp.setBackground(DARK_BG);
|
||||
comp.setForeground(DARK_FG);
|
||||
|
||||
if (comp instanceof JPanel) {
|
||||
JPanel panel = (JPanel) comp;
|
||||
// Style titled borders
|
||||
if (panel.getBorder() instanceof TitledBorder) {
|
||||
TitledBorder tb = (TitledBorder) panel.getBorder();
|
||||
tb.setTitleColor(DARK_FG);
|
||||
}
|
||||
}
|
||||
|
||||
if (comp instanceof Container) {
|
||||
for (Component child : ((Container) comp).getComponents()) {
|
||||
applyDarkTheme(child);
|
||||
}
|
||||
}
|
||||
ThemeManager.applyTheme(comp);
|
||||
}
|
||||
|
||||
// ========== Tab Panels ==========
|
||||
@@ -232,9 +113,22 @@ public class SettingsDialog extends JDialog {
|
||||
gbc.insets = new Insets(10, 10, 10, 10);
|
||||
gbc.anchor = GridBagConstraints.WEST;
|
||||
|
||||
// Font family
|
||||
// UI Theme
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
JLabel themeLabel = new JLabel("Java UI Theme:");
|
||||
panel.add(themeLabel, gbc);
|
||||
|
||||
uiThemeBox = new JComboBox<>(UITheme.values());
|
||||
uiThemeBox.setSelectedItem(Settings.getJavaUiTheme());
|
||||
gbc.gridx = 1;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
panel.add(uiThemeBox, gbc);
|
||||
|
||||
// Font family
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 1;
|
||||
gbc.fill = GridBagConstraints.NONE;
|
||||
JLabel fontLabel = new JLabel("Terminal Font:");
|
||||
panel.add(fontLabel, gbc);
|
||||
|
||||
@@ -254,7 +148,7 @@ public class SettingsDialog extends JDialog {
|
||||
|
||||
// Font size
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 1;
|
||||
gbc.gridy = 2;
|
||||
gbc.fill = GridBagConstraints.NONE;
|
||||
JLabel sizeLabel = new JLabel("Font Size:");
|
||||
panel.add(sizeLabel, gbc);
|
||||
@@ -267,7 +161,7 @@ public class SettingsDialog extends JDialog {
|
||||
|
||||
// Graphics Mode
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 2;
|
||||
gbc.gridy = 3;
|
||||
gbc.fill = GridBagConstraints.NONE;
|
||||
JLabel graphicsLabel = new JLabel("Graphics Mode:");
|
||||
panel.add(graphicsLabel, gbc);
|
||||
@@ -279,7 +173,7 @@ public class SettingsDialog extends JDialog {
|
||||
panel.add(graphicsModeBox, gbc);
|
||||
|
||||
// Fill remaining space
|
||||
gbc.gridy = 3;
|
||||
gbc.gridy = 4;
|
||||
gbc.weighty = 1.0;
|
||||
panel.add(Box.createGlue(), gbc);
|
||||
|
||||
@@ -343,8 +237,30 @@ public class SettingsDialog extends JDialog {
|
||||
blockSelectCheck = new JCheckBox("Block selection mode (rectangular select)", Settings.getBlockSelectMode());
|
||||
panel.add(blockSelectCheck, gbc);
|
||||
|
||||
// Placeholder for potentially more behavior options below
|
||||
// Default Dynamic Screen Size
|
||||
gbc.gridy = 3;
|
||||
gbc.gridwidth = 1;
|
||||
gbc.gridx = 0;
|
||||
panel.add(new JLabel("Default Dynamic Screen:"), gbc);
|
||||
|
||||
JPanel dynDimPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 6, 0));
|
||||
dynDimPanel.setOpaque(false);
|
||||
dynDimPanel.add(new JLabel("Rows:"));
|
||||
dynamicRowsSpinner = new JSpinner(new SpinnerNumberModel(Settings.getDynamicRows(), 24, 255, 1));
|
||||
ThemeManager.styleSpinner(dynamicRowsSpinner);
|
||||
dynDimPanel.add(dynamicRowsSpinner);
|
||||
dynDimPanel.add(new JLabel("Cols:"));
|
||||
dynamicColsSpinner = new JSpinner(new SpinnerNumberModel(Settings.getDynamicCols(), 80, 255, 1));
|
||||
ThemeManager.styleSpinner(dynamicColsSpinner);
|
||||
dynDimPanel.add(dynamicColsSpinner);
|
||||
|
||||
gbc.gridx = 1;
|
||||
panel.add(dynDimPanel, gbc);
|
||||
|
||||
// Placeholder for potentially more behavior options below
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 4;
|
||||
gbc.gridwidth = 2;
|
||||
gbc.weighty = 1.0;
|
||||
panel.add(Box.createGlue(), gbc);
|
||||
|
||||
@@ -429,6 +345,7 @@ public class SettingsDialog extends JDialog {
|
||||
|
||||
JPanel resetPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||
JButton btnResetColors = new JButton("Reset to Defaults");
|
||||
ThemeManager.styleButton(btnResetColors, ThemeManager.ButtonVariant.DEFAULT);
|
||||
btnResetColors.addActionListener(e -> {
|
||||
for (int i=0; i<16; i++) {
|
||||
tempHostColors[i] = TerminalPanel.DEFAULT_HOST_COLORS[i];
|
||||
@@ -436,8 +353,6 @@ public class SettingsDialog extends JDialog {
|
||||
for (int i=0; i<monoKeys.length; i++) {
|
||||
tempMonoColors.put(monoKeys[i], monoDefs[i]);
|
||||
}
|
||||
// Repaint container implicitly handled if we trigger a UI update,
|
||||
// but for simplicity user can just close/reopen or have it refresh on save
|
||||
JOptionPane.showMessageDialog(main, "Colors reset. Click Apply to save.");
|
||||
});
|
||||
resetPanel.add(btnResetColors);
|
||||
@@ -498,6 +413,7 @@ public class SettingsDialog extends JDialog {
|
||||
}
|
||||
|
||||
JTable table = new JTable(keymapModel);
|
||||
ThemeManager.styleTable(table);
|
||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
table.setFillsViewportHeight(true);
|
||||
|
||||
@@ -510,9 +426,8 @@ public class SettingsDialog extends JDialog {
|
||||
captureDialog.setSize(320, 100);
|
||||
captureDialog.setLocationRelativeTo(this);
|
||||
JLabel lbl = new JLabel("Press any key combination now...", SwingConstants.CENTER);
|
||||
lbl.setForeground(DARK_FG);
|
||||
captureDialog.getContentPane().setBackground(DARK_BG);
|
||||
captureDialog.add(lbl);
|
||||
ThemeManager.applyThemeToWindow(captureDialog);
|
||||
captureDialog.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
@@ -532,6 +447,7 @@ public class SettingsDialog extends JDialog {
|
||||
|
||||
// Replace Binding — sets the action to exactly one new key
|
||||
JButton btnReplace = new JButton("Replace Binding");
|
||||
ThemeManager.styleButton(btnReplace, ThemeManager.ButtonVariant.DEFAULT);
|
||||
btnReplace.addActionListener(e -> {
|
||||
int row = table.getSelectedRow();
|
||||
if (row < 0) return;
|
||||
@@ -545,6 +461,7 @@ public class SettingsDialog extends JDialog {
|
||||
|
||||
// Add Binding — appends an additional key to the existing binding(s)
|
||||
JButton btnAdd = new JButton("Add Binding");
|
||||
ThemeManager.styleButton(btnAdd, ThemeManager.ButtonVariant.DEFAULT);
|
||||
btnAdd.addActionListener(e -> {
|
||||
int row = table.getSelectedRow();
|
||||
if (row < 0) return;
|
||||
@@ -565,6 +482,7 @@ public class SettingsDialog extends JDialog {
|
||||
|
||||
// Remove Last — removes the last comma-separated binding entry
|
||||
JButton btnRemoveLast = new JButton("Remove Last");
|
||||
ThemeManager.styleButton(btnRemoveLast, ThemeManager.ButtonVariant.DEFAULT);
|
||||
btnRemoveLast.addActionListener(e -> {
|
||||
int row = table.getSelectedRow();
|
||||
if (row < 0) return;
|
||||
@@ -585,6 +503,7 @@ public class SettingsDialog extends JDialog {
|
||||
|
||||
// Unbind All — clears all bindings for the action
|
||||
JButton btnUnbind = new JButton("Unbind All");
|
||||
ThemeManager.styleButton(btnUnbind, ThemeManager.ButtonVariant.DEFAULT);
|
||||
btnUnbind.addActionListener(e -> {
|
||||
int row = table.getSelectedRow();
|
||||
if (row < 0) return;
|
||||
@@ -596,6 +515,7 @@ public class SettingsDialog extends JDialog {
|
||||
|
||||
// Reset Keymaps — restore all defaults
|
||||
JButton btnReset = new JButton("Reset All");
|
||||
ThemeManager.styleButton(btnReset, ThemeManager.ButtonVariant.DEFAULT);
|
||||
btnReset.addActionListener(e -> {
|
||||
for (int row = 0; row < keymapModel.getRowCount(); row++) {
|
||||
String action = (String) keymapModel.getValueAt(row, 0);
|
||||
@@ -628,6 +548,12 @@ public class SettingsDialog extends JDialog {
|
||||
private boolean applySettings() {
|
||||
try {
|
||||
// Apply Appearance
|
||||
UITheme selectedTheme = (UITheme) uiThemeBox.getSelectedItem();
|
||||
if (selectedTheme != null) {
|
||||
Settings.setJavaUiTheme(selectedTheme);
|
||||
ThemeManager.setTheme(selectedTheme);
|
||||
}
|
||||
|
||||
String fontFam = (String) fontBox.getSelectedItem();
|
||||
if (fontFam != null) {
|
||||
Settings.setFontFamily(fontFam);
|
||||
@@ -653,6 +579,12 @@ public class SettingsDialog extends JDialog {
|
||||
// Block select mode
|
||||
Settings.setBlockSelectMode(blockSelectCheck.isSelected());
|
||||
|
||||
// Default Dynamic screen dimensions
|
||||
if (dynamicRowsSpinner != null && dynamicColsSpinner != null) {
|
||||
Settings.setDynamicRows((Integer) dynamicRowsSpinner.getValue());
|
||||
Settings.setDynamicCols((Integer) dynamicColsSpinner.getValue());
|
||||
}
|
||||
|
||||
// Propagate visual changes to the app
|
||||
// Save Colors
|
||||
for (int i=0; i<16; i++) {
|
||||
@@ -668,6 +600,7 @@ public class SettingsDialog extends JDialog {
|
||||
}
|
||||
|
||||
parentApp.getTerminalPanel().reloadSettings();
|
||||
ThemeManager.applyThemeToWindow(this);
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -2,74 +2,60 @@ package haus.nightmare.j3270.ui;
|
||||
|
||||
import haus.nightmare.lib3270j.ConnectionState;
|
||||
import haus.nightmare.lib3270j.Telnet3270Client;
|
||||
import haus.nightmare.lib3270j.ecl.ECLConstants;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Status bar displaying connection state, cursor position, timing, and lock
|
||||
* status.
|
||||
* Status bar displaying connection state, cursor position, timing, and lock status.
|
||||
* Equivalent to the OIA (Operator Information Area) on a real 3270 terminal.
|
||||
*/
|
||||
public class StatusBar extends JPanel {
|
||||
|
||||
private final JLabel connectionStatus;
|
||||
private final JLabel tlsStatus;
|
||||
private final JLabel cursorPosition;
|
||||
private final JLabel luName;
|
||||
private final JLabel lockStatus;
|
||||
private final JLabel fieldTypeStatus;
|
||||
private final JLabel codePageInfo;
|
||||
private final JLabel modelInfo;
|
||||
private final JButton lpButton;
|
||||
private final JLabel cursorPosition;
|
||||
|
||||
private Telnet3270Client client;
|
||||
private TerminalPanel terminalPanel;
|
||||
|
||||
// OIA colors
|
||||
private static final Color OIA_BG = new Color(20, 20, 20);
|
||||
private static final Color OIA_FG = new Color(50, 205, 50);
|
||||
private static final Color OIA_DIM = new Color(80, 80, 80);
|
||||
private static final Color OIA_ALERT = new Color(255, 80, 80);
|
||||
|
||||
public StatusBar() {
|
||||
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
|
||||
setBackground(OIA_BG);
|
||||
setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, new Color(40, 40, 40)));
|
||||
setBackground(ThemeManager.getStatusBarBg());
|
||||
setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, ThemeManager.getStatusBarBorder()));
|
||||
setPreferredSize(new Dimension(800, 22));
|
||||
|
||||
Font oiaFont = new Font(Font.MONOSPACED, Font.PLAIN, 12);
|
||||
|
||||
connectionStatus = createLabel("Not Connected", oiaFont, OIA_DIM);
|
||||
tlsStatus = createLabel("", oiaFont, OIA_FG);
|
||||
luName = createLabel("", oiaFont, OIA_FG);
|
||||
lockStatus = createLabel("", oiaFont, OIA_ALERT);
|
||||
modelInfo = createLabel("", oiaFont, OIA_DIM);
|
||||
cursorPosition = createLabel("001/001", oiaFont, OIA_FG);
|
||||
|
||||
lpButton = new JButton("LightPen: OFF");
|
||||
lpButton.setFont(oiaFont);
|
||||
lpButton.setForeground(OIA_FG);
|
||||
lpButton.setBackground(OIA_BG);
|
||||
lpButton.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
|
||||
lpButton.setFocusable(false);
|
||||
lpButton.addActionListener(e -> {
|
||||
if (terminalPanel != null) {
|
||||
terminalPanel.toggleLightPen();
|
||||
lpButton.setText("LightPen: " + (terminalPanel.isLightPenMode() ? "ON" : "OFF"));
|
||||
}
|
||||
});
|
||||
connectionStatus = createLabel("Not Connected", oiaFont, ThemeManager.getOiaFgDim());
|
||||
tlsStatus = createLabel("", oiaFont, ThemeManager.getOiaFgNormal());
|
||||
luName = createLabel("", oiaFont, ThemeManager.getOiaFgNormal());
|
||||
lockStatus = createLabel("", oiaFont, ThemeManager.getOiaFgAlert());
|
||||
fieldTypeStatus = createLabel("", oiaFont, ThemeManager.getOiaFgDim());
|
||||
codePageInfo = createLabel("", oiaFont, ThemeManager.getOiaFgDim());
|
||||
modelInfo = createLabel("", oiaFont, ThemeManager.getOiaFgDim());
|
||||
cursorPosition = createLabel("001/001 [0000]", oiaFont, ThemeManager.getOiaFgNormal());
|
||||
|
||||
add(Box.createHorizontalStrut(6));
|
||||
add(connectionStatus);
|
||||
add(Box.createHorizontalStrut(10));
|
||||
add(tlsStatus);
|
||||
add(Box.createHorizontalStrut(12));
|
||||
add(Box.createHorizontalStrut(10));
|
||||
add(luName);
|
||||
add(Box.createHorizontalStrut(12));
|
||||
add(lockStatus);
|
||||
add(Box.createHorizontalStrut(12));
|
||||
add(lpButton);
|
||||
add(Box.createHorizontalStrut(10));
|
||||
add(fieldTypeStatus);
|
||||
add(Box.createHorizontalGlue());
|
||||
add(codePageInfo);
|
||||
add(Box.createHorizontalStrut(12));
|
||||
add(modelInfo);
|
||||
add(Box.createHorizontalStrut(12));
|
||||
add(cursorPosition);
|
||||
@@ -86,53 +72,67 @@ public class StatusBar extends JPanel {
|
||||
public void setClient(Telnet3270Client client, TerminalPanel terminalPanel) {
|
||||
this.client = client;
|
||||
this.terminalPanel = terminalPanel;
|
||||
if (terminalPanel != null) {
|
||||
terminalPanel.setOnLightPenToggle(this::updateStatus);
|
||||
}
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
public void applyTheme(UITheme theme) {
|
||||
setBackground(ThemeManager.getStatusBarBg(theme));
|
||||
setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, ThemeManager.getStatusBarBorder(theme)));
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
public void updateStatus() {
|
||||
if (client == null)
|
||||
UITheme theme = ThemeManager.getTheme();
|
||||
if (client == null) {
|
||||
connectionStatus.setText("Not Connected");
|
||||
connectionStatus.setForeground(ThemeManager.getOiaFgDim(theme));
|
||||
tlsStatus.setText("");
|
||||
luName.setText("");
|
||||
lockStatus.setText("");
|
||||
fieldTypeStatus.setText("");
|
||||
codePageInfo.setText("");
|
||||
modelInfo.setText("");
|
||||
cursorPosition.setText("001/001");
|
||||
return;
|
||||
}
|
||||
|
||||
// Connection state
|
||||
ConnectionState state = client.getConnectionState();
|
||||
switch (state) {
|
||||
case NOT_CONNECTED:
|
||||
connectionStatus.setText("Not Connected");
|
||||
connectionStatus.setForeground(OIA_DIM);
|
||||
connectionStatus.setForeground(ThemeManager.getOiaFgDim(theme));
|
||||
break;
|
||||
case TCP_PENDING:
|
||||
case TELNET_PENDING:
|
||||
connectionStatus.setText("Connecting...");
|
||||
connectionStatus.setForeground(OIA_ALERT);
|
||||
connectionStatus.setForeground(ThemeManager.getOiaFgAlert(theme));
|
||||
break;
|
||||
case CONNECTED_3270:
|
||||
connectionStatus.setText("TN3270");
|
||||
connectionStatus.setForeground(OIA_FG);
|
||||
connectionStatus.setForeground(ThemeManager.getOiaStatusSysAvail());
|
||||
break;
|
||||
case CONNECTED_TN3270E:
|
||||
connectionStatus.setText("TN3270E");
|
||||
connectionStatus.setForeground(OIA_FG);
|
||||
connectionStatus.setForeground(ThemeManager.getOiaStatusSysAvail());
|
||||
break;
|
||||
case CONNECTED_SSCP:
|
||||
connectionStatus.setText("SSCP-LU");
|
||||
connectionStatus.setForeground(OIA_FG);
|
||||
connectionStatus.setForeground(ThemeManager.getOiaStatusSysAvail());
|
||||
break;
|
||||
case CONNECTED_NVT:
|
||||
case CONNECTED_NVT_CHAR:
|
||||
case CONNECTED_E_NVT:
|
||||
connectionStatus.setText("NVT");
|
||||
connectionStatus.setForeground(OIA_FG);
|
||||
connectionStatus.setForeground(ThemeManager.getOiaStatusSysAvail());
|
||||
break;
|
||||
case CONNECTED_UNBOUND:
|
||||
connectionStatus.setText("Unbound");
|
||||
connectionStatus.setForeground(new Color(255, 255, 80));
|
||||
connectionStatus.setForeground(ThemeManager.getOiaAttention());
|
||||
break;
|
||||
default:
|
||||
connectionStatus.setText(state.name());
|
||||
connectionStatus.setForeground(OIA_DIM);
|
||||
connectionStatus.setForeground(ThemeManager.getOiaFgDim(theme));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -144,11 +144,11 @@ public class StatusBar extends JPanel {
|
||||
String protocol = session != null ? session.getProtocol() : "TLS";
|
||||
if (verified) {
|
||||
tlsStatus.setText("🔒 TLS");
|
||||
tlsStatus.setForeground(OIA_FG);
|
||||
tlsStatus.setForeground(ThemeManager.getOiaStatusSysAvail());
|
||||
tlsStatus.setToolTipText(protocol + " / " + cipher + " (Verified)");
|
||||
} else {
|
||||
tlsStatus.setText("🔓 TLS (Unverified)");
|
||||
tlsStatus.setForeground(new Color(255, 180, 80));
|
||||
tlsStatus.setForeground(ThemeManager.getOiaAttention());
|
||||
tlsStatus.setToolTipText(protocol + " / " + cipher + " (Verification Bypassed)");
|
||||
}
|
||||
} else {
|
||||
@@ -156,37 +156,95 @@ public class StatusBar extends JPanel {
|
||||
tlsStatus.setToolTipText(null);
|
||||
}
|
||||
|
||||
// LU name
|
||||
// LU Name
|
||||
String lu = "";
|
||||
if (client.getConnectionState().isTn3270e()) {
|
||||
// lu would come from FSM
|
||||
if (client.getTelnetFSM() != null && client.getTelnetFSM().getConnectedLu() != null && !client.getTelnetFSM().getConnectedLu().isEmpty()) {
|
||||
lu = "LU:" + client.getTelnetFSM().getConnectedLu();
|
||||
} else if (client.getConfig().getLuName() != null && !client.getConfig().getLuName().isEmpty()) {
|
||||
lu = "LU:" + client.getConfig().getLuName();
|
||||
}
|
||||
luName.setText(lu);
|
||||
luName.setForeground(ThemeManager.getOiaStatusSysAvail());
|
||||
|
||||
// Lock status
|
||||
if (client.getInputProcessor().isKeyboardLocked()) {
|
||||
lockStatus.setText("X SYSTEM");
|
||||
lockStatus.setForeground(OIA_ALERT);
|
||||
// Lock / Inhibit status
|
||||
int inhibit = client.getOIA().getInputInhibited();
|
||||
if (inhibit != ECLConstants.INHIBIT_NOT_INHIBITED) {
|
||||
Color lockFg = ThemeManager.getOiaInputInhibited(); // White (oII)
|
||||
switch (inhibit) {
|
||||
case ECLConstants.INHIBIT_SYSTEM_LOCK:
|
||||
lockStatus.setText("X SYSTEM");
|
||||
lockFg = ThemeManager.getOiaInputInhibited(); // White (oII)
|
||||
break;
|
||||
case ECLConstants.INHIBIT_COMM_CHECK:
|
||||
lockStatus.setText("X COMM");
|
||||
lockFg = ThemeManager.getOiaCommCheck(); // Red (oEI)
|
||||
break;
|
||||
case ECLConstants.INHIBIT_NUMERIC_ONLY:
|
||||
lockStatus.setText("X NUM");
|
||||
lockFg = ThemeManager.getOiaAttention(); // Yellow (oAI)
|
||||
break;
|
||||
case ECLConstants.INHIBIT_PROTECTED_FIELD:
|
||||
lockStatus.setText("X PROT");
|
||||
lockFg = ThemeManager.getOiaInputInhibited(); // White (oII)
|
||||
break;
|
||||
case ECLConstants.INHIBIT_OVERFLOW:
|
||||
lockStatus.setText("X >");
|
||||
lockFg = ThemeManager.getOiaAttention(); // Yellow (oAI)
|
||||
break;
|
||||
case ECLConstants.INHIBIT_OPERATOR_DUE:
|
||||
lockStatus.setText("X OP");
|
||||
lockFg = ThemeManager.getOiaAttention(); // Yellow (oAI)
|
||||
break;
|
||||
default:
|
||||
lockStatus.setText("X LOCKED");
|
||||
lockFg = ThemeManager.getOiaInputInhibited(); // White (oII)
|
||||
break;
|
||||
}
|
||||
lockStatus.setForeground(lockFg);
|
||||
} else if (client.getInputProcessor().isInsertMode()) {
|
||||
lockStatus.setText("INSERT");
|
||||
lockStatus.setForeground(OIA_FG);
|
||||
lockStatus.setForeground(ThemeManager.getOiaStatusSysAvail());
|
||||
} else {
|
||||
lockStatus.setText("");
|
||||
}
|
||||
|
||||
// Model info
|
||||
modelInfo.setText(client.getConfig().getModel().getTerminalType());
|
||||
// Field status: Numeric vs Alphanumeric
|
||||
if (state.isFullSession() && client.getScreenBuffer().isFormatted()) {
|
||||
if (client.getOIA().isNumeric()) {
|
||||
fieldTypeStatus.setText("NUM");
|
||||
fieldTypeStatus.setForeground(ThemeManager.getOiaFgWarn(theme));
|
||||
} else {
|
||||
fieldTypeStatus.setText("ALPHA");
|
||||
fieldTypeStatus.setForeground(ThemeManager.getOiaFgDim(theme));
|
||||
}
|
||||
} else {
|
||||
fieldTypeStatus.setText("");
|
||||
}
|
||||
|
||||
// Cursor position
|
||||
// Active Code Page
|
||||
String cp = client.getCodePage();
|
||||
codePageInfo.setText(cp != null ? "CP" + cp : "");
|
||||
codePageInfo.setForeground(ThemeManager.getOiaFgDim(theme));
|
||||
codePageInfo.setToolTipText("Active EBCDIC Code Page: CP" + cp);
|
||||
|
||||
// Model & Dimensions info
|
||||
ScreenBuffer sb = client.getScreenBuffer();
|
||||
int rows = sb.getDisplayRows();
|
||||
int cols = sb.getDisplayCols();
|
||||
String modelName;
|
||||
if (client.getConfig().isDynamicModel() || (client.getConfig().getModel() != null && client.getConfig().getModel().isDynamic())) {
|
||||
modelName = "IBM-DYNAMIC [" + rows + "x" + cols + "]";
|
||||
} else {
|
||||
modelName = client.getConfig().getModel().name().replace('_', '-') + " [" + rows + "x" + cols + "]";
|
||||
}
|
||||
modelInfo.setText(modelName);
|
||||
modelInfo.setForeground(ThemeManager.getOiaFgDim(theme));
|
||||
|
||||
// Cursor position and buffer address
|
||||
int curAddr = sb.getCursorAddress();
|
||||
int row = sb.getCursorRow() + 1;
|
||||
int col = sb.getCursorCol() + 1;
|
||||
cursorPosition.setText(String.format("%03d/%03d", row, col));
|
||||
|
||||
// Light Pen indicator — sync with actual state
|
||||
if (terminalPanel != null) {
|
||||
lpButton.setText("LightPen: " + (terminalPanel.isLightPenMode() ? "ON" : "OFF"));
|
||||
lpButton.setForeground(terminalPanel.isLightPenMode() ? new Color(255, 255, 80) : OIA_FG);
|
||||
}
|
||||
cursorPosition.setText(String.format("%03d/%03d [%04d]", row, col, curAddr));
|
||||
cursorPosition.setForeground(ThemeManager.getOiaFgNormal(theme));
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,968 @@
|
||||
package haus.nightmare.j3270.ui;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.LineBorder;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import javax.swing.plaf.basic.BasicButtonUI;
|
||||
import javax.swing.plaf.basic.BasicMenuBarUI;
|
||||
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||
import javax.swing.plaf.basic.BasicMenuUI;
|
||||
import javax.swing.plaf.basic.BasicPopupMenuUI;
|
||||
import javax.swing.plaf.basic.BasicTabbedPaneUI;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.JTableHeader;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Centralized theme manager for j3270 Java desktop UI.
|
||||
* Provides high-contrast, platform-independent Light and Dark theme palettes,
|
||||
* custom Swing UI delegates, and recursive styling helpers for all dialogs and menus.
|
||||
*/
|
||||
public final class ThemeManager {
|
||||
|
||||
private static UITheme currentTheme = UITheme.DARK;
|
||||
private static final List<Consumer<UITheme>> themeChangeListeners = new ArrayList<>();
|
||||
|
||||
private ThemeManager() {}
|
||||
|
||||
public static UITheme getTheme() {
|
||||
return currentTheme;
|
||||
}
|
||||
|
||||
public static boolean isDark() {
|
||||
return currentTheme == UITheme.DARK;
|
||||
}
|
||||
|
||||
public static boolean isLight() {
|
||||
return currentTheme == UITheme.LIGHT;
|
||||
}
|
||||
|
||||
public static void setTheme(UITheme theme) {
|
||||
if (theme == null) theme = UITheme.DARK;
|
||||
if (currentTheme != theme) {
|
||||
currentTheme = theme;
|
||||
applyUIManagerDefaults(theme);
|
||||
for (Consumer<UITheme> listener : new ArrayList<>(themeChangeListeners)) {
|
||||
try {
|
||||
listener.accept(theme);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void addThemeChangeListener(Consumer<UITheme> listener) {
|
||||
if (listener != null && !themeChangeListeners.contains(listener)) {
|
||||
themeChangeListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeThemeChangeListener(Consumer<UITheme> listener) {
|
||||
themeChangeListeners.remove(listener);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Color Palette Tokens
|
||||
// =========================================================================
|
||||
|
||||
public static Color getBgMain() { return getBgMain(currentTheme); }
|
||||
public static Color getBgMain(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(28, 28, 28) : new Color(242, 242, 244);
|
||||
}
|
||||
|
||||
public static Color getBgPanel() { return getBgPanel(currentTheme); }
|
||||
public static Color getBgPanel(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(36, 36, 36) : new Color(250, 250, 252);
|
||||
}
|
||||
|
||||
public static Color getBgComponent() { return getBgComponent(currentTheme); }
|
||||
public static Color getBgComponent(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(46, 46, 46) : Color.WHITE;
|
||||
}
|
||||
|
||||
public static Color getBgComponentLighter() { return getBgComponentLighter(currentTheme); }
|
||||
public static Color getBgComponentLighter(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(56, 56, 56) : new Color(236, 236, 238);
|
||||
}
|
||||
|
||||
public static Color getFgMain() { return getFgMain(currentTheme); }
|
||||
public static Color getFgMain(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(238, 238, 238) : new Color(24, 24, 24);
|
||||
}
|
||||
|
||||
public static Color getFgMuted() { return getFgMuted(currentTheme); }
|
||||
public static Color getFgMuted(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(175, 175, 175) : new Color(90, 90, 90);
|
||||
}
|
||||
|
||||
public static Color getFgDisabled() { return getFgDisabled(currentTheme); }
|
||||
public static Color getFgDisabled(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(110, 110, 110) : new Color(160, 160, 160);
|
||||
}
|
||||
|
||||
public static Color getBorder() { return getBorder(currentTheme); }
|
||||
public static Color getBorder(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(65, 65, 65) : new Color(205, 205, 205);
|
||||
}
|
||||
public static Color getBorderColor() { return getBorder(currentTheme); }
|
||||
public static Color getBorderColor(UITheme t) { return getBorder(t); }
|
||||
|
||||
public static Color getBorderFocused() { return getBorderFocused(currentTheme); }
|
||||
public static Color getBorderFocused(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(80, 140, 230) : new Color(50, 115, 220);
|
||||
}
|
||||
|
||||
public static Color getSelectionBg() { return getSelectionBg(currentTheme); }
|
||||
public static Color getSelectionBg(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(55, 105, 180) : new Color(60, 125, 215);
|
||||
}
|
||||
|
||||
public static Color getSelectionFg() { return getSelectionFg(currentTheme); }
|
||||
public static Color getSelectionFg(UITheme t) {
|
||||
return Color.WHITE;
|
||||
}
|
||||
|
||||
public static Color getMenuBarBg() { return getMenuBarBg(currentTheme); }
|
||||
public static Color getMenuBarBg(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(26, 26, 26) : new Color(240, 240, 242);
|
||||
}
|
||||
|
||||
public static Color getMenuBarFg() { return getMenuBarFg(currentTheme); }
|
||||
public static Color getMenuBarFg(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(235, 235, 235) : new Color(25, 25, 25);
|
||||
}
|
||||
|
||||
public static Color getMenuPopupBg() { return getMenuPopupBg(currentTheme); }
|
||||
public static Color getMenuPopupBg(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(38, 38, 38) : Color.WHITE;
|
||||
}
|
||||
|
||||
public static Color getMenuItemFg() { return getMenuItemFg(currentTheme); }
|
||||
public static Color getMenuItemFg(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(230, 230, 230) : new Color(30, 30, 30);
|
||||
}
|
||||
|
||||
public static Color getMenuItemHoverBg() { return getMenuItemHoverBg(currentTheme); }
|
||||
public static Color getMenuItemHoverBg(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(55, 95, 160) : new Color(220, 232, 250);
|
||||
}
|
||||
|
||||
public static Color getMenuItemHoverFg() { return getMenuItemHoverFg(currentTheme); }
|
||||
public static Color getMenuItemHoverFg(UITheme t) {
|
||||
return t == UITheme.DARK ? Color.WHITE : new Color(15, 15, 15);
|
||||
}
|
||||
|
||||
public static Color getTabBg() { return getTabBg(currentTheme); }
|
||||
public static Color getTabBg(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(34, 34, 34) : new Color(230, 230, 232);
|
||||
}
|
||||
|
||||
public static Color getTabFg() { return getTabFg(currentTheme); }
|
||||
public static Color getTabFg(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(185, 185, 185) : new Color(75, 75, 75);
|
||||
}
|
||||
|
||||
public static Color getTabSelectedBg() { return getTabSelectedBg(currentTheme); }
|
||||
public static Color getTabSelectedBg(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(50, 50, 50) : Color.WHITE;
|
||||
}
|
||||
|
||||
public static Color getTabSelectedFg() { return getTabSelectedFg(currentTheme); }
|
||||
public static Color getTabSelectedFg(UITheme t) {
|
||||
return t == UITheme.DARK ? Color.WHITE : new Color(20, 20, 20);
|
||||
}
|
||||
|
||||
public static Color getTableHeaderBg() { return getTableHeaderBg(currentTheme); }
|
||||
public static Color getTableHeaderBg(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(46, 46, 46) : new Color(232, 232, 236);
|
||||
}
|
||||
|
||||
public static Color getTableHeaderFg() { return getTableHeaderFg(currentTheme); }
|
||||
public static Color getTableHeaderFg(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(235, 235, 235) : new Color(30, 30, 30);
|
||||
}
|
||||
|
||||
public static Color getTableRowEven() { return getTableRowEven(currentTheme); }
|
||||
public static Color getTableRowEven(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(38, 38, 38) : Color.WHITE;
|
||||
}
|
||||
|
||||
public static Color getTableRowOdd() { return getTableRowOdd(currentTheme); }
|
||||
public static Color getTableRowOdd(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(33, 33, 33) : new Color(246, 247, 249);
|
||||
}
|
||||
|
||||
public static Color getTableGrid() { return getTableGrid(currentTheme); }
|
||||
public static Color getTableGrid(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(55, 55, 55) : new Color(225, 225, 225);
|
||||
}
|
||||
|
||||
public static Color getCodeAreaBg() { return getCodeAreaBg(currentTheme); }
|
||||
public static Color getCodeAreaBg(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(22, 22, 22) : new Color(252, 252, 252);
|
||||
}
|
||||
|
||||
public static Color getCodeAreaFg() { return getCodeAreaFg(currentTheme); }
|
||||
public static Color getCodeAreaFg(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(80, 230, 80) : new Color(0, 120, 0);
|
||||
}
|
||||
|
||||
public static Color getStatusBarBg() { return getStatusBarBg(currentTheme); }
|
||||
public static Color getStatusBarBg(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(20, 20, 20) : new Color(235, 235, 238);
|
||||
}
|
||||
|
||||
public static Color getStatusBarBorder() { return getStatusBarBorder(currentTheme); }
|
||||
public static Color getStatusBarBorder(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(42, 42, 42) : new Color(210, 210, 215);
|
||||
}
|
||||
|
||||
public static Color getOiaFgNormal() { return getOiaFgNormal(currentTheme); }
|
||||
public static Color getOiaFgNormal(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(50, 205, 50) : new Color(0, 130, 0);
|
||||
}
|
||||
|
||||
public static Color getOiaFgDim() { return getOiaFgDim(currentTheme); }
|
||||
public static Color getOiaFgDim(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(110, 110, 110) : new Color(90, 90, 90);
|
||||
}
|
||||
|
||||
public static Color getOiaFgAlert() { return getOiaFgAlert(currentTheme); }
|
||||
public static Color getOiaFgAlert(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(255, 80, 80) : new Color(190, 20, 20);
|
||||
}
|
||||
|
||||
public static Color getOiaFgWarn() { return getOiaFgWarn(currentTheme); }
|
||||
public static Color getOiaFgWarn(UITheme t) {
|
||||
return t == UITheme.DARK ? new Color(255, 190, 70) : new Color(180, 100, 0);
|
||||
}
|
||||
|
||||
// Host On-Demand OIA Category Colors (oSI, oII, oAI, oEI, oOB)
|
||||
public static final Color HOD_OIA_STATUS_SYS_AVAIL = new Color(120, 144, 240); // oSI: CUSTOMBLUE
|
||||
public static final Color HOD_OIA_INPUT_INHIBITED = new Color(255, 255, 255); // oII: White
|
||||
public static final Color HOD_OIA_ATTENTION_WARN = new Color(255, 255, 0); // oAI: Yellow
|
||||
public static final Color HOD_OIA_COMM_CHECK_ERROR = new Color(255, 0, 0); // oEI: Red
|
||||
public static final Color HOD_OIA_BG_BLACK = new Color(0, 0, 0); // oOB: Black
|
||||
|
||||
public static Color getOiaStatusSysAvail() { return HOD_OIA_STATUS_SYS_AVAIL; }
|
||||
public static Color getOiaInputInhibited() { return HOD_OIA_INPUT_INHIBITED; }
|
||||
public static Color getOiaAttention() { return HOD_OIA_ATTENTION_WARN; }
|
||||
public static Color getOiaCommCheck() { return HOD_OIA_COMM_CHECK_ERROR; }
|
||||
public static Color getOiaBackground() { return HOD_OIA_BG_BLACK; }
|
||||
|
||||
// =========================================================================
|
||||
// Button Variants & Color helpers
|
||||
// =========================================================================
|
||||
|
||||
public enum ButtonVariant {
|
||||
DEFAULT,
|
||||
PRIMARY,
|
||||
CANCEL,
|
||||
DANGER,
|
||||
ACCENT
|
||||
}
|
||||
|
||||
public static Color getButtonBg(ButtonVariant variant) {
|
||||
return getButtonBg(variant, currentTheme, false, false);
|
||||
}
|
||||
|
||||
public static Color getButtonBg(ButtonVariant variant, UITheme t) {
|
||||
return getButtonBg(variant, t, false, false);
|
||||
}
|
||||
|
||||
public static Color getButtonBg(ButtonVariant variant, UITheme t, boolean hover, boolean pressed) {
|
||||
switch (variant) {
|
||||
case PRIMARY:
|
||||
if (pressed) return new Color(25, 105, 35);
|
||||
if (hover) return new Color(38, 145, 48);
|
||||
return new Color(32, 128, 42);
|
||||
|
||||
case DANGER:
|
||||
if (pressed) return new Color(125, 30, 30);
|
||||
if (hover) return new Color(165, 45, 45);
|
||||
return new Color(145, 38, 38);
|
||||
|
||||
case ACCENT:
|
||||
if (pressed) return new Color(35, 80, 145);
|
||||
if (hover) return new Color(55, 115, 195);
|
||||
return new Color(45, 100, 175);
|
||||
|
||||
case CANCEL:
|
||||
case DEFAULT:
|
||||
default:
|
||||
if (t == UITheme.DARK) {
|
||||
if (pressed) return new Color(42, 42, 42);
|
||||
if (hover) return new Color(68, 68, 68);
|
||||
return new Color(56, 56, 56);
|
||||
} else {
|
||||
if (pressed) return new Color(210, 210, 212);
|
||||
if (hover) return new Color(228, 228, 232);
|
||||
return new Color(238, 238, 240);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Color getButtonFg(ButtonVariant variant, UITheme t) {
|
||||
switch (variant) {
|
||||
case PRIMARY:
|
||||
case DANGER:
|
||||
case ACCENT:
|
||||
return Color.WHITE;
|
||||
case CANCEL:
|
||||
case DEFAULT:
|
||||
default:
|
||||
return t == UITheme.DARK ? new Color(235, 235, 235) : new Color(30, 30, 30);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// UIManager Defaults Configuration
|
||||
// =========================================================================
|
||||
|
||||
public static void applyUIManagerDefaults(UITheme theme) {
|
||||
Color bgMain = getBgMain(theme);
|
||||
Color bgPanel = getBgPanel(theme);
|
||||
Color bgComp = getBgComponent(theme);
|
||||
Color fgMain = getFgMain(theme);
|
||||
Color fgMuted = getFgMuted(theme);
|
||||
Color border = getBorder(theme);
|
||||
Color selBg = getSelectionBg(theme);
|
||||
Color selFg = getSelectionFg(theme);
|
||||
|
||||
UIManager.put("Panel.background", bgPanel);
|
||||
UIManager.put("Panel.foreground", fgMain);
|
||||
UIManager.put("Label.foreground", fgMain);
|
||||
|
||||
UIManager.put("TextField.background", bgComp);
|
||||
UIManager.put("TextField.foreground", fgMain);
|
||||
UIManager.put("TextField.caretForeground", fgMain);
|
||||
UIManager.put("TextField.selectionBackground", selBg);
|
||||
UIManager.put("TextField.selectionForeground", selFg);
|
||||
|
||||
UIManager.put("TextArea.background", bgComp);
|
||||
UIManager.put("TextArea.foreground", fgMain);
|
||||
UIManager.put("TextArea.caretForeground", fgMain);
|
||||
UIManager.put("TextArea.selectionBackground", selBg);
|
||||
UIManager.put("TextArea.selectionForeground", selFg);
|
||||
|
||||
UIManager.put("Button.background", getBgComponentLighter(theme));
|
||||
UIManager.put("Button.foreground", fgMain);
|
||||
UIManager.put("Button.select", selBg);
|
||||
|
||||
UIManager.put("ComboBox.background", bgComp);
|
||||
UIManager.put("ComboBox.foreground", fgMain);
|
||||
UIManager.put("ComboBox.selectionBackground", selBg);
|
||||
UIManager.put("ComboBox.selectionForeground", selFg);
|
||||
|
||||
UIManager.put("Table.background", bgComp);
|
||||
UIManager.put("Table.foreground", fgMain);
|
||||
UIManager.put("Table.selectionBackground", selBg);
|
||||
UIManager.put("Table.selectionForeground", selFg);
|
||||
UIManager.put("Table.gridColor", getTableGrid(theme));
|
||||
|
||||
UIManager.put("TableHeader.background", getTableHeaderBg(theme));
|
||||
UIManager.put("TableHeader.foreground", getTableHeaderFg(theme));
|
||||
|
||||
UIManager.put("ScrollPane.background", bgPanel);
|
||||
UIManager.put("Viewport.background", bgComp);
|
||||
|
||||
UIManager.put("MenuBar.background", getMenuBarBg(theme));
|
||||
UIManager.put("MenuBar.foreground", getMenuBarFg(theme));
|
||||
UIManager.put("Menu.background", getMenuBarBg(theme));
|
||||
UIManager.put("Menu.foreground", getMenuBarFg(theme));
|
||||
UIManager.put("Menu.selectionBackground", selBg);
|
||||
UIManager.put("Menu.selectionForeground", selFg);
|
||||
|
||||
UIManager.put("PopupMenu.background", getMenuPopupBg(theme));
|
||||
UIManager.put("PopupMenu.foreground", getMenuItemFg(theme));
|
||||
|
||||
UIManager.put("MenuItem.background", getMenuPopupBg(theme));
|
||||
UIManager.put("MenuItem.foreground", getMenuItemFg(theme));
|
||||
UIManager.put("MenuItem.selectionBackground", selBg);
|
||||
UIManager.put("MenuItem.selectionForeground", selFg);
|
||||
|
||||
UIManager.put("CheckBoxMenuItem.background", getMenuPopupBg(theme));
|
||||
UIManager.put("CheckBoxMenuItem.foreground", getMenuItemFg(theme));
|
||||
UIManager.put("CheckBoxMenuItem.selectionBackground", selBg);
|
||||
UIManager.put("CheckBoxMenuItem.selectionForeground", selFg);
|
||||
|
||||
UIManager.put("RadioButtonMenuItem.background", getMenuPopupBg(theme));
|
||||
UIManager.put("RadioButtonMenuItem.foreground", getMenuItemFg(theme));
|
||||
UIManager.put("RadioButtonMenuItem.selectionBackground", selBg);
|
||||
UIManager.put("RadioButtonMenuItem.selectionForeground", selFg);
|
||||
|
||||
UIManager.put("Separator.background", border);
|
||||
UIManager.put("Separator.foreground", border);
|
||||
|
||||
UIManager.put("TabbedPane.background", getTabBg(theme));
|
||||
UIManager.put("TabbedPane.foreground", getTabFg(theme));
|
||||
UIManager.put("TabbedPane.selected", getTabSelectedBg(theme));
|
||||
UIManager.put("TabbedPane.selectHighlight", getTabSelectedBg(theme));
|
||||
|
||||
UIManager.put("CheckBox.background", bgPanel);
|
||||
UIManager.put("CheckBox.foreground", fgMain);
|
||||
UIManager.put("RadioButton.background", bgPanel);
|
||||
UIManager.put("RadioButton.foreground", fgMain);
|
||||
|
||||
UIManager.put("TitledBorder.titleColor", fgMain);
|
||||
UIManager.put("OptionPane.background", bgPanel);
|
||||
UIManager.put("OptionPane.messageForeground", fgMain);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Component Styling & Custom UI Delegates
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Styles a button with custom rendering, ensuring consistent high contrast
|
||||
* and proper background color across Windows, macOS, and Linux.
|
||||
*/
|
||||
public static JButton styleButton(JButton button, ButtonVariant variant) {
|
||||
if (button == null) return null;
|
||||
button.setUI(new StyledButtonUI(variant));
|
||||
button.setFocusPainted(false);
|
||||
button.setOpaque(false);
|
||||
button.setContentAreaFilled(false);
|
||||
button.setBorder(BorderFactory.createEmptyBorder(6, 14, 6, 14));
|
||||
button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
return button;
|
||||
}
|
||||
|
||||
public static JButton createStyledButton(String text, ButtonVariant variant, Runnable action) {
|
||||
JButton btn = new JButton(text);
|
||||
styleButton(btn, variant);
|
||||
if (action != null) {
|
||||
btn.addActionListener(e -> action.run());
|
||||
}
|
||||
return btn;
|
||||
}
|
||||
|
||||
public static JTextField styleTextField(JTextField field) {
|
||||
if (field == null) return null;
|
||||
field.setBackground(getBgComponent());
|
||||
field.setForeground(getFgMain());
|
||||
field.setCaretColor(getFgMain());
|
||||
field.setSelectionColor(getSelectionBg());
|
||||
field.setSelectedTextColor(getSelectionFg());
|
||||
field.setBorder(BorderFactory.createCompoundBorder(
|
||||
new LineBorder(getBorder(), 1, true),
|
||||
new EmptyBorder(4, 8, 4, 8)));
|
||||
return field;
|
||||
}
|
||||
|
||||
public static JTextArea styleTextArea(JTextArea area) {
|
||||
if (area == null) return null;
|
||||
area.setBackground(getCodeAreaBg());
|
||||
area.setForeground(getCodeAreaFg());
|
||||
area.setCaretColor(getFgMain());
|
||||
area.setSelectionColor(getSelectionBg());
|
||||
area.setSelectedTextColor(getSelectionFg());
|
||||
return area;
|
||||
}
|
||||
|
||||
public static JComboBox<?> styleComboBox(JComboBox<?> box) {
|
||||
if (box == null) return null;
|
||||
box.setBackground(getBgComponent());
|
||||
box.setForeground(getFgMain());
|
||||
box.setRenderer(new DefaultListCellRenderer() {
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||
setBorder(new EmptyBorder(4, 8, 4, 8));
|
||||
if (isSelected) {
|
||||
setBackground(getSelectionBg());
|
||||
setForeground(getSelectionFg());
|
||||
} else {
|
||||
setBackground(getBgComponent());
|
||||
setForeground(getFgMain());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
});
|
||||
return box;
|
||||
}
|
||||
|
||||
public static JTable styleTable(JTable table) {
|
||||
if (table == null) return null;
|
||||
table.setBackground(getBgComponent());
|
||||
table.setForeground(getFgMain());
|
||||
table.setSelectionBackground(getSelectionBg());
|
||||
table.setSelectionForeground(getSelectionFg());
|
||||
table.setGridColor(getTableGrid());
|
||||
table.setRowHeight(24);
|
||||
|
||||
table.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable tbl, Object val, boolean isSel, boolean hasFocus, int row, int col) {
|
||||
super.getTableCellRendererComponent(tbl, val, isSel, hasFocus, row, col);
|
||||
setBorder(new EmptyBorder(2, 6, 2, 6));
|
||||
if (isSel) {
|
||||
setBackground(getSelectionBg());
|
||||
setForeground(getSelectionFg());
|
||||
} else {
|
||||
setBackground(row % 2 == 0 ? getTableRowEven() : getTableRowOdd());
|
||||
setForeground(getFgMain());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
JTableHeader header = table.getTableHeader();
|
||||
if (header != null) {
|
||||
header.setBackground(getTableHeaderBg());
|
||||
header.setForeground(getTableHeaderFg());
|
||||
header.setFont(header.getFont().deriveFont(Font.BOLD));
|
||||
header.setDefaultRenderer(new DefaultTableCellRenderer() {
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable tbl, Object val, boolean isSel, boolean hasFocus, int row, int col) {
|
||||
super.getTableCellRendererComponent(tbl, val, isSel, hasFocus, row, col);
|
||||
setBackground(getTableHeaderBg());
|
||||
setForeground(getTableHeaderFg());
|
||||
setFont(getFont().deriveFont(Font.BOLD));
|
||||
setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createMatteBorder(0, 0, 1, 1, getBorderColor()),
|
||||
new EmptyBorder(4, 6, 4, 6)));
|
||||
return this;
|
||||
}
|
||||
});
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
public static JTabbedPane styleTabbedPane(JTabbedPane tp) {
|
||||
if (tp == null) return null;
|
||||
tp.setUI(new StyledTabbedPaneUI());
|
||||
tp.setBackground(getBgMain());
|
||||
tp.setForeground(getFgMain());
|
||||
return tp;
|
||||
}
|
||||
|
||||
public static JMenuBar styleMenuBar(JMenuBar bar) {
|
||||
if (bar == null) return null;
|
||||
bar.setUI(new StyledMenuBarUI());
|
||||
bar.setBackground(getMenuBarBg());
|
||||
bar.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, getBorder()));
|
||||
return bar;
|
||||
}
|
||||
|
||||
public static JMenu styleMenu(JMenu menu) {
|
||||
if (menu == null) return null;
|
||||
menu.setUI(new StyledMenuUI());
|
||||
menu.setForeground(getMenuBarFg());
|
||||
menu.setBackground(getMenuBarBg());
|
||||
menu.setOpaque(false);
|
||||
return menu;
|
||||
}
|
||||
|
||||
public static JMenuItem styleMenuItem(JMenuItem item) {
|
||||
if (item == null) return null;
|
||||
item.setUI(new StyledMenuItemUI());
|
||||
item.setBackground(getMenuPopupBg());
|
||||
item.setForeground(getMenuItemFg());
|
||||
return item;
|
||||
}
|
||||
|
||||
public static JPopupMenu stylePopupMenu(JPopupMenu popup) {
|
||||
if (popup == null) return null;
|
||||
popup.setUI(new StyledPopupMenuUI());
|
||||
popup.setBackground(getMenuPopupBg());
|
||||
popup.setBorder(BorderFactory.createCompoundBorder(
|
||||
new LineBorder(getBorder(), 1),
|
||||
new EmptyBorder(4, 0, 4, 0)));
|
||||
return popup;
|
||||
}
|
||||
|
||||
public static JScrollPane styleScrollPane(JScrollPane sp) {
|
||||
if (sp == null) return null;
|
||||
sp.setBackground(getBgPanel());
|
||||
if (sp.getViewport() != null) {
|
||||
sp.getViewport().setBackground(getBgComponent());
|
||||
}
|
||||
sp.setBorder(new LineBorder(getBorder(), 1));
|
||||
return sp;
|
||||
}
|
||||
|
||||
public static JSpinner styleSpinner(JSpinner sp) {
|
||||
if (sp == null) return null;
|
||||
sp.setBackground(getBgComponent());
|
||||
sp.setForeground(getFgMain());
|
||||
sp.setBorder(new LineBorder(getBorder(), 1));
|
||||
JComponent editor = sp.getEditor();
|
||||
if (editor instanceof JSpinner.DefaultEditor) {
|
||||
JTextField tf = ((JSpinner.DefaultEditor) editor).getTextField();
|
||||
tf.setBackground(getBgComponent());
|
||||
tf.setForeground(getFgMain());
|
||||
tf.setCaretColor(getFgMain());
|
||||
tf.setBorder(new EmptyBorder(2, 4, 2, 4));
|
||||
}
|
||||
return sp;
|
||||
}
|
||||
|
||||
public static JCheckBox styleCheckBox(JCheckBox cb) {
|
||||
if (cb == null) return null;
|
||||
cb.setOpaque(false);
|
||||
cb.setForeground(getFgMain());
|
||||
cb.setFocusPainted(false);
|
||||
return cb;
|
||||
}
|
||||
|
||||
public static JRadioButton styleRadioButton(JRadioButton rb) {
|
||||
if (rb == null) return null;
|
||||
rb.setOpaque(false);
|
||||
rb.setForeground(getFgMain());
|
||||
rb.setFocusPainted(false);
|
||||
return rb;
|
||||
}
|
||||
|
||||
public static Border createTitledBorder(String title) {
|
||||
return BorderFactory.createTitledBorder(
|
||||
new LineBorder(getBorder(), 1),
|
||||
title,
|
||||
TitledBorder.DEFAULT_JUSTIFICATION,
|
||||
TitledBorder.DEFAULT_POSITION,
|
||||
new Font(Font.SANS_SERIF, Font.BOLD, 12),
|
||||
getFgMain());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Recursive Component Styling
|
||||
// =========================================================================
|
||||
|
||||
public static void applyTheme(Component comp) {
|
||||
applyTheme(comp, currentTheme);
|
||||
}
|
||||
|
||||
public static void applyTheme(Component comp, UITheme theme) {
|
||||
if (comp == null) return;
|
||||
|
||||
// Skip swatch preview panels whose background is the user-configured color
|
||||
if ("colorSwatch".equals(comp.getName())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JMenuBar) {
|
||||
styleMenuBar((JMenuBar) comp);
|
||||
for (int i = 0; i < ((JMenuBar) comp).getMenuCount(); i++) {
|
||||
applyTheme(((JMenuBar) comp).getMenu(i), theme);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JMenu) {
|
||||
styleMenu((JMenu) comp);
|
||||
JMenu menu = (JMenu) comp;
|
||||
for (int i = 0; i < menu.getItemCount(); i++) {
|
||||
JMenuItem item = menu.getItem(i);
|
||||
if (item != null) applyTheme(item, theme);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JMenuItem) {
|
||||
styleMenuItem((JMenuItem) comp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JPopupMenu) {
|
||||
stylePopupMenu((JPopupMenu) comp);
|
||||
for (Component child : ((JPopupMenu) comp).getComponents()) {
|
||||
applyTheme(child, theme);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JTabbedPane) {
|
||||
styleTabbedPane((JTabbedPane) comp);
|
||||
JTabbedPane tp = (JTabbedPane) comp;
|
||||
for (int i = 0; i < tp.getTabCount(); i++) {
|
||||
applyTheme(tp.getComponentAt(i), theme);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JTable) {
|
||||
styleTable((JTable) comp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JScrollPane) {
|
||||
styleScrollPane((JScrollPane) comp);
|
||||
JScrollPane sp = (JScrollPane) comp;
|
||||
if (sp.getViewport() != null) {
|
||||
applyTheme(sp.getViewport().getView(), theme);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JButton) {
|
||||
JButton btn = (JButton) comp;
|
||||
if (!(btn.getUI() instanceof StyledButtonUI)) {
|
||||
styleButton(btn, ButtonVariant.DEFAULT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JComboBox) {
|
||||
styleComboBox((JComboBox<?>) comp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JSpinner) {
|
||||
styleSpinner((JSpinner) comp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JTextArea) {
|
||||
styleTextArea((JTextArea) comp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JTextField) {
|
||||
styleTextField((JTextField) comp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JCheckBox) {
|
||||
styleCheckBox((JCheckBox) comp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JRadioButton) {
|
||||
styleRadioButton((JRadioButton) comp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JLabel) {
|
||||
comp.setForeground(getFgMain(theme));
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp instanceof JPanel) {
|
||||
JPanel panel = (JPanel) comp;
|
||||
if (panel.isOpaque()) {
|
||||
panel.setBackground(getBgPanel(theme));
|
||||
}
|
||||
panel.setForeground(getFgMain(theme));
|
||||
Border b = panel.getBorder();
|
||||
if (b instanceof TitledBorder) {
|
||||
TitledBorder tb = (TitledBorder) b;
|
||||
tb.setTitleColor(getFgMain(theme));
|
||||
tb.setBorder(new LineBorder(getBorder(theme), 1));
|
||||
}
|
||||
} else if (comp instanceof Container) {
|
||||
comp.setBackground(getBgMain(theme));
|
||||
comp.setForeground(getFgMain(theme));
|
||||
}
|
||||
|
||||
if (comp instanceof Container) {
|
||||
for (Component child : ((Container) comp).getComponents()) {
|
||||
applyTheme(child, theme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void applyThemeToWindow(Window window) {
|
||||
if (window == null) return;
|
||||
window.setBackground(getBgMain());
|
||||
if (window instanceof RootPaneContainer) {
|
||||
RootPaneContainer rpc = (RootPaneContainer) window;
|
||||
if (rpc.getContentPane() != null) {
|
||||
rpc.getContentPane().setBackground(getBgMain());
|
||||
applyTheme(rpc.getContentPane());
|
||||
}
|
||||
if (rpc.getRootPane() != null && rpc.getRootPane().getJMenuBar() != null) {
|
||||
applyTheme(rpc.getRootPane().getJMenuBar());
|
||||
}
|
||||
}
|
||||
window.repaint();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Custom UI Implementations
|
||||
// =========================================================================
|
||||
|
||||
public static class StyledButtonUI extends BasicButtonUI {
|
||||
private final ButtonVariant variant;
|
||||
private boolean hover = false;
|
||||
|
||||
public StyledButtonUI(ButtonVariant variant) {
|
||||
this.variant = variant != null ? variant : ButtonVariant.DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI(JComponent c) {
|
||||
super.installUI(c);
|
||||
AbstractButton b = (AbstractButton) c;
|
||||
b.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
hover = true;
|
||||
b.repaint();
|
||||
}
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
hover = false;
|
||||
b.repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g, JComponent c) {
|
||||
AbstractButton b = (AbstractButton) c;
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
int w = c.getWidth();
|
||||
int h = c.getHeight();
|
||||
|
||||
ButtonModel model = b.getModel();
|
||||
boolean pressed = model.isArmed() && model.isPressed();
|
||||
boolean enabled = b.isEnabled();
|
||||
|
||||
UITheme t = ThemeManager.getTheme();
|
||||
Color bg = enabled ? ThemeManager.getButtonBg(variant, t, hover, pressed) : (t == UITheme.DARK ? new Color(40, 40, 40) : new Color(220, 220, 220));
|
||||
Color fg = enabled ? ThemeManager.getButtonFg(variant, t) : ThemeManager.getFgDisabled(t);
|
||||
|
||||
// Background
|
||||
g2.setColor(bg);
|
||||
g2.fillRoundRect(0, 0, w, h, 6, 6);
|
||||
|
||||
// Border
|
||||
if (variant == ButtonVariant.DEFAULT || variant == ButtonVariant.CANCEL) {
|
||||
g2.setColor(ThemeManager.getBorder(t));
|
||||
g2.drawRoundRect(0, 0, w - 1, h - 1, 6, 6);
|
||||
}
|
||||
|
||||
// Text / Icon
|
||||
FontMetrics fm = g2.getFontMetrics(b.getFont());
|
||||
String text = b.getText();
|
||||
if (text != null && !text.isEmpty()) {
|
||||
g2.setColor(fg);
|
||||
g2.setFont(b.getFont());
|
||||
int tx = (w - fm.stringWidth(text)) / 2;
|
||||
int ty = (h - fm.getHeight()) / 2 + fm.getAscent();
|
||||
g2.drawString(text, tx, ty);
|
||||
}
|
||||
|
||||
g2.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static class StyledMenuBarUI extends BasicMenuBarUI {
|
||||
@Override
|
||||
public void paint(Graphics g, JComponent c) {
|
||||
g.setColor(ThemeManager.getMenuBarBg());
|
||||
g.fillRect(0, 0, c.getWidth(), c.getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
public static class StyledMenuUI extends BasicMenuUI {
|
||||
@Override
|
||||
protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor) {
|
||||
ButtonModel model = menuItem.getModel();
|
||||
if (model.isArmed() || model.isSelected()) {
|
||||
g.setColor(ThemeManager.getMenuItemHoverBg());
|
||||
g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight());
|
||||
} else {
|
||||
g.setColor(ThemeManager.getMenuBarBg());
|
||||
g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect, String text) {
|
||||
ButtonModel model = menuItem.getModel();
|
||||
if (model.isArmed() || model.isSelected()) {
|
||||
g.setColor(ThemeManager.getMenuItemHoverFg());
|
||||
} else {
|
||||
g.setColor(ThemeManager.getMenuBarFg());
|
||||
}
|
||||
super.paintText(g, menuItem, textRect, text);
|
||||
}
|
||||
}
|
||||
|
||||
public static class StyledMenuItemUI extends BasicMenuItemUI {
|
||||
@Override
|
||||
protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor) {
|
||||
ButtonModel model = menuItem.getModel();
|
||||
if (model.isArmed() || model.isSelected()) {
|
||||
g.setColor(ThemeManager.getSelectionBg());
|
||||
g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight());
|
||||
} else {
|
||||
g.setColor(ThemeManager.getMenuPopupBg());
|
||||
g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect, String text) {
|
||||
ButtonModel model = menuItem.getModel();
|
||||
if (model.isArmed() || model.isSelected()) {
|
||||
g.setColor(ThemeManager.getSelectionFg());
|
||||
} else {
|
||||
g.setColor(ThemeManager.getMenuItemFg());
|
||||
}
|
||||
super.paintText(g, menuItem, textRect, text);
|
||||
}
|
||||
}
|
||||
|
||||
public static class StyledPopupMenuUI extends BasicPopupMenuUI {
|
||||
@Override
|
||||
public void paint(Graphics g, JComponent c) {
|
||||
g.setColor(ThemeManager.getMenuPopupBg());
|
||||
g.fillRect(0, 0, c.getWidth(), c.getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
public static class StyledTabbedPaneUI extends BasicTabbedPaneUI {
|
||||
@Override
|
||||
protected void installDefaults() {
|
||||
super.installDefaults();
|
||||
tabInsets = new Insets(6, 16, 6, 16);
|
||||
selectedTabPadInsets = new Insets(2, 2, 2, 2);
|
||||
tabAreaInsets = new Insets(4, 4, 0, 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2.setColor(isSelected ? ThemeManager.getTabSelectedBg() : ThemeManager.getTabBg());
|
||||
g2.fillRoundRect(x, y, w, h + 4, 6, 6);
|
||||
g2.setColor(ThemeManager.getBorder());
|
||||
g2.drawRoundRect(x, y, w - 1, h + 4, 6, 6);
|
||||
g2.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintText(Graphics g, int tabPlacement, Font font, FontMetrics metrics, int tabIndex, String title, Rectangle textRect, boolean isSelected) {
|
||||
g.setFont(font);
|
||||
g.setColor(isSelected ? ThemeManager.getTabSelectedFg() : ThemeManager.getTabFg());
|
||||
g.drawString(title, textRect.x, textRect.y + metrics.getAscent());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) {
|
||||
int width = tabPane.getWidth();
|
||||
int height = tabPane.getHeight();
|
||||
Insets insets = tabPane.getInsets();
|
||||
int x = insets.left;
|
||||
int y = insets.top + calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
|
||||
int w = width - insets.right - insets.left;
|
||||
int h = height - insets.top - insets.bottom - calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
|
||||
|
||||
g.setColor(ThemeManager.getTabSelectedBg());
|
||||
g.fillRect(x, y, w, h);
|
||||
g.setColor(ThemeManager.getBorder());
|
||||
g.drawRect(x, y, w - 1, h - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package haus.nightmare.j3270.ui;
|
||||
|
||||
/**
|
||||
* Supported UI themes for the j3270 Java desktop interface.
|
||||
*/
|
||||
public enum UITheme {
|
||||
DARK("Dark Mode"),
|
||||
LIGHT("Light Mode");
|
||||
|
||||
private final String displayName;
|
||||
|
||||
UITheme(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public static UITheme fromString(String name) {
|
||||
if (name == null) return DARK;
|
||||
String s = name.trim().toUpperCase();
|
||||
if ("LIGHT".equals(s) || "LIGHT MODE".equals(s) || "LIGHT_MODE".equals(s)) {
|
||||
return LIGHT;
|
||||
}
|
||||
return DARK;
|
||||
}
|
||||
}
|
||||
@@ -26,25 +26,24 @@ public class UntrustedCertificateDialog extends JDialog {
|
||||
private void buildUI(String host, int port, X509Certificate[] chain, CertificateException exception) {
|
||||
JPanel mainPanel = new JPanel(new BorderLayout(12, 12));
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(16, 16, 16, 16));
|
||||
mainPanel.setBackground(new Color(30, 30, 30));
|
||||
|
||||
// Header
|
||||
JPanel headerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0));
|
||||
headerPanel.setBackground(new Color(30, 30, 30));
|
||||
headerPanel.setOpaque(false);
|
||||
JLabel iconLabel = new JLabel("⚠️");
|
||||
iconLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 28));
|
||||
headerPanel.add(iconLabel);
|
||||
|
||||
JPanel titleBox = new JPanel(new GridLayout(2, 1, 0, 2));
|
||||
titleBox.setBackground(new Color(30, 30, 30));
|
||||
titleBox.setOpaque(false);
|
||||
JLabel titleLabel = new JLabel("Untrusted SSL Certificate");
|
||||
titleLabel.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 16));
|
||||
titleLabel.setForeground(new Color(255, 180, 80));
|
||||
titleLabel.setForeground(ThemeManager.getOiaFgWarn());
|
||||
titleBox.add(titleLabel);
|
||||
|
||||
JLabel subtitleLabel = new JLabel("The server certificate for " + host + ":" + port + " could not be verified.");
|
||||
subtitleLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 12));
|
||||
subtitleLabel.setForeground(new Color(180, 180, 180));
|
||||
subtitleLabel.setForeground(ThemeManager.getFgMuted());
|
||||
titleBox.add(subtitleLabel);
|
||||
headerPanel.add(titleBox);
|
||||
mainPanel.add(headerPanel, BorderLayout.NORTH);
|
||||
@@ -71,30 +70,27 @@ public class UntrustedCertificateDialog extends JDialog {
|
||||
JTextArea detailsArea = new JTextArea(sb.toString());
|
||||
detailsArea.setEditable(false);
|
||||
detailsArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
|
||||
detailsArea.setBackground(new Color(20, 20, 20));
|
||||
detailsArea.setForeground(new Color(210, 210, 210));
|
||||
ThemeManager.styleTextArea(detailsArea);
|
||||
detailsArea.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(detailsArea);
|
||||
scrollPane.setPreferredSize(new Dimension(520, 260));
|
||||
scrollPane.setBorder(BorderFactory.createLineBorder(new Color(60, 60, 60)));
|
||||
ThemeManager.styleScrollPane(scrollPane);
|
||||
mainPanel.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
// Buttons
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0));
|
||||
buttonPanel.setBackground(new Color(30, 30, 30));
|
||||
buttonPanel.setOpaque(false);
|
||||
|
||||
JButton cancelBtn = new JButton("Cancel Connection");
|
||||
cancelBtn.setBackground(new Color(60, 60, 60));
|
||||
cancelBtn.setForeground(new Color(220, 220, 220));
|
||||
ThemeManager.styleButton(cancelBtn, ThemeManager.ButtonVariant.CANCEL);
|
||||
cancelBtn.addActionListener(e -> {
|
||||
accepted = false;
|
||||
dispose();
|
||||
});
|
||||
|
||||
JButton trustBtn = new JButton("Connect Anyway");
|
||||
trustBtn.setBackground(new Color(180, 100, 40));
|
||||
trustBtn.setForeground(Color.WHITE);
|
||||
ThemeManager.styleButton(trustBtn, ThemeManager.ButtonVariant.DANGER);
|
||||
trustBtn.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 13));
|
||||
trustBtn.addActionListener(e -> {
|
||||
accepted = true;
|
||||
@@ -106,6 +102,7 @@ public class UntrustedCertificateDialog extends JDialog {
|
||||
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
|
||||
|
||||
setContentPane(mainPanel);
|
||||
ThemeManager.applyThemeToWindow(this);
|
||||
getRootPane().setDefaultButton(trustBtn);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package haus.nightmare.j3270.ui;
|
||||
|
||||
import haus.nightmare.j3270.J3270App;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.HeadlessException;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class MenuBarShortcutsTest {
|
||||
|
||||
private Map<String, JMenuItem> collectMenuItems(JMenu menu) {
|
||||
Map<String, JMenuItem> map = new HashMap<>();
|
||||
for (int i = 0; i < menu.getItemCount(); i++) {
|
||||
JMenuItem item = menu.getItem(i);
|
||||
if (item != null) {
|
||||
map.put(item.getText(), item);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActionAndViewShortcutsUseAlt() {
|
||||
J3270App app;
|
||||
try {
|
||||
app = new J3270App();
|
||||
} catch (HeadlessException e) {
|
||||
// In automated/headless environments, JFrame cannot be initialized
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
JMenuBar mb = app.getJMenuBar();
|
||||
assertNotNull(mb, "JMenuBar should be present");
|
||||
|
||||
JMenu viewMenu = null;
|
||||
JMenu actionsMenu = null;
|
||||
JMenu fileMenu = null;
|
||||
JMenu editMenu = null;
|
||||
|
||||
for (int i = 0; i < mb.getMenuCount(); i++) {
|
||||
JMenu m = mb.getMenu(i);
|
||||
if (m != null) {
|
||||
if ("View".equals(m.getText())) viewMenu = m;
|
||||
else if ("Actions".equals(m.getText())) actionsMenu = m;
|
||||
else if ("File".equals(m.getText())) fileMenu = m;
|
||||
else if ("Edit".equals(m.getText())) editMenu = m;
|
||||
}
|
||||
}
|
||||
|
||||
assertNotNull(viewMenu, "View menu should exist");
|
||||
assertNotNull(actionsMenu, "Actions menu should exist");
|
||||
assertNotNull(fileMenu, "File menu should exist");
|
||||
assertNotNull(editMenu, "Edit menu should exist");
|
||||
|
||||
// Verify View Menu items
|
||||
Map<String, JMenuItem> viewItems = collectMenuItems(viewMenu);
|
||||
assertAcceleratorUsesAlt(viewItems.get("Font Size +"), KeyEvent.VK_EQUALS);
|
||||
assertAcceleratorUsesAlt(viewItems.get("Font Size -"), KeyEvent.VK_MINUS);
|
||||
assertAcceleratorUsesAlt(viewItems.get("Reset Font"), KeyEvent.VK_0);
|
||||
|
||||
// Crosshair Ruler must use Alt+Shift+R
|
||||
JMenuItem rulerItem = viewItems.get("Crosshair Ruler");
|
||||
assertNotNull(rulerItem, "Crosshair Ruler menu item must exist");
|
||||
KeyStroke rulerKs = rulerItem.getAccelerator();
|
||||
assertNotNull(rulerKs, "Crosshair Ruler should have an accelerator");
|
||||
assertEquals(KeyEvent.VK_R, rulerKs.getKeyCode(), "Key code mismatch for Crosshair Ruler");
|
||||
int rulerMods = rulerKs.getModifiers();
|
||||
assertTrue((rulerMods & (KeyEvent.ALT_DOWN_MASK | KeyEvent.ALT_MASK)) != 0,
|
||||
"Crosshair Ruler accelerator must have ALT modifier");
|
||||
assertTrue((rulerMods & (KeyEvent.SHIFT_DOWN_MASK | KeyEvent.SHIFT_MASK)) != 0,
|
||||
"Crosshair Ruler accelerator must have SHIFT modifier");
|
||||
assertEquals(0, rulerMods & (KeyEvent.CTRL_DOWN_MASK | KeyEvent.CTRL_MASK | KeyEvent.META_DOWN_MASK | KeyEvent.META_MASK),
|
||||
"Crosshair Ruler accelerator must NOT have CTRL or META modifiers");
|
||||
|
||||
// Verify Actions Menu items use ALT_DOWN_MASK
|
||||
Map<String, JMenuItem> actionItems = collectMenuItems(actionsMenu);
|
||||
assertAcceleratorUsesAlt(actionItems.get("Send Enter"), KeyEvent.VK_ENTER);
|
||||
assertAcceleratorUsesAlt(actionItems.get("Clear"), KeyEvent.VK_K);
|
||||
assertAcceleratorUsesAlt(actionItems.get("Reset"), KeyEvent.VK_R);
|
||||
assertAcceleratorUsesAlt(actionItems.get("Erase Input"), KeyEvent.VK_E);
|
||||
assertAcceleratorUsesAlt(actionItems.get("Attention"), KeyEvent.VK_A);
|
||||
assertAcceleratorUsesAlt(actionItems.get("System Request"), KeyEvent.VK_S);
|
||||
assertAcceleratorUsesAlt(actionItems.get("Cursor Select"), KeyEvent.VK_Q);
|
||||
assertAcceleratorUsesAlt(actionItems.get("Toggle Light Pen (Alt+L)"), KeyEvent.VK_L);
|
||||
assertAcceleratorUsesAlt(actionItems.get("File Transfer..."), KeyEvent.VK_T);
|
||||
|
||||
// Ensure Crosshair Ruler and Reset do not collide
|
||||
assertNotEquals(rulerKs, actionItems.get("Reset").getAccelerator(),
|
||||
"Crosshair Ruler and Reset accelerators must not collide");
|
||||
} finally {
|
||||
// Dispose frame
|
||||
app.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void assertAcceleratorUsesAlt(JMenuItem item, int expectedKeyCode) {
|
||||
assertNotNull(item, "Menu item must exist");
|
||||
KeyStroke ks = item.getAccelerator();
|
||||
assertNotNull(ks, "Menu item " + item.getText() + " should have an accelerator");
|
||||
assertEquals(expectedKeyCode, ks.getKeyCode(), "Key code mismatch for " + item.getText());
|
||||
int mods = ks.getModifiers();
|
||||
assertTrue((mods & (KeyEvent.ALT_DOWN_MASK | KeyEvent.ALT_MASK)) != 0,
|
||||
"Menu item " + item.getText() + " accelerator must have ALT modifier");
|
||||
assertEquals(0, mods & (KeyEvent.SHIFT_DOWN_MASK | KeyEvent.SHIFT_MASK),
|
||||
"Menu item " + item.getText() + " accelerator must NOT have SHIFT modifier");
|
||||
assertEquals(0, mods & (KeyEvent.CTRL_DOWN_MASK | KeyEvent.CTRL_MASK | KeyEvent.META_DOWN_MASK | KeyEvent.META_MASK),
|
||||
"Menu item " + item.getText() + " accelerator must NOT have CTRL or META modifiers");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package haus.nightmare.j3270.ui;
|
||||
|
||||
import haus.nightmare.j3270.config.Settings;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.HeadlessException;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Verification test suite for Phase 1 UI Overlays and Color Alignment.
|
||||
* Covers items 1.1, 1.2, and 1.5 of the PhasedUpdates specification.
|
||||
*/
|
||||
public class Phase1UiOverlayTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Item 1.1 & 1.2: TerminalPanel host and mono colors match IBM Host On-Demand specs")
|
||||
public void testTerminalPanelColorAlignment() {
|
||||
// Index 10: Orange (255, 162, 0)
|
||||
assertEquals(new Color(255, 162, 0), TerminalPanel.DEFAULT_HOST_COLORS[10]);
|
||||
|
||||
// Index 14: Mustard (160, 160, 0)
|
||||
assertEquals(new Color(160, 160, 0), TerminalPanel.DEFAULT_HOST_COLORS[14]);
|
||||
|
||||
// Index 1: Blue (120, 144, 240)
|
||||
assertEquals(new Color(120, 144, 240), TerminalPanel.DEFAULT_HOST_COLORS[1]);
|
||||
|
||||
// Base 4-Color mono defaults
|
||||
assertEquals(new Color(0, 255, 0), TerminalPanel.DEFAULT_MONO_NORMAL);
|
||||
assertEquals(new Color(255, 0, 0), TerminalPanel.DEFAULT_MONO_INTENSIFY);
|
||||
assertEquals(new Color(0, 255, 255), TerminalPanel.DEFAULT_MONO_PROTECTED);
|
||||
assertEquals(new Color(255, 255, 255), TerminalPanel.DEFAULT_MONO_PROTECTED_HIGH);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Item 1.5: ThemeManager HoD OIA category colors match specification")
|
||||
public void testThemeManagerOiaColors() {
|
||||
// oSI: Status / System Available -> CUSTOMBLUE (120, 144, 240)
|
||||
assertEquals(new Color(120, 144, 240), ThemeManager.getOiaStatusSysAvail());
|
||||
|
||||
// oII: Input Inhibited / X SYSTEM -> White (255, 255, 255)
|
||||
assertEquals(new Color(255, 255, 255), ThemeManager.getOiaInputInhibited());
|
||||
|
||||
// oAI: Attention / Reminders / Message Waiting -> Yellow (255, 255, 0)
|
||||
assertEquals(new Color(255, 255, 0), ThemeManager.getOiaAttention());
|
||||
|
||||
// oEI: Error Checks / Comm Check -> Red (255, 0, 0)
|
||||
assertEquals(new Color(255, 0, 0), ThemeManager.getOiaCommCheck());
|
||||
|
||||
// oOB: OIA Separator / Background -> Black (0, 0, 0)
|
||||
assertEquals(new Color(0, 0, 0), ThemeManager.getOiaBackground());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Item 1.5: TerminalPanel Crosshair Ruler and Cursor Style controls")
|
||||
public void testTerminalPanelRulerAndCursorStyle() {
|
||||
try {
|
||||
TerminalPanel panel = new TerminalPanel();
|
||||
|
||||
// Default cursor style is BLOCK
|
||||
assertEquals(TerminalPanel.CursorStyle.BLOCK, panel.getCursorStyle());
|
||||
panel.setCursorStyle(TerminalPanel.CursorStyle.UNDERLINE);
|
||||
assertEquals(TerminalPanel.CursorStyle.UNDERLINE, panel.getCursorStyle());
|
||||
|
||||
// Crosshair ruler toggling
|
||||
assertFalse(panel.isCrosshairRulerEnabled());
|
||||
panel.setCrosshairRulerEnabled(true);
|
||||
assertTrue(panel.isCrosshairRulerEnabled());
|
||||
panel.toggleCrosshairRuler();
|
||||
assertFalse(panel.isCrosshairRulerEnabled());
|
||||
|
||||
// Wallpaper image setter
|
||||
assertNull(panel.getWallpaperImage());
|
||||
} catch (HeadlessException e) {
|
||||
// Handled gracefully in headless CI environments
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Settings persistence for Crosshair Ruler and Cursor Style")
|
||||
public void testSettingsPersistence() {
|
||||
boolean originalRuler = Settings.getCrosshairRuler();
|
||||
String originalCursor = Settings.getCursorStyle();
|
||||
|
||||
try {
|
||||
Settings.setCrosshairRuler(true);
|
||||
assertTrue(Settings.getCrosshairRuler());
|
||||
|
||||
Settings.setCrosshairRuler(false);
|
||||
assertFalse(Settings.getCrosshairRuler());
|
||||
|
||||
Settings.setCursorStyle("UNDERLINE");
|
||||
assertEquals("UNDERLINE", Settings.getCursorStyle());
|
||||
|
||||
Settings.setCursorStyle("BLOCK");
|
||||
assertEquals("BLOCK", Settings.getCursorStyle());
|
||||
} finally {
|
||||
Settings.setCrosshairRuler(originalRuler);
|
||||
Settings.setCursorStyle(originalCursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package haus.nightmare.j3270.ui;
|
||||
|
||||
import haus.nightmare.lib3270j.graphics.GocaConstants;
|
||||
import haus.nightmare.lib3270j.graphics.GraphicsPlane;
|
||||
import haus.nightmare.lib3270j.graphics.HODWallpaper;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.HeadlessException;
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Verification test suite for Phase 2 UI Vector Graphics, Wallpaper, and Cursor Overlays.
|
||||
*/
|
||||
public class Phase2UiGraphicsTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Test TerminalPanel Wallpaper Integration (Tile, Center, Stretch)")
|
||||
public void testTerminalPanelWallpaperModes() {
|
||||
try {
|
||||
TerminalPanel panel = new TerminalPanel();
|
||||
assertNull(panel.getHodWallpaper());
|
||||
assertNull(panel.getWallpaperImage());
|
||||
|
||||
BufferedImage img = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
|
||||
panel.setWallpaperImage(img);
|
||||
assertNotNull(panel.getWallpaperImage());
|
||||
assertNotNull(panel.getHodWallpaper());
|
||||
assertEquals(HODWallpaper.HOD_STRETCH, panel.getHodWallpaper().getDisplay());
|
||||
|
||||
panel.setWallpaperMode(HODWallpaper.HOD_TILE);
|
||||
assertEquals(HODWallpaper.HOD_TILE, panel.getHodWallpaper().getDisplay());
|
||||
|
||||
panel.setWallpaperMode(HODWallpaper.HOD_CENTER);
|
||||
assertEquals(HODWallpaper.HOD_CENTER, panel.getHodWallpaper().getDisplay());
|
||||
|
||||
HODWallpaper customWp = new HODWallpaper(img, HODWallpaper.HOD_TILE);
|
||||
panel.setHodWallpaper(customWp);
|
||||
assertSame(customWp, panel.getHodWallpaper());
|
||||
|
||||
// Clear wallpaper
|
||||
panel.setWallpaperImage(null);
|
||||
assertNull(panel.getHodWallpaper());
|
||||
assertNull(panel.getWallpaperImage());
|
||||
} catch (HeadlessException e) {
|
||||
// Handled gracefully in headless environments
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test Graphics Plane Cursor Synchronization and Rendering")
|
||||
public void testGraphicsCursorOverlay() {
|
||||
try {
|
||||
TerminalPanel panel = new TerminalPanel();
|
||||
panel.setSize(new Dimension(800, 600));
|
||||
panel.setClient(new haus.nightmare.lib3270j.Telnet3270Client(new haus.nightmare.lib3270j.ConnectionConfig("localhost", 23)));
|
||||
|
||||
GraphicsPlane plane = panel.getClient() != null ? panel.getClient().getGraphicsPlane() : null;
|
||||
if (plane != null) {
|
||||
// Attach graphics cursor
|
||||
plane.attachGraphicCursor(100, 100);
|
||||
plane.setHodCursorShape(GocaConstants.GCURSOR_SHAPE_CROSSHAIR);
|
||||
assertTrue(plane.isGraphicCursorAttached());
|
||||
assertEquals(100, plane.getGraphicCursorX());
|
||||
assertEquals(100, plane.getGraphicCursorY());
|
||||
assertEquals(1, plane.getHodCursorShape());
|
||||
|
||||
// Paint component to verify no exceptions during rendering
|
||||
BufferedImage offscreen = new BufferedImage(800, 600, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g2 = offscreen.createGraphics();
|
||||
panel.paint(g2);
|
||||
g2.dispose();
|
||||
|
||||
// Change cursor shape to box
|
||||
plane.setHodCursorShape(GocaConstants.GCURSOR_SHAPE_BOX);
|
||||
assertEquals(2, plane.getHodCursorShape());
|
||||
offscreen = new BufferedImage(800, 600, BufferedImage.TYPE_INT_ARGB);
|
||||
g2 = offscreen.createGraphics();
|
||||
panel.paint(g2);
|
||||
g2.dispose();
|
||||
|
||||
// Detach graphics cursor
|
||||
plane.detachGraphicCursor();
|
||||
assertFalse(plane.isGraphicCursorAttached());
|
||||
}
|
||||
} catch (HeadlessException e) {
|
||||
// Handled gracefully in headless environments
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package haus.nightmare.j3270.ui;
|
||||
|
||||
import haus.nightmare.lib3270j.ConnectionConfig;
|
||||
import haus.nightmare.lib3270j.Telnet3270Client;
|
||||
import haus.nightmare.lib3270j.TerminalModel;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class ScreenExporterTest {
|
||||
|
||||
private Telnet3270Client client;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
ConnectionConfig config = new ConnectionConfig("localhost", 23, TerminalModel.IBM_3279_2, false);
|
||||
client = new Telnet3270Client(config);
|
||||
client.getInputProcessor().setKeyboardLocked(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlainTextExport() throws IOException {
|
||||
client.getPS().setText("TSO/E LOGON SCREEN - WELCOME", 0);
|
||||
|
||||
File tempTxt = File.createTempFile("export_test_", ".txt");
|
||||
tempTxt.deleteOnExit();
|
||||
|
||||
ScreenExporter.exportToText(client, tempTxt);
|
||||
|
||||
assertTrue(tempTxt.exists());
|
||||
assertTrue(tempTxt.length() > 0);
|
||||
|
||||
String content = Files.readString(tempTxt.toPath());
|
||||
assertTrue(content.contains("TSO/E LOGON SCREEN - WELCOME"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHtmlExport() throws IOException {
|
||||
client.getPS().setText("TSO/E LOGON SCREEN - WELCOME", 0);
|
||||
|
||||
File tempHtml = File.createTempFile("export_test_", ".html");
|
||||
tempHtml.deleteOnExit();
|
||||
|
||||
ScreenExporter.exportToHtml(client, tempHtml);
|
||||
|
||||
assertTrue(tempHtml.exists());
|
||||
assertTrue(tempHtml.length() > 0);
|
||||
|
||||
String html = Files.readString(tempHtml.toPath());
|
||||
assertTrue(html.contains("<!DOCTYPE html>"));
|
||||
assertTrue(html.contains("TSO/E LOGON SCREEN - WELCOME"));
|
||||
assertTrue(html.contains("font-family: 'Courier New', Courier, monospace"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package haus.nightmare.j3270.ui;
|
||||
|
||||
import haus.nightmare.lib3270j.ConnectionConfig;
|
||||
import haus.nightmare.lib3270j.Telnet3270Client;
|
||||
import haus.nightmare.lib3270j.TerminalModel;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class StatusBarTest {
|
||||
|
||||
@Test
|
||||
public void testStatusBarDoesNotContainLightPenButton() {
|
||||
try {
|
||||
StatusBar statusBar = new StatusBar();
|
||||
|
||||
// Ensure no JButton exists in StatusBar components (lightpen button removed)
|
||||
for (Component comp : statusBar.getComponents()) {
|
||||
assertFalse(comp instanceof JButton, "StatusBar should not contain any JButton (lightpen removed from bottom bar)");
|
||||
}
|
||||
} catch (HeadlessException e) {
|
||||
// Ignored in headless environments
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatusBarUpdatesWithClient() {
|
||||
try {
|
||||
ConnectionConfig config = new ConnectionConfig("mvs.example.com", 23, TerminalModel.IBM_3279_4, false);
|
||||
config.setLuName("TSU001");
|
||||
config.setCodePage("1047");
|
||||
Telnet3270Client client = new Telnet3270Client(config);
|
||||
|
||||
StatusBar statusBar = new StatusBar();
|
||||
statusBar.setClient(client, null);
|
||||
statusBar.updateStatus();
|
||||
|
||||
// Check labels
|
||||
boolean foundCodePage = false;
|
||||
boolean foundModel = false;
|
||||
for (Component comp : statusBar.getComponents()) {
|
||||
if (comp instanceof JLabel) {
|
||||
JLabel label = (JLabel) comp;
|
||||
if ("CP1047".equals(label.getText())) {
|
||||
foundCodePage = true;
|
||||
}
|
||||
if (label.getText() != null && label.getText().contains("3279-4")) {
|
||||
foundModel = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(foundCodePage, "Should display CP1047");
|
||||
assertTrue(foundModel, "Should display IBM-3279-4 model info");
|
||||
} catch (HeadlessException e) {
|
||||
// Ignored in headless environments
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
package haus.nightmare.j3270.ui;
|
||||
|
||||
import haus.nightmare.j3270.config.Settings;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class ThemeManagerTest {
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ThemeManager.setTheme(UITheme.DARK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUIThemeEnum() {
|
||||
assertEquals("Dark Mode", UITheme.DARK.getDisplayName());
|
||||
assertEquals("Light Mode", UITheme.LIGHT.getDisplayName());
|
||||
|
||||
assertEquals(UITheme.DARK, UITheme.fromString("DARK"));
|
||||
assertEquals(UITheme.DARK, UITheme.fromString("dark"));
|
||||
assertEquals(UITheme.DARK, UITheme.fromString("Dark Mode"));
|
||||
assertEquals(UITheme.LIGHT, UITheme.fromString("LIGHT"));
|
||||
assertEquals(UITheme.LIGHT, UITheme.fromString("light"));
|
||||
assertEquals(UITheme.LIGHT, UITheme.fromString("Light Mode"));
|
||||
assertEquals(UITheme.DARK, UITheme.fromString("invalid_value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testColorTokensContrast() {
|
||||
for (UITheme theme : UITheme.values()) {
|
||||
Color bg = ThemeManager.getBgMain(theme);
|
||||
Color fg = ThemeManager.getFgMain(theme);
|
||||
assertNotNull(bg);
|
||||
assertNotNull(fg);
|
||||
|
||||
// Compute luminance contrast
|
||||
double bgLum = (0.299 * bg.getRed() + 0.587 * bg.getGreen() + 0.114 * bg.getBlue());
|
||||
double fgLum = (0.299 * fg.getRed() + 0.587 * fg.getGreen() + 0.114 * fg.getBlue());
|
||||
double lumDiff = Math.abs(bgLum - fgLum);
|
||||
|
||||
// Minimum luminance difference to guarantee readability
|
||||
assertTrue(lumDiff > 120, "Theme " + theme + " text contrast luminance difference " + lumDiff + " must be > 120");
|
||||
|
||||
// Verify Menu Bar colors
|
||||
Color menuBg = ThemeManager.getMenuBarBg(theme);
|
||||
Color menuFg = ThemeManager.getMenuItemFg(theme);
|
||||
double menuBgLum = (0.299 * menuBg.getRed() + 0.587 * menuBg.getGreen() + 0.114 * menuBg.getBlue());
|
||||
double menuFgLum = (0.299 * menuFg.getRed() + 0.587 * menuFg.getGreen() + 0.114 * menuFg.getBlue());
|
||||
assertTrue(Math.abs(menuBgLum - menuFgLum) > 100, "Menu contrast in " + theme + " must be high");
|
||||
|
||||
// Verify Status Bar colors
|
||||
Color sbBg = ThemeManager.getStatusBarBg(theme);
|
||||
Color sbNormal = ThemeManager.getOiaFgNormal(theme);
|
||||
assertNotNull(sbBg);
|
||||
assertNotNull(sbNormal);
|
||||
|
||||
// Verify Button colors
|
||||
Color btnDefBg = ThemeManager.getButtonBg(ThemeManager.ButtonVariant.DEFAULT, theme);
|
||||
Color btnDefFg = ThemeManager.getButtonFg(ThemeManager.ButtonVariant.DEFAULT, theme);
|
||||
assertNotNull(btnDefBg);
|
||||
assertNotNull(btnDefFg);
|
||||
|
||||
Color btnPriBg = ThemeManager.getButtonBg(ThemeManager.ButtonVariant.PRIMARY, theme);
|
||||
Color btnPriFg = ThemeManager.getButtonFg(ThemeManager.ButtonVariant.PRIMARY, theme);
|
||||
assertNotNull(btnPriBg);
|
||||
assertNotNull(btnPriFg);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThemeChangeListeners() {
|
||||
AtomicReference<UITheme> notifiedTheme = new AtomicReference<>(null);
|
||||
ThemeManager.addThemeChangeListener(notifiedTheme::set);
|
||||
|
||||
ThemeManager.setTheme(UITheme.LIGHT);
|
||||
assertEquals(UITheme.LIGHT, ThemeManager.getTheme());
|
||||
assertEquals(UITheme.LIGHT, notifiedTheme.get());
|
||||
assertTrue(ThemeManager.isLight());
|
||||
assertFalse(ThemeManager.isDark());
|
||||
|
||||
ThemeManager.setTheme(UITheme.DARK);
|
||||
assertEquals(UITheme.DARK, ThemeManager.getTheme());
|
||||
assertEquals(UITheme.DARK, notifiedTheme.get());
|
||||
assertTrue(ThemeManager.isDark());
|
||||
assertFalse(ThemeManager.isLight());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComponentStyling() {
|
||||
try {
|
||||
JButton btn = new JButton("Test");
|
||||
ThemeManager.styleButton(btn, ThemeManager.ButtonVariant.PRIMARY);
|
||||
assertNotNull(btn.getUI());
|
||||
assertTrue(btn.getUI() instanceof ThemeManager.StyledButtonUI);
|
||||
|
||||
JTextField tf = new JTextField("Test");
|
||||
ThemeManager.styleTextField(tf);
|
||||
assertNotNull(tf.getCaretColor());
|
||||
|
||||
JTextArea ta = new JTextArea("Test area");
|
||||
ThemeManager.styleTextArea(ta);
|
||||
assertNotNull(ta.getCaretColor());
|
||||
|
||||
JComboBox<String> cb = new JComboBox<>(new String[]{"A", "B"});
|
||||
ThemeManager.styleComboBox(cb);
|
||||
assertNotNull(cb.getUI());
|
||||
|
||||
JTable table = new JTable(new DefaultTableModel(new Object[]{"Col1"}, 1));
|
||||
ThemeManager.styleTable(table);
|
||||
assertNotNull(table.getSelectionBackground());
|
||||
|
||||
JTabbedPane tp = new JTabbedPane();
|
||||
tp.addTab("Tab1", new JPanel());
|
||||
ThemeManager.styleTabbedPane(tp);
|
||||
assertNotNull(tp.getUI());
|
||||
|
||||
JMenuBar mb = new JMenuBar();
|
||||
ThemeManager.styleMenuBar(mb);
|
||||
|
||||
JMenu menu = new JMenu("File");
|
||||
ThemeManager.styleMenu(menu);
|
||||
|
||||
JMenuItem mi = new JMenuItem("Open");
|
||||
ThemeManager.styleMenuItem(mi);
|
||||
|
||||
JPopupMenu popup = new JPopupMenu();
|
||||
ThemeManager.stylePopupMenu(popup);
|
||||
|
||||
JCheckBox chk = new JCheckBox("Check");
|
||||
ThemeManager.styleCheckBox(chk);
|
||||
|
||||
JRadioButton rb = new JRadioButton("Radio");
|
||||
ThemeManager.styleRadioButton(rb);
|
||||
|
||||
Border tb = ThemeManager.createTitledBorder("Title");
|
||||
assertNotNull(tb);
|
||||
} catch (HeadlessException e) {
|
||||
// Ignored in headless environments
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecursiveApplyTheme() {
|
||||
try {
|
||||
JPanel root = new JPanel(new BorderLayout());
|
||||
JButton b = new JButton("OK");
|
||||
JTextField f = new JTextField("Data");
|
||||
JTabbedPane tp = new JTabbedPane();
|
||||
JPanel tabContent = new JPanel();
|
||||
JLabel lbl = new JLabel("Label");
|
||||
tabContent.add(lbl);
|
||||
tp.addTab("T1", tabContent);
|
||||
|
||||
root.add(b, BorderLayout.NORTH);
|
||||
root.add(f, BorderLayout.CENTER);
|
||||
root.add(tp, BorderLayout.SOUTH);
|
||||
|
||||
ThemeManager.setTheme(UITheme.LIGHT);
|
||||
ThemeManager.applyTheme(root);
|
||||
|
||||
assertEquals(ThemeManager.getBgPanel(UITheme.LIGHT), root.getBackground());
|
||||
assertEquals(ThemeManager.getFgMain(UITheme.LIGHT), lbl.getForeground());
|
||||
|
||||
ThemeManager.setTheme(UITheme.DARK);
|
||||
ThemeManager.applyTheme(root);
|
||||
|
||||
assertEquals(ThemeManager.getBgPanel(UITheme.DARK), root.getBackground());
|
||||
assertEquals(ThemeManager.getFgMain(UITheme.DARK), lbl.getForeground());
|
||||
} catch (HeadlessException e) {
|
||||
// Ignored in headless environments
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSettingsPersistence() throws Exception {
|
||||
Settings.setJavaUiTheme(UITheme.LIGHT);
|
||||
assertEquals(UITheme.LIGHT, Settings.getJavaUiTheme());
|
||||
|
||||
File tmpIni = File.createTempFile("j3270_test_config", ".ini");
|
||||
tmpIni.deleteOnExit();
|
||||
|
||||
try {
|
||||
// Write INI file with javaUiTheme = dark
|
||||
try (PrintWriter pw = new PrintWriter(tmpIni)) {
|
||||
pw.println("[j3270]");
|
||||
pw.println("javaUiTheme = dark");
|
||||
pw.println("fontFamily = Monospaced");
|
||||
pw.println("fontSize = 16");
|
||||
}
|
||||
|
||||
Settings.loadFromIniFile(tmpIni.getAbsolutePath());
|
||||
assertEquals(UITheme.DARK, Settings.getJavaUiTheme());
|
||||
|
||||
// Switch to light and export
|
||||
Settings.setJavaUiTheme(UITheme.LIGHT);
|
||||
File exportFile = File.createTempFile("j3270_export_config", ".ini");
|
||||
exportFile.deleteOnExit();
|
||||
|
||||
Settings.exportToIniFile(exportFile.getAbsolutePath());
|
||||
String exportedContent = new String(Files.readAllBytes(exportFile.toPath()));
|
||||
assertTrue(exportedContent.toLowerCase().contains("javauitheme = light"));
|
||||
|
||||
// Reload exported file
|
||||
Settings.loadFromIniFile(exportFile.getAbsolutePath());
|
||||
assertEquals(UITheme.LIGHT, Settings.getJavaUiTheme());
|
||||
} finally {
|
||||
tmpIni.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,10 @@ package haus.nightmare.lib3270j;
|
||||
*/
|
||||
public class ConnectionConfig {
|
||||
|
||||
public enum ProxyType {
|
||||
NONE, HTTP, SOCKS4, SOCKS5
|
||||
}
|
||||
|
||||
private String host;
|
||||
private int port = 23;
|
||||
private TerminalModel model = TerminalModel.IBM_3279_4;
|
||||
@@ -14,11 +18,45 @@ public class ConnectionConfig {
|
||||
private boolean tlsVerifyCert = true;
|
||||
private haus.nightmare.lib3270j.tls.TlsCertificateVerifier certificateVerifier = null;
|
||||
private String sslProtocol = "TLS";
|
||||
private String keyStorePath = null;
|
||||
private String keyStorePassword = null;
|
||||
private String keyStoreType = null;
|
||||
private String keyStoreAlias = null;
|
||||
private String trustStorePath = null;
|
||||
private String trustStorePassword = null;
|
||||
private String trustStoreType = null;
|
||||
private ClassLoader customizedCAsClassLoader = null;
|
||||
private java.util.List<String> enabledProtocols = new java.util.ArrayList<>();
|
||||
private java.util.List<String> enabledCipherSuites = new java.util.ArrayList<>();
|
||||
private int connectTimeoutMs = 15000;
|
||||
private int nopIntervalSeconds = 0;
|
||||
private String terminalName = null; // override terminal type string
|
||||
private boolean tn3270eEnabled = true;
|
||||
private boolean tcpNoDelay = true;
|
||||
private boolean soKeepAlive = true;
|
||||
private int soTimeoutMs = 0;
|
||||
private java.util.List<String> luNames = new java.util.ArrayList<>();
|
||||
private boolean dynamicModel = false;
|
||||
private int dynamicRows = 62;
|
||||
private int dynamicCols = 160;
|
||||
private haus.nightmare.lib3270j.graphics.GraphicsMode graphicsMode = haus.nightmare.lib3270j.graphics.GraphicsMode.BOTH;
|
||||
private String codePage = "037";
|
||||
private String associatedPrinterLu = null;
|
||||
private boolean nvtLocalEcho = false;
|
||||
|
||||
// Proxy configuration
|
||||
private ProxyType proxyType = ProxyType.NONE;
|
||||
private String proxyHost = null;
|
||||
private int proxyPort = 0;
|
||||
private String proxyUsername = null;
|
||||
private String proxyPassword = null;
|
||||
|
||||
// STARTTLS (Telnet Option 46) dynamic socket elevation
|
||||
private boolean startTlsEnabled = true;
|
||||
|
||||
// RFC 1572 / RFC 2877 Environment variables (Express Logon)
|
||||
private java.util.Map<String, String> environmentVariables = new java.util.LinkedHashMap<>();
|
||||
private java.util.Map<String, String> userVariables = new java.util.LinkedHashMap<>();
|
||||
|
||||
public ConnectionConfig() {}
|
||||
|
||||
@@ -53,7 +91,12 @@ public class ConnectionConfig {
|
||||
public void setPort(int port) { this.port = port; }
|
||||
|
||||
public TerminalModel getModel() { return model; }
|
||||
public void setModel(TerminalModel model) { this.model = model; }
|
||||
public void setModel(TerminalModel model) {
|
||||
this.model = model;
|
||||
if (model != null && model.isDynamic()) {
|
||||
this.dynamicModel = true;
|
||||
}
|
||||
}
|
||||
|
||||
public String getLuName() { return luName; }
|
||||
public void setLuName(String luName) { this.luName = luName; }
|
||||
@@ -76,6 +119,52 @@ public class ConnectionConfig {
|
||||
public String getSslProtocol() { return sslProtocol; }
|
||||
public void setSslProtocol(String protocol) { this.sslProtocol = protocol; }
|
||||
|
||||
public String getKeyStorePath() { return keyStorePath; }
|
||||
public void setKeyStorePath(String path) { this.keyStorePath = path; }
|
||||
|
||||
public String getKeyStorePassword() { return keyStorePassword; }
|
||||
public void setKeyStorePassword(String password) { this.keyStorePassword = password; }
|
||||
|
||||
public String getKeyStoreType() { return keyStoreType; }
|
||||
public void setKeyStoreType(String type) { this.keyStoreType = type; }
|
||||
|
||||
public String getKeyStoreAlias() { return keyStoreAlias; }
|
||||
public void setKeyStoreAlias(String alias) { this.keyStoreAlias = alias; }
|
||||
|
||||
public String getTrustStorePath() { return trustStorePath; }
|
||||
public void setTrustStorePath(String path) { this.trustStorePath = path; }
|
||||
|
||||
public String getTrustStorePassword() { return trustStorePassword; }
|
||||
public void setTrustStorePassword(String password) { this.trustStorePassword = password; }
|
||||
|
||||
public String getTrustStoreType() { return trustStoreType; }
|
||||
public void setTrustStoreType(String type) { this.trustStoreType = type; }
|
||||
|
||||
public ClassLoader getCustomizedCAsClassLoader() { return customizedCAsClassLoader; }
|
||||
public void setCustomizedCAsClassLoader(ClassLoader cl) { this.customizedCAsClassLoader = cl; }
|
||||
|
||||
public java.util.List<String> getEnabledProtocols() { return enabledProtocols; }
|
||||
public void setEnabledProtocols(java.util.List<String> protocols) {
|
||||
this.enabledProtocols = protocols != null ? new java.util.ArrayList<>(protocols) : new java.util.ArrayList<>();
|
||||
}
|
||||
public void setEnabledProtocols(String... protocols) {
|
||||
this.enabledProtocols = new java.util.ArrayList<>();
|
||||
if (protocols != null) {
|
||||
for (String p : protocols) if (p != null) this.enabledProtocols.add(p);
|
||||
}
|
||||
}
|
||||
|
||||
public java.util.List<String> getEnabledCipherSuites() { return enabledCipherSuites; }
|
||||
public void setEnabledCipherSuites(java.util.List<String> cipherSuites) {
|
||||
this.enabledCipherSuites = cipherSuites != null ? new java.util.ArrayList<>(cipherSuites) : new java.util.ArrayList<>();
|
||||
}
|
||||
public void setEnabledCipherSuites(String... cipherSuites) {
|
||||
this.enabledCipherSuites = new java.util.ArrayList<>();
|
||||
if (cipherSuites != null) {
|
||||
for (String c : cipherSuites) if (c != null) this.enabledCipherSuites.add(c);
|
||||
}
|
||||
}
|
||||
|
||||
public int getConnectTimeoutMs() { return connectTimeoutMs; }
|
||||
public void setConnectTimeoutMs(int ms) { this.connectTimeoutMs = ms; }
|
||||
|
||||
@@ -87,12 +176,114 @@ public class ConnectionConfig {
|
||||
this.graphicsMode = (mode != null) ? mode : haus.nightmare.lib3270j.graphics.GraphicsMode.NONE;
|
||||
}
|
||||
|
||||
public String getCodePage() { return codePage; }
|
||||
public void setCodePage(String codePage) {
|
||||
this.codePage = (codePage != null && !codePage.trim().isEmpty()) ? codePage.trim() : "037";
|
||||
}
|
||||
|
||||
public boolean isNvtLocalEcho() { return nvtLocalEcho; }
|
||||
public void setNvtLocalEcho(boolean nvtLocalEcho) { this.nvtLocalEcho = nvtLocalEcho; }
|
||||
|
||||
public String getTerminalName() { return terminalName; }
|
||||
public void setTerminalName(String name) { this.terminalName = name; }
|
||||
|
||||
public boolean isTcpNoDelay() { return tcpNoDelay; }
|
||||
public void setTcpNoDelay(boolean tcpNoDelay) { this.tcpNoDelay = tcpNoDelay; }
|
||||
|
||||
public boolean isSoKeepAlive() { return soKeepAlive; }
|
||||
public void setSoKeepAlive(boolean soKeepAlive) { this.soKeepAlive = soKeepAlive; }
|
||||
|
||||
public int getSoTimeoutMs() { return soTimeoutMs; }
|
||||
public void setSoTimeoutMs(int soTimeoutMs) { this.soTimeoutMs = soTimeoutMs; }
|
||||
|
||||
public java.util.List<String> getLuNames() { return luNames; }
|
||||
public void setLuNames(java.util.List<String> luNames) {
|
||||
this.luNames = (luNames != null) ? new java.util.ArrayList<>(luNames) : new java.util.ArrayList<>();
|
||||
}
|
||||
public void addLuName(String luName) {
|
||||
if (luName != null && !luName.trim().isEmpty()) {
|
||||
this.luNames.add(luName.trim());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDynamicModel() {
|
||||
return dynamicModel || (model != null && model.isDynamic());
|
||||
}
|
||||
|
||||
public void setDynamicModel(boolean dynamicModel) {
|
||||
this.dynamicModel = dynamicModel;
|
||||
if (dynamicModel && (model == null || !model.isDynamic())) {
|
||||
this.model = TerminalModel.IBM_DYNAMIC;
|
||||
} else if (!dynamicModel && model != null && model.isDynamic()) {
|
||||
this.model = TerminalModel.IBM_3279_4;
|
||||
}
|
||||
}
|
||||
|
||||
public void setDynamic(boolean dynamic) {
|
||||
setDynamicModel(dynamic);
|
||||
}
|
||||
|
||||
public int getDynamicRows() { return dynamicRows; }
|
||||
public int getDynamicCols() { return dynamicCols; }
|
||||
public void setDynamicDimensions(int rows, int cols) {
|
||||
this.dynamicModel = true;
|
||||
this.dynamicRows = Math.max(1, rows);
|
||||
this.dynamicCols = Math.max(1, cols);
|
||||
this.model = TerminalModel.IBM_DYNAMIC;
|
||||
}
|
||||
|
||||
public ProxyType getProxyType() { return proxyType; }
|
||||
public void setProxyType(ProxyType proxyType) { this.proxyType = proxyType != null ? proxyType : ProxyType.NONE; }
|
||||
|
||||
public String getProxyHost() { return proxyHost; }
|
||||
public void setProxyHost(String proxyHost) { this.proxyHost = proxyHost; }
|
||||
|
||||
public int getProxyPort() { return proxyPort; }
|
||||
public void setProxyPort(int proxyPort) { this.proxyPort = proxyPort; }
|
||||
|
||||
public String getProxyUsername() { return proxyUsername; }
|
||||
public void setProxyUsername(String proxyUsername) { this.proxyUsername = proxyUsername; }
|
||||
|
||||
public String getProxyPassword() { return proxyPassword; }
|
||||
public void setProxyPassword(String proxyPassword) { this.proxyPassword = proxyPassword; }
|
||||
|
||||
public void setProxy(ProxyType type, String host, int port, String username, String password) {
|
||||
this.proxyType = type != null ? type : ProxyType.NONE;
|
||||
this.proxyHost = host;
|
||||
this.proxyPort = port;
|
||||
this.proxyUsername = username;
|
||||
this.proxyPassword = password;
|
||||
}
|
||||
|
||||
public boolean isStartTlsEnabled() { return startTlsEnabled; }
|
||||
public void setStartTlsEnabled(boolean enabled) { this.startTlsEnabled = enabled; }
|
||||
|
||||
public java.util.Map<String, String> getEnvironmentVariables() { return environmentVariables; }
|
||||
public void setEnvironmentVariables(java.util.Map<String, String> vars) {
|
||||
this.environmentVariables = (vars != null) ? new java.util.LinkedHashMap<>(vars) : new java.util.LinkedHashMap<>();
|
||||
}
|
||||
public void setEnvironmentVariable(String name, String value) {
|
||||
if (name != null) {
|
||||
if (value != null) this.environmentVariables.put(name, value);
|
||||
else this.environmentVariables.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public java.util.Map<String, String> getUserVariables() { return userVariables; }
|
||||
public void setUserVariables(java.util.Map<String, String> vars) {
|
||||
this.userVariables = (vars != null) ? new java.util.LinkedHashMap<>(vars) : new java.util.LinkedHashMap<>();
|
||||
}
|
||||
public void setUserVariable(String name, String value) {
|
||||
if (name != null) {
|
||||
if (value != null) this.userVariables.put(name, value);
|
||||
else this.userVariables.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a host connection string which may include prefixes for TLS (e.g. "L:host:port", "ssl:host:port", "y:host:port"),
|
||||
* plain TN3270 (e.g. "P:host:port", "plain:host:port", "non-e:host:port"), or standard "host:port" formats.
|
||||
* plain TN3270 (e.g. "P:host:port", "plain:host:port", "non-e:host:port"), proxy flags (e.g. "--proxy=http://proxy:8080 host:23"),
|
||||
* or standard "host:port" formats.
|
||||
*/
|
||||
public static ConnectionConfig parseHostString(String hostStr, int defaultPort, TerminalModel defaultModel) {
|
||||
if (hostStr == null || hostStr.trim().isEmpty()) {
|
||||
@@ -101,6 +292,53 @@ public class ConnectionConfig {
|
||||
String s = hostStr.trim();
|
||||
boolean tls = false;
|
||||
boolean tn3270e = true;
|
||||
boolean dynamic = false;
|
||||
int dynRows = 62;
|
||||
int dynCols = 160;
|
||||
|
||||
// Parse --proxy=<url> or -proxy=<url> flags
|
||||
ProxyType pType = ProxyType.NONE;
|
||||
String pHost = null;
|
||||
int pPort = 0;
|
||||
String pUser = null;
|
||||
String pPass = null;
|
||||
|
||||
String[] tokens = s.split("\\s+");
|
||||
StringBuilder remaining = new StringBuilder();
|
||||
for (String tok : tokens) {
|
||||
if (tok.startsWith("--proxy=") || tok.startsWith("-proxy=")) {
|
||||
String proxyUrl = tok.substring(tok.indexOf('=') + 1).trim();
|
||||
try {
|
||||
java.net.URI uri = new java.net.URI(proxyUrl);
|
||||
String scheme = uri.getScheme() != null ? uri.getScheme().toLowerCase() : "http";
|
||||
if (scheme.equals("http") || scheme.equals("https")) {
|
||||
pType = ProxyType.HTTP;
|
||||
pPort = uri.getPort() > 0 ? uri.getPort() : 8080;
|
||||
} else if (scheme.equals("socks4") || scheme.equals("socks4a")) {
|
||||
pType = ProxyType.SOCKS4;
|
||||
pPort = uri.getPort() > 0 ? uri.getPort() : 1080;
|
||||
} else if (scheme.equals("socks5") || scheme.equals("socks")) {
|
||||
pType = ProxyType.SOCKS5;
|
||||
pPort = uri.getPort() > 0 ? uri.getPort() : 1080;
|
||||
}
|
||||
pHost = uri.getHost();
|
||||
String userInfo = uri.getUserInfo();
|
||||
if (userInfo != null) {
|
||||
int colon = userInfo.indexOf(':');
|
||||
if (colon >= 0) {
|
||||
pUser = userInfo.substring(0, colon);
|
||||
pPass = userInfo.substring(colon + 1);
|
||||
} else {
|
||||
pUser = userInfo;
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
} else {
|
||||
if (remaining.length() > 0) remaining.append(" ");
|
||||
remaining.append(tok);
|
||||
}
|
||||
}
|
||||
s = remaining.toString().trim();
|
||||
|
||||
// Parse chained x3270-style prefixes (e.g. "L:P:host:port" or "P:host:port")
|
||||
boolean prefixFound = true;
|
||||
@@ -128,6 +366,29 @@ public class ConnectionConfig {
|
||||
int colon = s.indexOf(':');
|
||||
s = s.substring(colon + 1);
|
||||
prefixFound = true;
|
||||
} else if (s.startsWith("D:") || s.startsWith("d:")) {
|
||||
dynamic = true;
|
||||
s = s.substring(2);
|
||||
prefixFound = true;
|
||||
} else if (s.toLowerCase().startsWith("dyn:") || s.toLowerCase().startsWith("dynamic:") ||
|
||||
s.toLowerCase().startsWith("dyn[") || s.toLowerCase().startsWith("dynamic[")) {
|
||||
int colon = s.indexOf(':');
|
||||
if (colon > 0) {
|
||||
String prefix = s.substring(0, colon);
|
||||
s = s.substring(colon + 1);
|
||||
prefixFound = true;
|
||||
dynamic = true;
|
||||
if (prefix.contains("[") && prefix.contains("]")) {
|
||||
String dim = prefix.substring(prefix.indexOf('[') + 1, prefix.indexOf(']'));
|
||||
String[] parts = dim.toLowerCase().split("x");
|
||||
if (parts.length == 2) {
|
||||
try {
|
||||
dynRows = Integer.parseInt(parts[0].trim());
|
||||
dynCols = Integer.parseInt(parts[1].trim());
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,6 +417,12 @@ public class ConnectionConfig {
|
||||
ConnectionConfig config = new ConnectionConfig(host, port, defaultModel != null ? defaultModel : TerminalModel.IBM_3279_4);
|
||||
config.setUseTls(tls);
|
||||
config.setTn3270eEnabled(tn3270e);
|
||||
if (dynamic) {
|
||||
config.setDynamicDimensions(dynRows, dynCols);
|
||||
}
|
||||
if (pType != ProxyType.NONE && pHost != null) {
|
||||
config.setProxy(pType, pHost, pPort, pUser, pPass);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -166,6 +433,25 @@ public class ConnectionConfig {
|
||||
if (terminalName != null) {
|
||||
return terminalName;
|
||||
}
|
||||
if (isDynamicModel()) {
|
||||
return extendedDataStream ? "IBM-DYNAMIC-E" : "IBM-DYNAMIC";
|
||||
}
|
||||
return extendedDataStream ? model.getTerminalType() : model.getBaseTerminalType();
|
||||
}
|
||||
|
||||
public String getAssociatedPrinterLu() { return associatedPrinterLu; }
|
||||
public void setAssociatedPrinterLu(String printerLu) { this.associatedPrinterLu = printerLu; }
|
||||
|
||||
public haus.nightmare.lib3270j.printer.PrinterConfig toPrinterConfig() {
|
||||
haus.nightmare.lib3270j.printer.PrinterConfig pcfg = new haus.nightmare.lib3270j.printer.PrinterConfig(host, port);
|
||||
pcfg.setUseTls(useTls);
|
||||
pcfg.setTlsVerifyCert(tlsVerifyCert);
|
||||
pcfg.setCertificateVerifier(certificateVerifier);
|
||||
pcfg.setSslProtocol(sslProtocol);
|
||||
pcfg.setConnectTimeoutMs(connectTimeoutMs);
|
||||
pcfg.setPrinterLuName(associatedPrinterLu);
|
||||
pcfg.setAssociatedDisplayLuName(luName);
|
||||
pcfg.setCodePage(codePage);
|
||||
return pcfg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,4 +59,32 @@ public enum ConnectionState {
|
||||
public boolean isFullSession() {
|
||||
return isNvt() || is3270();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map to IBM Host On-Demand ECL connection state integer codes.
|
||||
* 0 = Disconnected, 1 = Connecting/Resolving, 2 = Connected (NVT/Unbound), 3 = Bound (Full 3270 session).
|
||||
*/
|
||||
public int toHoDStateCode() {
|
||||
switch (this) {
|
||||
case NOT_CONNECTED:
|
||||
return 0;
|
||||
case RECONNECTING:
|
||||
case RESOLVING:
|
||||
case TCP_PENDING:
|
||||
case TLS_PENDING:
|
||||
case PROXY_PENDING:
|
||||
case TELNET_PENDING:
|
||||
return 1;
|
||||
case CONNECTED_NVT:
|
||||
case CONNECTED_NVT_CHAR:
|
||||
case CONNECTED_UNBOUND:
|
||||
case CONNECTED_E_NVT:
|
||||
case CONNECTED_SSCP:
|
||||
return 2;
|
||||
case CONNECTED_3270:
|
||||
case CONNECTED_TN3270E:
|
||||
default:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,22 +39,55 @@ public class Telnet3270Client {
|
||||
private final DataStreamProcessor dsProcessor;
|
||||
private final TelnetFSM fsm;
|
||||
private final InputProcessor inputProcessor;
|
||||
private final haus.nightmare.lib3270j.ecl.ECLPS ps;
|
||||
private final haus.nightmare.lib3270j.ecl.ECLOIA oia;
|
||||
private final haus.nightmare.lib3270j.ecl.ECLXfer xfer;
|
||||
private TelnetConnection connection;
|
||||
|
||||
public Telnet3270Client(ConnectionConfig config) {
|
||||
this.config = config;
|
||||
this.translator = new EbcdicTranslator();
|
||||
this.screenBuffer = new ScreenBuffer(config.getModel(), translator);
|
||||
this.translator = new EbcdicTranslator(config.getCodePage());
|
||||
if (config.isDynamicModel() || (config.getModel() != null && config.getModel().isDynamic())) {
|
||||
this.screenBuffer = new ScreenBuffer(
|
||||
haus.nightmare.lib3270j.protocol.DS3270Constants.MODEL_2_ROWS,
|
||||
haus.nightmare.lib3270j.protocol.DS3270Constants.MODEL_2_COLS,
|
||||
config.getDynamicRows(),
|
||||
config.getDynamicCols(),
|
||||
translator);
|
||||
} else {
|
||||
this.screenBuffer = new ScreenBuffer(config.getModel(), translator);
|
||||
}
|
||||
this.dsProcessor = new DataStreamProcessor(screenBuffer, translator);
|
||||
this.dsProcessor.getQueryReplyBuilder().setGraphicsMode(config.getGraphicsMode());
|
||||
this.fsm = new TelnetFSM(config, screenBuffer, dsProcessor);
|
||||
this.inputProcessor = new InputProcessor(screenBuffer, translator, fsm);
|
||||
this.ps = new haus.nightmare.lib3270j.ecl.ECLPS(screenBuffer, inputProcessor, translator);
|
||||
this.oia = new haus.nightmare.lib3270j.ecl.ECLOIA(screenBuffer, inputProcessor, fsm);
|
||||
this.inputProcessor.setOIA(oia);
|
||||
this.xfer = new haus.nightmare.lib3270j.ecl.ECLXfer(screenBuffer, inputProcessor, dsProcessor, translator);
|
||||
|
||||
// Wire up the output sender: DSProcessor -> FSM -> TelnetConnection
|
||||
dsProcessor.setOutputSender(fsm::send3270Data);
|
||||
dsProcessor.setInputProcessor(inputProcessor);
|
||||
inputProcessor.setGraphicsPlane(dsProcessor.getGraphicsPlane());
|
||||
inputProcessor.setGocaDecoder(dsProcessor.getGocaDecoder());
|
||||
|
||||
// Wire screen update to ECLXfer for CUT mode screen tracking and ECLPS for event dispatching
|
||||
addScreenUpdateListener(new haus.nightmare.lib3270j.listener.ScreenUpdateListener() {
|
||||
@Override public void onScreenUpdated() {
|
||||
xfer.onScreenUpdated();
|
||||
ps.notifyPSUpdate(0, 0, screenBuffer.getRows() - 1, screenBuffer.getCols() - 1, true);
|
||||
}
|
||||
@Override public void onCursorMoved(int oldAddress, int newAddress) {
|
||||
ps.notifyCursorMoved(oldAddress, newAddress);
|
||||
}
|
||||
@Override public void onScreenSizeChanged(int rows, int cols) {
|
||||
ps.notifyScreenResized(rows, cols);
|
||||
}
|
||||
@Override public void onSoundAlarm() {
|
||||
ps.notifyAlarm();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,6 +109,52 @@ public class Telnet3270Client {
|
||||
fsm.onConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured host synchronously, blocking until the full data session is established
|
||||
* (or timeout expires).
|
||||
* @param timeoutMs maximum time to wait in milliseconds
|
||||
* @return true if successfully connected, false if timed out
|
||||
* @throws IOException if network connection fails
|
||||
*/
|
||||
public boolean connect(long timeoutMs) throws IOException {
|
||||
connect();
|
||||
long start = System.currentTimeMillis();
|
||||
while (System.currentTimeMillis() - start < timeoutMs) {
|
||||
ConnectionState state = getConnectionState();
|
||||
if (state.isFullSession() || (state.isFullyConnected() && isConnected())) {
|
||||
return true;
|
||||
}
|
||||
if (state == ConnectionState.NOT_CONNECTED && !isConnected()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(25);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return getConnectionState().isFullSession() || isConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the configured host synchronously and block until the presentation space matches
|
||||
* the specified screen descriptor.
|
||||
* @param timeoutMs maximum time to wait in milliseconds
|
||||
* @param desc ECLScreenDesc descriptor to match against
|
||||
* @return true if successfully connected and screen matched, false otherwise
|
||||
* @throws IOException if network connection fails
|
||||
*/
|
||||
public boolean connect(long timeoutMs, haus.nightmare.lib3270j.ecl.ECLScreenDesc desc) throws IOException {
|
||||
long start = System.currentTimeMillis();
|
||||
if (!connect(timeoutMs)) {
|
||||
return false;
|
||||
}
|
||||
long elapsed = System.currentTimeMillis() - start;
|
||||
long remaining = Math.max(1, timeoutMs - elapsed);
|
||||
return ps.waitForScreen(desc, remaining);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from the host.
|
||||
*/
|
||||
@@ -87,6 +166,35 @@ public class Telnet3270Client {
|
||||
fsm.onDisconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from host with synchronous teardown.
|
||||
* @param timeoutMs maximum time to wait for graceful disconnect in milliseconds
|
||||
*/
|
||||
public void disconnect(long timeoutMs) {
|
||||
disconnect();
|
||||
long start = System.currentTimeMillis();
|
||||
while (System.currentTimeMillis() - start < timeoutMs) {
|
||||
if (getConnectionState() == ConnectionState.NOT_CONNECTED && !isConnected()) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(20);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** HoD StopCommunication compatibility alias. */
|
||||
public void stopCommunication() {
|
||||
disconnect();
|
||||
}
|
||||
|
||||
public void stopCommunication(long timeoutMs) {
|
||||
disconnect(timeoutMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if connected (any state past TCP pending).
|
||||
*/
|
||||
@@ -100,11 +208,28 @@ public class Telnet3270Client {
|
||||
fsm.addConnectionListener(l);
|
||||
}
|
||||
|
||||
public void removeConnectionListener(ConnectionListener l) {
|
||||
fsm.removeConnectionListener(l);
|
||||
}
|
||||
|
||||
public void addScreenUpdateListener(ScreenUpdateListener l) {
|
||||
fsm.addScreenUpdateListener(l);
|
||||
dsProcessor.addScreenUpdateListener(l);
|
||||
}
|
||||
|
||||
public void removeScreenUpdateListener(ScreenUpdateListener l) {
|
||||
fsm.removeScreenUpdateListener(l);
|
||||
dsProcessor.removeScreenUpdateListener(l);
|
||||
}
|
||||
|
||||
public void addSCSInboundListener(haus.nightmare.lib3270j.listener.SCSInboundListener l) {
|
||||
fsm.addSCSInboundListener(l);
|
||||
}
|
||||
|
||||
public void removeSCSInboundListener(haus.nightmare.lib3270j.listener.SCSInboundListener l) {
|
||||
fsm.removeSCSInboundListener(l);
|
||||
}
|
||||
|
||||
// ========== Screen access ==========
|
||||
|
||||
/** Get the screen buffer for rendering. */
|
||||
@@ -113,6 +238,12 @@ public class Telnet3270Client {
|
||||
/** Get the EBCDIC translator. */
|
||||
public EbcdicTranslator getTranslator() { return translator; }
|
||||
|
||||
/** Get the active Code Page identifier. */
|
||||
public String getCodePage() { return translator.getCodePageId(); }
|
||||
|
||||
/** Set the active Code Page identifier. */
|
||||
public void setCodePage(String codePageId) { translator.setCodePage(codePageId); }
|
||||
|
||||
/** Get the current connection state. */
|
||||
public ConnectionState getConnectionState() { return fsm.getConnectionState(); }
|
||||
|
||||
@@ -122,9 +253,27 @@ public class Telnet3270Client {
|
||||
/** Get the data stream processor. */
|
||||
public DataStreamProcessor getDataStreamProcessor() { return dsProcessor; }
|
||||
|
||||
/** Get the underlying telnet connection. */
|
||||
public TelnetConnection getConnection() { return connection; }
|
||||
|
||||
/** Get the connection config. */
|
||||
public ConnectionConfig getConfig() { return config; }
|
||||
|
||||
/** Get the ECL Presentation Space API. */
|
||||
public haus.nightmare.lib3270j.ecl.ECLPS getPS() { return ps; }
|
||||
|
||||
/** Get the ECL Operator Information Area API. */
|
||||
public haus.nightmare.lib3270j.ecl.ECLOIA getOIA() { return oia; }
|
||||
|
||||
/** Get the ECL File Transfer API. */
|
||||
public haus.nightmare.lib3270j.ecl.ECLXfer getXfer() { return xfer; }
|
||||
|
||||
/** Get the list of all fields currently on screen. */
|
||||
public haus.nightmare.lib3270j.ecl.ECLFieldList getFieldList() { return ps.getFieldList(); }
|
||||
|
||||
/** Send IBM ECL bracketed mnemonic keystrokes (e.g. "USER[tab]PASS[enter]"). */
|
||||
public void sendKeys(String keys) { inputProcessor.sendKeys(keys); }
|
||||
|
||||
/** Set a custom or interactive TLS certificate verifier callback. */
|
||||
public void setTlsCertificateVerifier(haus.nightmare.lib3270j.tls.TlsCertificateVerifier verifier) {
|
||||
config.setCertificateVerifier(verifier);
|
||||
@@ -132,14 +281,71 @@ public class Telnet3270Client {
|
||||
|
||||
/** Get the active SSLSession if connected over TLS, or null. */
|
||||
public javax.net.ssl.SSLSession getSslSession() {
|
||||
return connection.getSslSession();
|
||||
return connection != null ? connection.getSslSession() : null;
|
||||
}
|
||||
|
||||
// ========== Convenience input methods ==========
|
||||
/** Get the NVT processor for ASCII / ANSI terminal processing. */
|
||||
public haus.nightmare.lib3270j.nvt.NvtProcessor getNvtProcessor() {
|
||||
return fsm.getNvtProcessor();
|
||||
}
|
||||
|
||||
/** Get the Telnet state machine. */
|
||||
public TelnetFSM getTelnetFSM() {
|
||||
return fsm;
|
||||
}
|
||||
|
||||
/** Send an NVT ASCII character in NVT mode. */
|
||||
public void sendNVTChar(char c) throws IOException {
|
||||
fsm.sendNVTChar(c);
|
||||
}
|
||||
|
||||
/** Send an NVT ASCII string in NVT mode. */
|
||||
public void sendNVTString(String s) throws IOException {
|
||||
fsm.sendNVTString(s);
|
||||
}
|
||||
|
||||
/** Send an NVT key event mapped through NVT processor. */
|
||||
public boolean sendNVTKey(int keyCode, char keyChar, boolean shift, boolean ctrl, boolean alt) throws IOException {
|
||||
haus.nightmare.lib3270j.nvt.NvtProcessor nvt = getNvtProcessor();
|
||||
if (nvt != null) {
|
||||
String seq = nvt.mapKey(keyCode, keyChar, shift, ctrl, alt);
|
||||
if (seq != null && !seq.isEmpty()) {
|
||||
fsm.sendNvtData(seq.getBytes(java.nio.charset.StandardCharsets.US_ASCII));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addNvtTitleListener(haus.nightmare.lib3270j.nvt.NvtProcessor.NvtTitleListener l) {
|
||||
if (getNvtProcessor() != null) {
|
||||
getNvtProcessor().addTitleListener(l);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeNvtTitleListener(haus.nightmare.lib3270j.nvt.NvtProcessor.NvtTitleListener l) {
|
||||
if (getNvtProcessor() != null) {
|
||||
getNvtProcessor().removeTitleListener(l);
|
||||
}
|
||||
}
|
||||
|
||||
public void setNvtClipboardHandler(haus.nightmare.lib3270j.nvt.NvtProcessor.ClipboardHandler h) {
|
||||
if (getNvtProcessor() != null) {
|
||||
getNvtProcessor().setClipboardHandler(h);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Convenience input methods & HoD compatibility ==========
|
||||
|
||||
/** Type a character at the cursor position. */
|
||||
public void typeCharacter(char ch) { inputProcessor.typeCharacter(ch); }
|
||||
|
||||
/** Process a character at the current cursor position. */
|
||||
public void processChar(char ch) { inputProcessor.processChar(ch); }
|
||||
|
||||
/** Process a character with explicit position and insert mode flag. */
|
||||
public void processChar(char ch, int pos, boolean insert) { inputProcessor.processChar(ch, pos, insert); }
|
||||
|
||||
/** Type a string at the cursor position. */
|
||||
public void typeString(String s) {
|
||||
for (char ch : s.toCharArray()) {
|
||||
@@ -156,6 +362,7 @@ public class Telnet3270Client {
|
||||
public void sendEnter() {
|
||||
inputProcessor.sendAid(haus.nightmare.lib3270j.protocol.DS3270Constants.AID_ENTER);
|
||||
}
|
||||
public void processEnter() { inputProcessor.processEnter(); }
|
||||
|
||||
/** Send a PF key (1-24). */
|
||||
public void sendPF(int number) {
|
||||
@@ -177,6 +384,7 @@ public class Telnet3270Client {
|
||||
}
|
||||
inputProcessor.sendAid(aid);
|
||||
}
|
||||
public void processPF(int number) { inputProcessor.processPF(number); }
|
||||
|
||||
/** Send a PA key (1-3). */
|
||||
public void sendPA(int number) {
|
||||
@@ -189,44 +397,104 @@ public class Telnet3270Client {
|
||||
}
|
||||
inputProcessor.sendAid(aid);
|
||||
}
|
||||
public void processPA(int number) { inputProcessor.processPA(number); }
|
||||
|
||||
/** Send Clear key. */
|
||||
public void sendClear() {
|
||||
inputProcessor.sendAid(haus.nightmare.lib3270j.protocol.DS3270Constants.AID_CLEAR);
|
||||
}
|
||||
public void processClear() { inputProcessor.processClear(); }
|
||||
|
||||
/** Move cursor up. */
|
||||
public void cursorUp() { inputProcessor.cursorUp(); }
|
||||
public void cursorUp() { inputProcessor.cursorUp(); }
|
||||
public void processCursorUp() { inputProcessor.processCursorUp(); }
|
||||
|
||||
/** Move cursor down. */
|
||||
public void cursorDown() { inputProcessor.cursorDown(); }
|
||||
public void cursorDown() { inputProcessor.cursorDown(); }
|
||||
public void processCursorDown() { inputProcessor.processCursorDown(); }
|
||||
|
||||
/** Move cursor left. */
|
||||
public void cursorLeft() { inputProcessor.cursorLeft(); }
|
||||
public void cursorLeft() { inputProcessor.cursorLeft(); }
|
||||
public void processCursorLeft() { inputProcessor.processCursorLeft(); }
|
||||
|
||||
/** Move cursor right. */
|
||||
public void cursorRight() { inputProcessor.cursorRight(); }
|
||||
public void cursorRight() { inputProcessor.cursorRight(); }
|
||||
public void processCursorRight() { inputProcessor.processCursorRight(); }
|
||||
|
||||
/** Move cursor to home position. */
|
||||
public void cursorHome() { inputProcessor.cursorHome(); }
|
||||
public void cursorHome() { inputProcessor.cursorHome(); }
|
||||
public void processHome() { inputProcessor.processHome(); }
|
||||
|
||||
/** Tab to next unprotected field. */
|
||||
public void tab() { inputProcessor.tab(); }
|
||||
public void tab() { inputProcessor.tab(); }
|
||||
public void processTab() { inputProcessor.processTab(); }
|
||||
|
||||
/** Back-tab to previous unprotected field. */
|
||||
public void backTab() { inputProcessor.backTab(); }
|
||||
public void backTab() { inputProcessor.backTab(); }
|
||||
public void processBackTab() { inputProcessor.processBackTab(); }
|
||||
|
||||
/** Move cursor to next line. */
|
||||
public void newline() { inputProcessor.newline(); }
|
||||
public void newline() { inputProcessor.newline(); }
|
||||
public void processNewline() { inputProcessor.processNewline(); }
|
||||
|
||||
/** Delete character under cursor. */
|
||||
public void deleteChar() { inputProcessor.deleteChar(); }
|
||||
public void processDelete() { inputProcessor.processDelete(); }
|
||||
|
||||
/** Backspace character before cursor. */
|
||||
public void backspace() { inputProcessor.backspace(); }
|
||||
public void processBackspace() { inputProcessor.processBackspace(); }
|
||||
|
||||
/** Erase to end of field. */
|
||||
public void eraseEof() { inputProcessor.eraseEof(); }
|
||||
public void processEraseEOF() { inputProcessor.processEraseEOF(); }
|
||||
|
||||
/** Erase all unprotected fields. */
|
||||
public void eraseInput() { inputProcessor.eraseInput(); }
|
||||
public void eraseInput() { inputProcessor.eraseInput(); }
|
||||
public void processEraseInput() { inputProcessor.processEraseInput(); }
|
||||
|
||||
/** Insert Duplicate order. */
|
||||
public void dup() { inputProcessor.dup(); }
|
||||
public void processDup() { inputProcessor.processDup(); }
|
||||
|
||||
/** Insert Field Mark order. */
|
||||
public void fieldMark() { inputProcessor.fieldMark(); }
|
||||
public void fieldMark() { inputProcessor.fieldMark(); }
|
||||
public void processFieldMark() { inputProcessor.processFieldMark(); }
|
||||
|
||||
/** Toggle Insert Mode. */
|
||||
public void toggleInsert() { inputProcessor.setInsertMode(!inputProcessor.isInsertMode()); }
|
||||
public void processToggleInsert() { inputProcessor.processToggleInsert(); }
|
||||
|
||||
/** Move word left. */
|
||||
public void processWordLeft() { inputProcessor.processWordLeft(); }
|
||||
|
||||
/** Move word right. */
|
||||
public void processWordRight() { inputProcessor.processWordRight(); }
|
||||
|
||||
/** Move to field end. */
|
||||
public void processFieldEnd() { inputProcessor.processFieldEnd(); }
|
||||
|
||||
/** Attention key. */
|
||||
public void attn() { inputProcessor.attn(); }
|
||||
public void processAttn() { inputProcessor.processAttn(); }
|
||||
|
||||
/** SysReq key. */
|
||||
public void sysReq() { inputProcessor.sysReq(); }
|
||||
/** Reset (unlock keyboard). */
|
||||
public void reset() { inputProcessor.reset(); }
|
||||
public void sysReq() { inputProcessor.sysReq(); }
|
||||
public void processSysReq() { inputProcessor.processSysReq(); }
|
||||
|
||||
/** Reset (unlock keyboard, reset OIA). */
|
||||
public void reset() { inputProcessor.reset(); }
|
||||
public void processReset() { inputProcessor.processReset(); }
|
||||
|
||||
/** Trigger 3270 CURSR SEL (Cursor Select) key at current cursor position. */
|
||||
public boolean cursorSelect() { return inputProcessor.cursorSelect(); }
|
||||
/** Trigger Light Pen selection at the specified screen address. */
|
||||
public boolean cursorSelect() { return inputProcessor.cursorSelect(); }
|
||||
public boolean processCurSel() { return inputProcessor.processCurSel(); }
|
||||
public boolean processCursorSelect() { return inputProcessor.processCursorSelect(); }
|
||||
|
||||
/** Trigger Light Pen selection at the specified screen address or cursor position. */
|
||||
public boolean lightPenSelect(int address) { return inputProcessor.lightPenSelect(address); }
|
||||
public boolean processLightPen() { return inputProcessor.processLightPen(); }
|
||||
public boolean processLightPen(int addr) { return inputProcessor.processLightPen(addr); }
|
||||
|
||||
public haus.nightmare.lib3270j.graphics.ProgramSymbolManager getProgramSymbolManager() {
|
||||
return dsProcessor.getProgramSymbolManager();
|
||||
|
||||
@@ -14,7 +14,8 @@ public enum TerminalModel {
|
||||
IBM_3279_2(2, true, MODEL_2_ROWS, MODEL_2_COLS, MODEL_2_ROWS, MODEL_2_COLS),
|
||||
IBM_3279_3(3, true, MODEL_2_ROWS, MODEL_2_COLS, MODEL_3_ROWS, MODEL_3_COLS),
|
||||
IBM_3279_4(4, true, MODEL_2_ROWS, MODEL_2_COLS, MODEL_4_ROWS, MODEL_4_COLS),
|
||||
IBM_3279_5(5, true, MODEL_2_ROWS, MODEL_2_COLS, MODEL_5_ROWS, MODEL_5_COLS);
|
||||
IBM_3279_5(5, true, MODEL_2_ROWS, MODEL_2_COLS, MODEL_5_ROWS, MODEL_5_COLS),
|
||||
IBM_DYNAMIC(0, true, MODEL_2_ROWS, MODEL_2_COLS, 62, 160);
|
||||
|
||||
private final int modelNumber;
|
||||
private final boolean color;
|
||||
@@ -40,12 +41,17 @@ public enum TerminalModel {
|
||||
public int getDefaultCols() { return defaultCols; }
|
||||
public int getAlternateRows() { return alternateRows; }
|
||||
public int getAlternateCols() { return alternateCols; }
|
||||
public boolean isDynamic() { return modelNumber == 0; }
|
||||
|
||||
/**
|
||||
* Returns the terminal type string for TN3270E negotiation.
|
||||
* e.g., "IBM-3279-4-E" for a color model 4 with extended data stream.
|
||||
* e.g., "IBM-3279-4-E" for a color model 4 with extended data stream,
|
||||
* or "IBM-DYNAMIC-E" for dynamic model.
|
||||
*/
|
||||
public String getTerminalType() {
|
||||
if (modelNumber == 0) {
|
||||
return "IBM-DYNAMIC-E";
|
||||
}
|
||||
return String.format("IBM-327%c-%d-E", color ? '9' : '8', modelNumber);
|
||||
}
|
||||
|
||||
@@ -53,6 +59,9 @@ public enum TerminalModel {
|
||||
* Returns the base terminal type without "-E" suffix (for non-extended mode).
|
||||
*/
|
||||
public String getBaseTerminalType() {
|
||||
if (modelNumber == 0) {
|
||||
return "IBM-DYNAMIC";
|
||||
}
|
||||
return String.format("IBM-327%c-%d", color ? '9' : '8', modelNumber);
|
||||
}
|
||||
|
||||
@@ -60,6 +69,9 @@ public enum TerminalModel {
|
||||
* Look up a model by number and color mode.
|
||||
*/
|
||||
public static TerminalModel forModel(int number, boolean isColor) {
|
||||
if (number == 0) {
|
||||
return IBM_DYNAMIC;
|
||||
}
|
||||
for (TerminalModel m : values()) {
|
||||
if (m.modelNumber == number && m.color == isColor) {
|
||||
return m;
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base implementation for Single-Byte Character Set (SBCS) EBCDIC Code Pages.
|
||||
*/
|
||||
public abstract class AbstractCodePage implements CodePage {
|
||||
|
||||
protected final String id;
|
||||
protected final String description;
|
||||
protected final int cpgid;
|
||||
protected final int cgcsgid;
|
||||
|
||||
protected final int[] toUnicode = new int[256];
|
||||
protected final int[] toEbcdic = new int[256];
|
||||
protected final Map<Character, Integer> extendedToEbcdic = new HashMap<>();
|
||||
|
||||
public AbstractCodePage(String id, String description, int cpgid, int cgcsgid, int[] unicodeMapping) {
|
||||
this(id, description, cpgid, cgcsgid, null, unicodeMapping);
|
||||
}
|
||||
|
||||
public AbstractCodePage(String id, String description, int cpgid, int cgcsgid, String javaCharsetName, int[] unicodeMapping) {
|
||||
this.id = id;
|
||||
this.description = description;
|
||||
this.cpgid = cpgid;
|
||||
this.cgcsgid = cgcsgid;
|
||||
|
||||
int[] map = loadMapping(javaCharsetName, unicodeMapping);
|
||||
System.arraycopy(map, 0, this.toUnicode, 0, 256);
|
||||
initReverseMapping();
|
||||
}
|
||||
|
||||
public static int[] loadMapping(String charsetName, int[] fallback) {
|
||||
if (charsetName != null) {
|
||||
String[] candidates = {
|
||||
charsetName,
|
||||
"IBM" + charsetName,
|
||||
"Cp" + charsetName,
|
||||
"IBM-" + charsetName,
|
||||
"x-IBM" + charsetName
|
||||
};
|
||||
for (String name : candidates) {
|
||||
try {
|
||||
if (Charset.isSupported(name)) {
|
||||
Charset cs = Charset.forName(name);
|
||||
int[] map = new int[256];
|
||||
for (int i = 0; i < 256; i++) {
|
||||
byte[] b = new byte[]{(byte) i};
|
||||
String s = new String(b, cs);
|
||||
map[i] = (!s.isEmpty() && s.charAt(0) != '\uFFFD') ? s.charAt(0) : (fallback != null ? fallback[i] : 0);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
return fallback != null ? fallback : new int[256];
|
||||
}
|
||||
|
||||
protected void initReverseMapping() {
|
||||
Arrays.fill(toEbcdic, -1);
|
||||
for (int i = 0; i < 256; i++) {
|
||||
int uc = toUnicode[i];
|
||||
if (uc >= 0 && uc < 256) {
|
||||
if (toEbcdic[uc] == -1) {
|
||||
toEbcdic[uc] = i;
|
||||
}
|
||||
} else if (uc >= 256) {
|
||||
extendedToEbcdic.putIfAbsent((char) uc, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCodePageId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCpgid() {
|
||||
return cpgid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCgcsgid() {
|
||||
return cgcsgid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDBCS() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char ebcdicToUnicode(int ebc) {
|
||||
return (char) toUnicode[ebc & 0xFF];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int unicodeToEbcdic(char unicode) {
|
||||
int u = unicode;
|
||||
if (u >= 0 && u < 256) {
|
||||
return toEbcdic[u];
|
||||
}
|
||||
Integer val = extendedToEbcdic.get(unicode);
|
||||
return val != null ? val : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte unicodeToEbcdicSafe(char unicode) {
|
||||
int ebc = unicodeToEbcdic(unicode);
|
||||
return (byte) (ebc >= 0 ? ebc : 0x40);
|
||||
}
|
||||
|
||||
@Override
|
||||
public char dbcsToUnicode(int b1, int b2) {
|
||||
return '\uFFFD';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int unicodeToDbcs(char unicode) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String ebcdicToString(byte[] ebcdic, int offset, int length) {
|
||||
if (ebcdic == null || length <= 0) return "";
|
||||
char[] chars = new char[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
chars[i] = ebcdicToUnicode(ebcdic[offset + i]);
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] stringToEbcdic(String s) {
|
||||
if (s == null || s.isEmpty()) return new byte[0];
|
||||
byte[] bytes = new byte[s.length()];
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
bytes[i] = unicodeToEbcdicSafe(s.charAt(i));
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return id + " (" + description + ")";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.charset.CodingErrorAction;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base implementation for Mixed Double-Byte Character Set (DBCS) EBCDIC Code Pages
|
||||
* (e.g. Japanese Katakana 930, Japanese Latin 939, Chinese 935/937/1388, Korean 933).
|
||||
*/
|
||||
public abstract class AbstractDBCSCodePage extends AbstractCodePage {
|
||||
|
||||
public static final int SO = 0x0E; // Shift Out (enter DBCS mode)
|
||||
public static final int SI = 0x0F; // Shift In (enter SBCS mode)
|
||||
|
||||
protected final Map<Integer, Character> dbcsToUnicodeMap = new HashMap<>();
|
||||
protected final Map<Character, Integer> unicodeToDbcsMap = new HashMap<>();
|
||||
protected Charset nioCharset;
|
||||
|
||||
public AbstractDBCSCodePage(String id, String description, int cpgid, int cgcsgid,
|
||||
int[] sbcsMapping, String nioCharsetName) {
|
||||
super(id, description, cpgid, cgcsgid, sbcsMapping);
|
||||
if (nioCharsetName != null) {
|
||||
try {
|
||||
if (Charset.isSupported(nioCharsetName)) {
|
||||
this.nioCharset = Charset.forName(nioCharsetName);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
// Initialize ideographic space (0x4040 <-> \u3000)
|
||||
registerDbcsPair(0x4040, '\u3000');
|
||||
}
|
||||
|
||||
public void registerDbcsPair(int ebcdicDBCS, char unicodeChar) {
|
||||
dbcsToUnicodeMap.put(ebcdicDBCS, unicodeChar);
|
||||
unicodeToDbcsMap.put(unicodeChar, ebcdicDBCS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDBCS() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char dbcsToUnicode(int b1, int b2) {
|
||||
int key = ((b1 & 0xFF) << 8) | (b2 & 0xFF);
|
||||
Character c = dbcsToUnicodeMap.get(key);
|
||||
if (c != null) {
|
||||
return c;
|
||||
}
|
||||
|
||||
if (nioCharset != null) {
|
||||
try {
|
||||
byte[] raw = new byte[] { (byte) SO, (byte) b1, (byte) b2, (byte) SI };
|
||||
CharBuffer cb = nioCharset.decode(ByteBuffer.wrap(raw));
|
||||
if (cb.hasRemaining()) {
|
||||
char decoded = cb.get();
|
||||
if (decoded != '\uFFFD' && decoded != 0) {
|
||||
registerDbcsPair(key, decoded);
|
||||
return decoded;
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
return '?';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int unicodeToDbcs(char unicode) {
|
||||
Integer val = unicodeToDbcsMap.get(unicode);
|
||||
if (val != null) {
|
||||
return val;
|
||||
}
|
||||
|
||||
if (nioCharset != null) {
|
||||
try {
|
||||
CharsetEncoder encoder = nioCharset.newEncoder()
|
||||
.onMalformedInput(CodingErrorAction.REPLACE)
|
||||
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||
ByteBuffer bb = encoder.encode(CharBuffer.wrap(new char[]{unicode}));
|
||||
byte[] bytes = new byte[bb.remaining()];
|
||||
bb.get(bytes);
|
||||
|
||||
// Check if encoded as SO + b1 + b2 + SI or b1 + b2
|
||||
if (bytes.length >= 4 && (bytes[0] & 0xFF) == SO) {
|
||||
int key = ((bytes[1] & 0xFF) << 8) | (bytes[2] & 0xFF);
|
||||
registerDbcsPair(key, unicode);
|
||||
return key;
|
||||
} else if (bytes.length == 2) {
|
||||
int key = ((bytes[0] & 0xFF) << 8) | (bytes[1] & 0xFF);
|
||||
registerDbcsPair(key, unicode);
|
||||
return key;
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String ebcdicToString(byte[] ebcdic, int offset, int length) {
|
||||
return ebcdicToString(ebcdic, offset, length, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate an EBCDIC byte array slice to a Unicode String with optional SO/SI escape preservation.
|
||||
*
|
||||
* @param ebcdic source byte array
|
||||
* @param offset start offset
|
||||
* @param length number of bytes
|
||||
* @param preserveSOSI if true, preserves Shift-Out (\u000E) and Shift-In (\u000F) control characters
|
||||
* @return translated Unicode String
|
||||
*/
|
||||
public String ebcdicToString(byte[] ebcdic, int offset, int length, boolean preserveSOSI) {
|
||||
if (ebcdic == null || length <= 0) return "";
|
||||
|
||||
StringBuilder sb = new StringBuilder(length);
|
||||
boolean inDBCS = false;
|
||||
int end = Math.min(ebcdic.length, offset + length);
|
||||
int i = offset;
|
||||
|
||||
while (i < end) {
|
||||
int b = ebcdic[i] & 0xFF;
|
||||
if (b == SO) {
|
||||
inDBCS = true;
|
||||
if (preserveSOSI) {
|
||||
sb.append((char) SO);
|
||||
}
|
||||
i++;
|
||||
} else if (b == SI) {
|
||||
inDBCS = false;
|
||||
if (preserveSOSI) {
|
||||
sb.append((char) SI);
|
||||
}
|
||||
i++;
|
||||
} else if (inDBCS) {
|
||||
if (i + 1 < end) {
|
||||
int b2 = ebcdic[i + 1] & 0xFF;
|
||||
if (b2 == SI) {
|
||||
// Orphaned single byte before SI
|
||||
inDBCS = false;
|
||||
if (preserveSOSI) {
|
||||
sb.append(ebcdicToUnicode(b));
|
||||
sb.append((char) SI);
|
||||
}
|
||||
i += 2;
|
||||
} else if (b2 == SO) {
|
||||
// Unmatched byte followed by another SO
|
||||
sb.append(ebcdicToUnicode(b));
|
||||
i++;
|
||||
} else {
|
||||
sb.append(dbcsToUnicode(b, b2));
|
||||
i += 2;
|
||||
}
|
||||
} else {
|
||||
// Trailing single byte inside unclosed DBCS shift
|
||||
sb.append(ebcdicToUnicode(b));
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
sb.append(ebcdicToUnicode(b));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] stringToEbcdic(String s) {
|
||||
return stringToEbcdic(s, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a Unicode String to EBCDIC byte array with optional parsing of embedded SO/SI markers.
|
||||
*
|
||||
* @param s input Unicode string
|
||||
* @param parseSOSIMarkers whether to interpret embedded \u000E (SO) and \u000F (SI) characters
|
||||
* @return EBCDIC byte array with balanced SO/SI framing
|
||||
*/
|
||||
public byte[] stringToEbcdic(String s, boolean parseSOSIMarkers) {
|
||||
if (s == null || s.isEmpty()) return new byte[0];
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(s.length() * 2);
|
||||
boolean inDBCS = false;
|
||||
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
|
||||
if (parseSOSIMarkers && c == (char) SO) {
|
||||
if (!inDBCS) {
|
||||
out.write(SO);
|
||||
inDBCS = true;
|
||||
}
|
||||
continue;
|
||||
} else if (parseSOSIMarkers && c == (char) SI) {
|
||||
if (inDBCS) {
|
||||
out.write(SI);
|
||||
inDBCS = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
int dbcsCode = unicodeToDbcs(c);
|
||||
|
||||
if (dbcsCode >= 0) {
|
||||
// Character is DBCS
|
||||
if (!inDBCS) {
|
||||
out.write(SO);
|
||||
inDBCS = true;
|
||||
}
|
||||
out.write((dbcsCode >> 8) & 0xFF);
|
||||
out.write(dbcsCode & 0xFF);
|
||||
} else {
|
||||
// Character is SBCS
|
||||
if (inDBCS) {
|
||||
out.write(SI);
|
||||
inDBCS = false;
|
||||
}
|
||||
out.write(unicodeToEbcdicSafe(c));
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-close unclosed DBCS shift sequence
|
||||
if (inDBCS) {
|
||||
out.write(SI);
|
||||
}
|
||||
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to decode an EBCDIC byte array to UTF-8 String preserving SO/SI control codes.
|
||||
*/
|
||||
public String ebcdicToUtf8WithSOSI(byte[] ebcdic) {
|
||||
if (ebcdic == null) return "";
|
||||
return ebcdicToString(ebcdic, 0, ebcdic.length, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to encode a UTF-8/Unicode String with embedded SO/SI markers to EBCDIC.
|
||||
*/
|
||||
public byte[] utf8WithSOSIToEbcdic(String s) {
|
||||
return stringToEbcdic(s, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* Interface defining character translation and metadata for EBCDIC code pages
|
||||
* conforming to IBM Host On-Demand (HoD v14) converters specification.
|
||||
*/
|
||||
public interface CodePage {
|
||||
|
||||
/**
|
||||
* Get the primary identifier for this code page (e.g. "037", "1047", "500", "273").
|
||||
*/
|
||||
String getCodePageId();
|
||||
|
||||
/**
|
||||
* Get a human-readable description (e.g. "US / Canada - EBCDIC").
|
||||
*/
|
||||
String getDescription();
|
||||
|
||||
/**
|
||||
* Get the Code Page Global ID (CPGID / CCSID) for 3270 query replies.
|
||||
*/
|
||||
int getCpgid();
|
||||
|
||||
/**
|
||||
* Get the Graphic Character Set Global ID (GCSGID) for 3270 query replies.
|
||||
*/
|
||||
int getCgcsgid();
|
||||
|
||||
/**
|
||||
* Translate an EBCDIC byte (0x00-0xFF) to a Unicode character.
|
||||
*/
|
||||
char ebcdicToUnicode(int ebc);
|
||||
|
||||
/**
|
||||
* Translate a Unicode character to an EBCDIC byte value.
|
||||
* Returns -1 if unmappable.
|
||||
*/
|
||||
int unicodeToEbcdic(char unicode);
|
||||
|
||||
/**
|
||||
* Translate a Unicode character to an EBCDIC byte value, returning a safe
|
||||
* fallback (default EBCDIC space 0x40) if unmappable.
|
||||
*/
|
||||
byte unicodeToEbcdicSafe(char unicode);
|
||||
|
||||
/**
|
||||
* Translate an EBCDIC byte array slice to a Unicode String.
|
||||
*/
|
||||
String ebcdicToString(byte[] ebcdic, int offset, int length);
|
||||
|
||||
/**
|
||||
* Translate a Unicode String to an EBCDIC byte array.
|
||||
*/
|
||||
byte[] stringToEbcdic(String s);
|
||||
|
||||
/**
|
||||
* Check if this code page is a double-byte (DBCS) / mixed SBCS+DBCS code page.
|
||||
*/
|
||||
boolean isDBCS();
|
||||
|
||||
/**
|
||||
* Translate a double-byte EBCDIC pair (b1, b2) to a Unicode character.
|
||||
* Returns '\uFFFD' or '?' if unmappable.
|
||||
*/
|
||||
char dbcsToUnicode(int b1, int b2);
|
||||
|
||||
/**
|
||||
* Translate a Unicode character to a double-byte EBCDIC code ((b1 << 8) | b2).
|
||||
* Returns -1 if unmappable.
|
||||
*/
|
||||
int unicodeToDbcs(char unicode);
|
||||
|
||||
/**
|
||||
* Convert an EBCDIC byte buffer to a char array matching IBM HoD conversion.
|
||||
*/
|
||||
default char[] convBuffByte2Char(byte[] buf, int offset, int length) {
|
||||
if (buf == null || length <= 0) return new char[0];
|
||||
char[] out = new char[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
out[i] = ebcdicToUnicode(buf[offset + i] & 0xFF);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a char array to an EBCDIC byte array matching IBM HoD conversion.
|
||||
*/
|
||||
default byte[] convBuffChar2Byte(char[] buf, int offset, int length) {
|
||||
if (buf == null || length <= 0) return new byte[0];
|
||||
byte[] out = new byte[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
out[i] = unicodeToEbcdicSafe(buf[offset + i]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a HODByteToCharConverter instance backed by this CodePage.
|
||||
*/
|
||||
default haus.nightmare.lib3270j.converters.HODByteToCharConverter getByteToCharConverter() {
|
||||
if (isDBCS()) {
|
||||
return new haus.nightmare.lib3270j.converters.ByteToCharDBCS_EBCDIC(this);
|
||||
} else {
|
||||
return new haus.nightmare.lib3270j.converters.ByteToCharSingleByte(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a HODCharToByteConverter instance backed by this CodePage.
|
||||
*/
|
||||
default haus.nightmare.lib3270j.converters.HODCharToByteConverter getCharToByteConverter() {
|
||||
if (isDBCS()) {
|
||||
return new haus.nightmare.lib3270j.converters.CharToByteDBCS_EBCDIC(this);
|
||||
} else {
|
||||
return new haus.nightmare.lib3270j.converters.CharToByteSingleByte(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to test if a pair of characters forms a Unicode surrogate pair.
|
||||
*/
|
||||
static boolean isSurrogate(char high, char low) {
|
||||
return Character.isSurrogatePair(high, low);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to test if a character is a high surrogate.
|
||||
*/
|
||||
static boolean isHighSurrogate(char c) {
|
||||
return Character.isHighSurrogate(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to test if a character is a low surrogate.
|
||||
*/
|
||||
static boolean isLowSurrogate(char c) {
|
||||
return Character.isLowSurrogate(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper matching IBM HoD CodePage.ComposeChar to combine characters.
|
||||
*/
|
||||
static boolean ComposeChar(char[] chars) {
|
||||
if (chars == null || chars.length < 2) return false;
|
||||
if (chars[1] >= '\u0300' && chars[1] <= '\u036F') {
|
||||
String decomposed = new String(chars, 0, 2);
|
||||
String normalized = java.text.Normalizer.normalize(decomposed, java.text.Normalizer.Form.NFC);
|
||||
if (normalized.length() == 1) {
|
||||
chars[0] = normalized.charAt(0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,488 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Registry and factory for 3270 EBCDIC Code Pages.
|
||||
* Manages built-in SBCS/DBCS codepages, alias normalization, and dynamic JVM Charset resolution.
|
||||
*/
|
||||
public class CodePageRegistry {
|
||||
|
||||
private static final Logger log = Logger.getLogger(CodePageRegistry.class.getName());
|
||||
|
||||
private static final Map<String, CodePage> CODE_PAGES = new LinkedHashMap<>();
|
||||
private static final Map<String, String> ALIASES = new HashMap<>();
|
||||
|
||||
static {
|
||||
// Register core SBCS codepages
|
||||
register(new Cp037());
|
||||
register(new Cp1047());
|
||||
register(new Cp500());
|
||||
register(new Cp273());
|
||||
register(new Cp277());
|
||||
register(new Cp278());
|
||||
register(new Cp280());
|
||||
register(new Cp284());
|
||||
register(new Cp285());
|
||||
register(new Cp297());
|
||||
register(new Cp870());
|
||||
register(new Cp871());
|
||||
register(new Cp875());
|
||||
register(new Cp1026());
|
||||
|
||||
// Register Euro variants (1140-1149)
|
||||
register(new CpEuroVariants.Cp1140());
|
||||
register(new CpEuroVariants.Cp1141());
|
||||
register(new CpEuroVariants.Cp1142());
|
||||
register(new CpEuroVariants.Cp1143());
|
||||
register(new CpEuroVariants.Cp1144());
|
||||
register(new CpEuroVariants.Cp1145());
|
||||
register(new CpEuroVariants.Cp1146());
|
||||
register(new CpEuroVariants.Cp1147());
|
||||
register(new CpEuroVariants.Cp1148());
|
||||
register(new CpEuroVariants.Cp1149());
|
||||
|
||||
// Register DBCS mixed codepages
|
||||
register(new Cp930());
|
||||
register(new Cp939());
|
||||
register(new Cp935());
|
||||
register(new Cp935.Cp1388());
|
||||
register(new Cp937());
|
||||
register(new Cp937.Cp1371());
|
||||
register(new Cp933());
|
||||
|
||||
// Register Phase 8 Extended EBCDIC Codepages (Arabic, Hebrew, Thai, Cyrillic, Turkish, Extended DBCS)
|
||||
registerExtendedCodepages();
|
||||
|
||||
// Setup common aliases
|
||||
addAlias("us", "037");
|
||||
addAlias("usa", "037");
|
||||
addAlias("ebcdic-cp-us", "037");
|
||||
addAlias("ebcdic-cp-ca", "037");
|
||||
addAlias("ebcdic-cp-nl", "037");
|
||||
|
||||
addAlias("posix", "1047");
|
||||
addAlias("unix", "1047");
|
||||
addAlias("open-systems", "1047");
|
||||
addAlias("zos-unix", "1047");
|
||||
|
||||
addAlias("intl", "500");
|
||||
addAlias("international", "500");
|
||||
addAlias("ebcdic-cp-ch", "500");
|
||||
|
||||
addAlias("de", "273");
|
||||
addAlias("germany", "273");
|
||||
addAlias("austria", "273");
|
||||
addAlias("ebcdic-cp-de", "273");
|
||||
|
||||
addAlias("dk", "277");
|
||||
addAlias("no", "277");
|
||||
addAlias("denmark", "277");
|
||||
addAlias("norway", "277");
|
||||
addAlias("ebcdic-cp-dk", "277");
|
||||
addAlias("ebcdic-cp-no", "277");
|
||||
|
||||
addAlias("se", "278");
|
||||
addAlias("fi", "278");
|
||||
addAlias("sweden", "278");
|
||||
addAlias("finland", "278");
|
||||
addAlias("ebcdic-cp-se", "278");
|
||||
addAlias("ebcdic-cp-fi", "278");
|
||||
|
||||
addAlias("it", "280");
|
||||
addAlias("italy", "280");
|
||||
addAlias("ebcdic-cp-it", "280");
|
||||
|
||||
addAlias("es", "284");
|
||||
addAlias("spain", "284");
|
||||
addAlias("latin-america", "284");
|
||||
addAlias("ebcdic-cp-es", "284");
|
||||
|
||||
addAlias("uk", "285");
|
||||
addAlias("gb", "285");
|
||||
addAlias("great-britain", "285");
|
||||
addAlias("ebcdic-cp-gb", "285");
|
||||
|
||||
addAlias("fr", "297");
|
||||
addAlias("france", "297");
|
||||
addAlias("ebcdic-cp-fr", "297");
|
||||
|
||||
addAlias("latin2", "870");
|
||||
addAlias("pl", "870");
|
||||
addAlias("cz", "870");
|
||||
addAlias("hu", "870");
|
||||
addAlias("ebcdic-cp-roece", "870");
|
||||
|
||||
addAlias("is", "871");
|
||||
addAlias("iceland", "871");
|
||||
addAlias("ebcdic-cp-is", "871");
|
||||
|
||||
addAlias("gr", "875");
|
||||
addAlias("greece", "875");
|
||||
addAlias("greek", "875");
|
||||
addAlias("ebcdic-cp-gr", "875");
|
||||
|
||||
addAlias("tr", "1026");
|
||||
addAlias("turkey", "1026");
|
||||
addAlias("turkish", "1026");
|
||||
addAlias("ebcdic-cp-tr", "1026");
|
||||
|
||||
addAlias("ja", "930");
|
||||
addAlias("japanese", "930");
|
||||
addAlias("katakana", "930");
|
||||
addAlias("ja-latin", "939");
|
||||
|
||||
addAlias("zh", "935");
|
||||
addAlias("chinese-simplified", "935");
|
||||
addAlias("zh-tw", "937");
|
||||
addAlias("chinese-traditional", "937");
|
||||
|
||||
addAlias("ko", "933");
|
||||
addAlias("korean", "933");
|
||||
}
|
||||
|
||||
public static void register(CodePage cp) {
|
||||
if (cp != null) {
|
||||
CODE_PAGES.put(cp.getCodePageId(), cp);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addAlias(String alias, String targetId) {
|
||||
if (alias != null && targetId != null) {
|
||||
ALIASES.put(normalizeKey(alias), targetId);
|
||||
}
|
||||
}
|
||||
|
||||
public static String normalizeKey(String name) {
|
||||
if (name == null) return "";
|
||||
String s = name.trim();
|
||||
// Strip package qualifiers if present (e.g. com.ibm.eNetwork.HOD.converters.onea.ByteToCharCp273)
|
||||
int lastDot = Math.max(s.lastIndexOf('.'), s.lastIndexOf('/'));
|
||||
if (lastDot >= 0 && lastDot < s.length() - 1) {
|
||||
s = s.substring(lastDot + 1);
|
||||
}
|
||||
|
||||
// Strip HoD converter class prefixes
|
||||
for (String pfx : new String[]{
|
||||
"HODByteToChar", "HODCharToByte", "ByteToChar", "CharToByte",
|
||||
"ConverterBIDIPrinter", "ConverterFT", "ConverterJDK", "ConverterVT", "PrtConverter"
|
||||
}) {
|
||||
if (s.startsWith(pfx)) {
|
||||
s = s.substring(pfx.length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s = s.toLowerCase();
|
||||
s = s.replace("_", "").replace("-", "");
|
||||
if (s.startsWith("ebcdiccp")) {
|
||||
s = s.substring(8);
|
||||
} else if (s.startsWith("ebcdic")) {
|
||||
s = s.substring(6);
|
||||
} else if (s.startsWith("ibm")) {
|
||||
s = s.substring(3);
|
||||
} else if (s.startsWith("cp")) {
|
||||
s = s.substring(2);
|
||||
} else if (s.startsWith("ccsid")) {
|
||||
s = s.substring(5);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a code page by ID, alias, or converter name without fallback to CP037.
|
||||
* Returns null if unresolvable.
|
||||
*/
|
||||
public static CodePage resolveCodePage(String name) {
|
||||
if (name == null || name.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String raw = name.trim();
|
||||
// Direct match
|
||||
CodePage cp = CODE_PAGES.get(raw);
|
||||
if (cp != null) return cp;
|
||||
|
||||
// Normalized key lookup
|
||||
String norm = normalizeKey(raw);
|
||||
cp = CODE_PAGES.get(norm);
|
||||
if (cp != null) return cp;
|
||||
|
||||
// Alias lookup
|
||||
String targetId = ALIASES.get(norm);
|
||||
if (targetId != null) {
|
||||
cp = CODE_PAGES.get(targetId);
|
||||
if (cp != null) return cp;
|
||||
String normTarget = normalizeKey(targetId);
|
||||
cp = CODE_PAGES.get(normTarget);
|
||||
if (cp != null) return cp;
|
||||
}
|
||||
|
||||
// Try standard NIO Charset dynamic adapter
|
||||
try {
|
||||
if (Charset.isSupported(raw)) {
|
||||
return new NioCodePageAdapter(raw);
|
||||
}
|
||||
if (Charset.isSupported(norm)) {
|
||||
return new NioCodePageAdapter(norm);
|
||||
}
|
||||
String ibmName = "IBM" + norm;
|
||||
if (Charset.isSupported(ibmName)) {
|
||||
return new NioCodePageAdapter(ibmName);
|
||||
}
|
||||
String ibmDashName = "IBM-" + norm;
|
||||
if (Charset.isSupported(ibmDashName)) {
|
||||
return new NioCodePageAdapter(ibmDashName);
|
||||
}
|
||||
String cpName = "Cp" + norm;
|
||||
if (Charset.isSupported(cpName)) {
|
||||
return new NioCodePageAdapter(cpName);
|
||||
}
|
||||
String isoName = "ISO-8859-" + norm.replace("8859", "");
|
||||
if (Charset.isSupported(isoName)) {
|
||||
return new NioCodePageAdapter(isoName);
|
||||
}
|
||||
String winName = "windows-" + norm;
|
||||
if (Charset.isSupported(winName)) {
|
||||
return new NioCodePageAdapter(winName);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.fine("Dynamic charset loading failed for " + name + ": " + e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a code page by ID or alias.
|
||||
* If not found in built-ins, attempts to load via java.nio.charset.Charset.
|
||||
* Falls back to CP037 if completely unresolvable.
|
||||
*/
|
||||
public static CodePage get(String name) {
|
||||
return getCodePage(name);
|
||||
}
|
||||
|
||||
public static CodePage getDefault() {
|
||||
return getCodePage("037");
|
||||
}
|
||||
|
||||
public static CodePage getCodePage(String name) {
|
||||
CodePage cp = resolveCodePage(name);
|
||||
if (cp != null) {
|
||||
return cp;
|
||||
}
|
||||
|
||||
log.warning("CodePage not recognized: '" + name + "'; falling back to CP037");
|
||||
return CODE_PAGES.get("037");
|
||||
}
|
||||
|
||||
/**
|
||||
* Register extended EBCDIC codepages from HoD v14 converters:
|
||||
* - Arabic: Cp420, Cp424
|
||||
* - Hebrew: Cp424, Cp803
|
||||
* - Thai: Cp838, Cp1160
|
||||
* - Cyrillic: Cp1025, Cp1123, Cp1154, Cp880
|
||||
* - Turkish: Cp1155, Cp905
|
||||
* - Extended Chinese DBCS: Cp1388, Cp1371
|
||||
*/
|
||||
public static void registerExtendedCodepages() {
|
||||
// Arabic & Hebrew
|
||||
register(new Cp420());
|
||||
register(new Cp424());
|
||||
register(new Cp803());
|
||||
|
||||
// Thai
|
||||
register(new Cp838());
|
||||
register(new Cp1160());
|
||||
|
||||
// Cyrillic / Russian / Ukrainian
|
||||
register(new Cp1025());
|
||||
register(new Cp1123());
|
||||
register(new Cp1154());
|
||||
register(new Cp880());
|
||||
|
||||
// Turkish
|
||||
register(new Cp1155());
|
||||
register(new Cp905());
|
||||
|
||||
// Extended Chinese DBCS
|
||||
register(new Cp1388());
|
||||
register(new Cp1371());
|
||||
|
||||
// Setup Extended Aliases
|
||||
addAlias("ar", "420");
|
||||
addAlias("arabic", "420");
|
||||
addAlias("ebcdic-cp-ar", "420");
|
||||
|
||||
addAlias("he", "424");
|
||||
addAlias("hebrew", "424");
|
||||
addAlias("hebrew-lowercase", "424");
|
||||
addAlias("ebcdic-cp-he", "424");
|
||||
|
||||
addAlias("hebrew-old", "803");
|
||||
addAlias("israel", "803");
|
||||
addAlias("iw", "803");
|
||||
addAlias("ebcdic-cp-he-old", "803");
|
||||
|
||||
addAlias("th", "838");
|
||||
addAlias("thai", "838");
|
||||
addAlias("ebcdic-cp-th", "838");
|
||||
addAlias("thai-euro", "1160");
|
||||
|
||||
addAlias("ru", "1025");
|
||||
addAlias("russian", "1025");
|
||||
addAlias("cyrillic", "1025");
|
||||
addAlias("ebcdic-cp-ru", "1025");
|
||||
|
||||
addAlias("ukraine", "1123");
|
||||
addAlias("ukrainian", "1123");
|
||||
addAlias("ebcdic-cp-ua", "1123");
|
||||
|
||||
addAlias("cyrillic-euro", "1154");
|
||||
addAlias("ru-euro", "1154");
|
||||
addAlias("russian-euro", "1154");
|
||||
|
||||
addAlias("cyrillic-russian", "880");
|
||||
addAlias("ru-old", "880");
|
||||
|
||||
addAlias("turkish-euro", "1155");
|
||||
addAlias("tr-euro", "1155");
|
||||
addAlias("turkish-latin3", "905");
|
||||
addAlias("tr-latin3", "905");
|
||||
|
||||
addAlias("chinese-ext-simplified", "1388");
|
||||
addAlias("zh-simplified-ext", "1388");
|
||||
addAlias("zh-ext", "1388");
|
||||
addAlias("chinese-ext-traditional", "1371");
|
||||
addAlias("zh-traditional-ext", "1371");
|
||||
addAlias("zh-tw-ext", "1371");
|
||||
|
||||
// HoD Converter aliases mapping all HoD converter names to CodePage / Charset
|
||||
addAlias("1390", "930");
|
||||
addAlias("1390jis2004", "930");
|
||||
addAlias("1399", "939");
|
||||
addAlias("1399jis2004", "939");
|
||||
addAlias("937macau", "937");
|
||||
addAlias("1364", "933");
|
||||
addAlias("1379", "937");
|
||||
addAlias("274", "500");
|
||||
addAlias("275", "037");
|
||||
addAlias("924", "1047");
|
||||
addAlias("1153", "870");
|
||||
addAlias("1156", "1025");
|
||||
addAlias("1157", "1025");
|
||||
addAlias("1158", "1025");
|
||||
addAlias("1166", "1025");
|
||||
addAlias("1112", "1025");
|
||||
addAlias("1122", "1025");
|
||||
addAlias("1137", "037");
|
||||
addAlias("1008", "420");
|
||||
addAlias("449", "420");
|
||||
addAlias("1089", "420");
|
||||
addAlias("1134", "424");
|
||||
addAlias("1349", "424");
|
||||
addAlias("8585", "875");
|
||||
addAlias("8586", "875");
|
||||
addAlias("220", "284");
|
||||
addAlias("big5550", "937");
|
||||
addAlias("cns", "937");
|
||||
addAlias("tca", "937");
|
||||
addAlias("ks25550", "933");
|
||||
addAlias("jis", "930");
|
||||
addAlias("euc", "937");
|
||||
addAlias("singlebyte", "037");
|
||||
addAlias("dbcsebcdic", "930");
|
||||
addAlias("dbcsebcdicnibm", "930");
|
||||
addAlias("dbcsebcdicibm", "930");
|
||||
addAlias("dbcsascii", "930");
|
||||
addAlias("encodings", "037");
|
||||
addAlias("1011", "037");
|
||||
addAlias("1012", "037");
|
||||
addAlias("1020", "037");
|
||||
addAlias("1021", "037");
|
||||
addAlias("1023", "037");
|
||||
addAlias("1090", "037");
|
||||
addAlias("1101", "037");
|
||||
addAlias("1102", "037");
|
||||
addAlias("1103", "037");
|
||||
addAlias("1104", "037");
|
||||
addAlias("1105", "037");
|
||||
addAlias("1106", "037");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a code page or converter is registered or resolvable.
|
||||
*/
|
||||
public static boolean hasCodePage(String name) {
|
||||
if (name == null || name.trim().isEmpty()) return false;
|
||||
return resolveCodePage(name) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience factory method to get a HODByteToCharConverter by name or alias.
|
||||
*/
|
||||
public static haus.nightmare.lib3270j.converters.HODByteToCharConverter getByteToCharConverter(String name)
|
||||
throws haus.nightmare.lib3270j.converters.HODUnsupportedCodepageException {
|
||||
return haus.nightmare.lib3270j.converters.HODByteToCharConverter.getHODConverter(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience factory method to get a HODCharToByteConverter by name or alias.
|
||||
*/
|
||||
public static haus.nightmare.lib3270j.converters.HODCharToByteConverter getCharToByteConverter(String name)
|
||||
throws haus.nightmare.lib3270j.converters.HODUnsupportedCodepageException {
|
||||
return haus.nightmare.lib3270j.converters.HODCharToByteConverter.getHODConverter(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an unmodifiable list of all registered built-in CodePages.
|
||||
*/
|
||||
public static List<CodePage> getAvailableCodePages() {
|
||||
return Collections.unmodifiableList(new ArrayList<>(CODE_PAGES.values()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of all registered built-in CodePage IDs.
|
||||
*/
|
||||
public static String[] getAvailableCodePageIds() {
|
||||
return CODE_PAGES.keySet().toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamic fallback adapter wrapping any JVM java.nio.charset.Charset.
|
||||
*/
|
||||
public static class NioCodePageAdapter extends AbstractCodePage {
|
||||
private final Charset charset;
|
||||
|
||||
public NioCodePageAdapter(String charsetName) {
|
||||
this(Charset.forName(charsetName));
|
||||
}
|
||||
|
||||
public NioCodePageAdapter(Charset charset) {
|
||||
super(charset.name(), "JVM Charset: " + charset.displayName(), parseCpgid(charset.name()), 697, buildMappingFromCharset(charset));
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
private static int parseCpgid(String name) {
|
||||
try {
|
||||
String num = name.replaceAll("\\D+", "");
|
||||
if (!num.isEmpty()) {
|
||||
return Integer.parseInt(num);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
return 37;
|
||||
}
|
||||
|
||||
private static int[] buildMappingFromCharset(Charset cs) {
|
||||
int[] map = new int[256];
|
||||
for (int i = 0; i < 256; i++) {
|
||||
byte[] b = new byte[]{(byte) i};
|
||||
String s = new String(b, cs);
|
||||
map[i] = !s.isEmpty() ? s.charAt(0) : (char) i;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 037 (US / Canada / Netherlands / Portugal / Brazil).
|
||||
* CCSID / CPGID: 37, GCSGID: 697.
|
||||
*/
|
||||
public class Cp037 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "037";
|
||||
public static final String DESCRIPTION = "US / Canada / Brazil - EBCDIC";
|
||||
public static final int CPGID = 37;
|
||||
public static final int GCSGID = 697;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F (space, accent chars, punctuation)
|
||||
0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5,
|
||||
0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C,
|
||||
// 50-5F
|
||||
0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x00EC, 0x00DF, 0x0021, 0x0024, 0x002A, 0x0029, 0x003B, 0x00AC,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5,
|
||||
0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022,
|
||||
// 80-8F (lowercase a-i)
|
||||
0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F (lowercase j-r)
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF (lowercase s-z)
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x005B, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF (uppercase A-I)
|
||||
0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF (uppercase J-R)
|
||||
0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF (uppercase S-Z)
|
||||
0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF (digits 0-9)
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp037() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "037", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 1025 (Cyrillic Multilingual EBCDIC - Russian, Bulgarian, Belarusian, Serbian, Macedonian).
|
||||
* CCSID / CPGID: 1025, GCSGID: 1150.
|
||||
*/
|
||||
public class Cp1025 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "1025";
|
||||
public static final String DESCRIPTION = "Cyrillic Multilingual EBCDIC";
|
||||
public static final int CPGID = 1025;
|
||||
public static final int GCSGID = 1150;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x044F, 0x044E, 0x044D, 0x044C, 0x044B, 0x044A, 0x0449,
|
||||
0x0448, 0x0447, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x0446, 0x0445, 0x0444, 0x0443, 0x0442, 0x0441, 0x0440,
|
||||
0x043F, 0x043E, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x043D, 0x043C, 0x043B, 0x043A, 0x0439, 0x0438,
|
||||
0x0437, 0x0436, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x0435, 0x0434, 0x0433, 0x0432, 0x0431, 0x0430, 0x0451, 0x0453,
|
||||
0x0452, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x0454, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A,
|
||||
// 90-9F
|
||||
0x045B, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x045C, 0x045E, 0x045F, 0x0401, 0x0402, 0x0403,
|
||||
// A0-AF
|
||||
0x0404, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040A,
|
||||
// B0-BF
|
||||
0x040B, 0x00A3, 0x040C, 0x00B7, 0x00A9, 0x040E, 0x040F, 0x0410,
|
||||
0x0411, 0x0412, 0x00AC, 0x007C, 0x0413, 0x0414, 0x0415, 0x0416,
|
||||
// C0-CF
|
||||
0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C,
|
||||
// D0-DF
|
||||
0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422,
|
||||
// E0-EF
|
||||
0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E,
|
||||
};
|
||||
|
||||
public Cp1025() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1025", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 1026 (Turkey - Turkish Latin-5 EBCDIC).
|
||||
* CCSID / CPGID: 1026, GCSGID: 1152.
|
||||
*/
|
||||
public class Cp1026 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "1026";
|
||||
public static final String DESCRIPTION = "Turkey - Turkish Latin-5 EBCDIC";
|
||||
public static final int CPGID = 1026;
|
||||
public static final int GCSGID = 1152;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5,
|
||||
0x00E7, 0x00F1, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x00EC, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5,
|
||||
0x00C7, 0x00D1, 0x015E, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x00CC, 0x0060, 0x003A, 0x00D6, 0x00C7, 0x00DC, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x0130, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x00F6, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x011E, 0x00F4, 0x0131, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF
|
||||
0x00E7, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x011F, 0x00FB, 0x015F, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF
|
||||
0x00FC, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00A6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp1026() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1026", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 1047 (IBM Open Systems / z/OS Unix System Services Latin-1).
|
||||
* CCSID / CPGID: 1047, GCSGID: 103.
|
||||
*/
|
||||
public class Cp1047 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "1047";
|
||||
public static final String DESCRIPTION = "IBM Open Systems / z/OS Unix Latin-1";
|
||||
public static final int CPGID = 1047;
|
||||
public static final int GCSGID = 103;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x000A, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5,
|
||||
0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C,
|
||||
// 50-5F
|
||||
0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x00EC, 0x00DF, 0x0021, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5,
|
||||
0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x005B, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x00AC, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x00DD, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF
|
||||
0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF
|
||||
0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp1047() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1047", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 1123 (Cyrillic Ukraine EBCDIC).
|
||||
* CCSID / CPGID: 1123, GCSGID: 1399.
|
||||
*/
|
||||
public class Cp1123 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "1123";
|
||||
public static final String DESCRIPTION = "Cyrillic Ukraine EBCDIC";
|
||||
public static final int CPGID = 1123;
|
||||
public static final int GCSGID = 1399;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x044F, 0x044E, 0x044D, 0x044C, 0x044B, 0x044A, 0x0449,
|
||||
0x0448, 0x0447, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x0446, 0x0445, 0x0444, 0x0443, 0x0442, 0x0441, 0x0440,
|
||||
0x043F, 0x043E, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x043D, 0x043C, 0x043B, 0x043A, 0x0439, 0x0438,
|
||||
0x0437, 0x0436, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x0435, 0x0434, 0x0433, 0x0432, 0x0431, 0x0430, 0x0451, 0x0491,
|
||||
0x0454, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x0456, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x0457, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C,
|
||||
// D0-DF
|
||||
0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422,
|
||||
// E0-EF
|
||||
0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E,
|
||||
};
|
||||
|
||||
public Cp1123() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1123", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 1154 (Cyrillic Multilingual with Euro \u20AC).
|
||||
* CCSID / CPGID: 1154, GCSGID: 1305.
|
||||
*/
|
||||
public class Cp1154 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "1154";
|
||||
public static final String DESCRIPTION = "Cyrillic Multilingual EBCDIC (with Euro \u20AC)";
|
||||
public static final int CPGID = 1154;
|
||||
public static final int GCSGID = 1305;
|
||||
|
||||
public static final int[] MAPPING = new int[256];
|
||||
|
||||
static {
|
||||
System.arraycopy(Cp1025.MAPPING, 0, MAPPING, 0, 256);
|
||||
MAPPING[0x9F] = 0x20AC; // Euro character \u20AC
|
||||
}
|
||||
|
||||
public Cp1154() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1154", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 1155 (Turkey - Turkish Latin-5 with Euro \u20AC).
|
||||
* CCSID / CPGID: 1155, GCSGID: 1306.
|
||||
*/
|
||||
public class Cp1155 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "1155";
|
||||
public static final String DESCRIPTION = "Turkey - Turkish Latin-5 (with Euro \u20AC)";
|
||||
public static final int CPGID = 1155;
|
||||
public static final int GCSGID = 1306;
|
||||
|
||||
public static final int[] MAPPING = new int[256];
|
||||
|
||||
static {
|
||||
System.arraycopy(Cp1026.MAPPING, 0, MAPPING, 0, 256);
|
||||
MAPPING[0x9F] = 0x20AC; // Euro character \u20AC
|
||||
}
|
||||
|
||||
public Cp1155() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1155", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 1160 (Thai EBCDIC with Euro \u20AC).
|
||||
* CCSID / CPGID: 1160, GCSGID: 1176.
|
||||
*/
|
||||
public class Cp1160 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "1160";
|
||||
public static final String DESCRIPTION = "Thai EBCDIC (with Euro \u20AC)";
|
||||
public static final int CPGID = 1160;
|
||||
public static final int GCSGID = 1176;
|
||||
|
||||
public static final int[] MAPPING = new int[256];
|
||||
|
||||
static {
|
||||
System.arraycopy(Cp838.MAPPING, 0, MAPPING, 0, 256);
|
||||
MAPPING[0x9F] = 0x20AC; // Euro character \u20AC
|
||||
}
|
||||
|
||||
public Cp1160() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1160", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 1371 (Traditional Chinese Extended Mixed DBCS).
|
||||
* CCSID / CPGID: 1371, GCSGID: 1174.
|
||||
*/
|
||||
public class Cp1371 extends AbstractDBCSCodePage {
|
||||
|
||||
public static final String ID = "1371";
|
||||
public static final String DESCRIPTION = "Traditional Chinese Extended Mixed DBCS";
|
||||
public static final int CPGID = 1371;
|
||||
public static final int GCSGID = 1174;
|
||||
|
||||
public Cp1371() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, Cp037.MAPPING, "x-IBM1371");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 1388 (Simplified Chinese Extended Mixed DBCS).
|
||||
* CCSID / CPGID: 1388, GCSGID: 1175.
|
||||
*/
|
||||
public class Cp1388 extends AbstractDBCSCodePage {
|
||||
|
||||
public static final String ID = "1388";
|
||||
public static final String DESCRIPTION = "Simplified Chinese Extended Mixed DBCS";
|
||||
public static final int CPGID = 1388;
|
||||
public static final int GCSGID = 1175;
|
||||
|
||||
public Cp1388() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, Cp037.MAPPING, "x-IBM1388");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 273 (Germany / Austria).
|
||||
* CCSID / CPGID: 273, GCSGID: 697.
|
||||
*/
|
||||
public class Cp273 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "273";
|
||||
public static final String DESCRIPTION = "Germany / Austria - EBCDIC";
|
||||
public static final int CPGID = 273;
|
||||
public static final int GCSGID = 697;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x00A0, 0x00E2, 0x007B, 0x00E0, 0x00E1, 0x00E3, 0x00E5,
|
||||
0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x00EC, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x00C2, 0x005B, 0x00C0, 0x00C1, 0x00C3, 0x00C5,
|
||||
0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x00CC, 0x0060, 0x003A, 0x00E4, 0x00F6, 0x00FC, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x00E4, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF
|
||||
0x00FC, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x00B9, 0x00FB, 0x00DC, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF
|
||||
0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp273() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "273", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 277 (Denmark / Norway).
|
||||
* CCSID / CPGID: 277, GCSGID: 697.
|
||||
*/
|
||||
public class Cp277 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "277";
|
||||
public static final String DESCRIPTION = "Denmark / Norway - EBCDIC";
|
||||
public static final int CPGID = 277;
|
||||
public static final int GCSGID = 697;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5,
|
||||
0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x00EC, 0x00DF, 0x00A4, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5,
|
||||
0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x00CC, 0x0060, 0x003A, 0x00E6, 0x00F8, 0x00E5, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x00C6, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF
|
||||
0x00D8, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF
|
||||
0x00C5, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp277() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "277", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 278 (Sweden / Finland).
|
||||
* CCSID / CPGID: 278, GCSGID: 697.
|
||||
*/
|
||||
public class Cp278 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "278";
|
||||
public static final String DESCRIPTION = "Sweden / Finland - EBCDIC";
|
||||
public static final int CPGID = 278;
|
||||
public static final int GCSGID = 697;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x00A0, 0x00E2, 0x007B, 0x00E0, 0x00E1, 0x00E3, 0x00E5,
|
||||
0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x00EC, 0x00DF, 0x00A4, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x00C2, 0x005B, 0x00C0, 0x00C1, 0x00C3, 0x00C5,
|
||||
0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x00CC, 0x0060, 0x003A, 0x00E4, 0x00F6, 0x00E5, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x00C4, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF
|
||||
0x00D6, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF
|
||||
0x00C5, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp278() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "278", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 280 (Italy).
|
||||
* CCSID / CPGID: 280, GCSGID: 697.
|
||||
*/
|
||||
public class Cp280 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "280";
|
||||
public static final String DESCRIPTION = "Italy - EBCDIC";
|
||||
public static final int CPGID = 280;
|
||||
public static final int GCSGID = 697;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x00A0, 0x00E2, 0x007B, 0x00E0, 0x00E1, 0x00E3, 0x00E5,
|
||||
0x00E7, 0x00F1, 0x00B0, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x00EC, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x00C2, 0x005B, 0x00C0, 0x00C1, 0x00C3, 0x00C5,
|
||||
0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x00CC, 0x0060, 0x003A, 0x00A3, 0x00A7, 0x00E9, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x00F2, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF
|
||||
0x00F9, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF
|
||||
0x00E0, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp280() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "280", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 284 (Spain / Latin America).
|
||||
* CCSID / CPGID: 284, GCSGID: 697.
|
||||
*/
|
||||
public class Cp284 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "284";
|
||||
public static final String DESCRIPTION = "Spain / Latin America - EBCDIC";
|
||||
public static final int CPGID = 284;
|
||||
public static final int GCSGID = 697;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5,
|
||||
0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C,
|
||||
// 50-5F
|
||||
0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x00EC, 0x00DF, 0x0021, 0x0024, 0x002A, 0x0029, 0x003B, 0x00AC,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5,
|
||||
0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x00CC, 0x0060, 0x003A, 0x00F1, 0x00D1, 0x00E7, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x005B, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF
|
||||
0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF
|
||||
0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp284() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "284", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 285 (United Kingdom).
|
||||
* CCSID / CPGID: 285, GCSGID: 697.
|
||||
*/
|
||||
public class Cp285 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "285";
|
||||
public static final String DESCRIPTION = "United Kingdom - EBCDIC";
|
||||
public static final int CPGID = 285;
|
||||
public static final int GCSGID = 697;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5,
|
||||
0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C,
|
||||
// 50-5F
|
||||
0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x00EC, 0x00DF, 0x0021, 0x0024, 0x002A, 0x0029, 0x003B, 0x00AC,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5,
|
||||
0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x00CC, 0x0060, 0x003A, 0x00A3, 0x0040, 0x0027, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x005B, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF
|
||||
0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF
|
||||
0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp285() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "285", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 297 (France).
|
||||
* CCSID / CPGID: 297, GCSGID: 697.
|
||||
*/
|
||||
public class Cp297 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "297";
|
||||
public static final String DESCRIPTION = "France - EBCDIC";
|
||||
public static final int CPGID = 297;
|
||||
public static final int GCSGID = 697;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x00A0, 0x00E2, 0x007B, 0x00E0, 0x00E1, 0x00E3, 0x00E5,
|
||||
0x00E7, 0x00F1, 0x00B0, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x00EC, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x00C2, 0x005B, 0x00C0, 0x00C1, 0x00C3, 0x00C5,
|
||||
0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x00CC, 0x0060, 0x003A, 0x00E0, 0x00E9, 0x00E8, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x00E9, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF
|
||||
0x00E8, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF
|
||||
0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp297() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "297", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 420 (Arabic Bilingual EBCDIC).
|
||||
* CCSID / CPGID: 420, GCSGID: 235.
|
||||
*/
|
||||
public class Cp420 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "420";
|
||||
public static final String DESCRIPTION = "Arabic Bilingual EBCDIC";
|
||||
public static final int CPGID = 420;
|
||||
public static final int GCSGID = 235;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x0640, 0xFE83, 0xFE87, 0xFE85, 0xFE81, 0xFE80, 0x0621,
|
||||
0xFE8D, 0xFE8B, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0xFE8E, 0xFE8F, 0xFE91, 0xFE93, 0xFE95, 0xFE97, 0xFE99,
|
||||
0xFE9B, 0xFE9D, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0xFE9F, 0xFEA1, 0xFEA3, 0xFEA5, 0xFEA7, 0xFEA9,
|
||||
0xFEAB, 0xFEAD, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0xFEAF, 0xFEB1, 0xFEB3, 0xFEB5, 0xFEB7, 0xFEB9, 0xFEBB, 0xFEBD,
|
||||
0xFEBF, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0xFEC1, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0xFEC3, 0xFEC5, 0xFEC7, 0xFEC9, 0xFECB, 0xFECD,
|
||||
// 90-9F
|
||||
0xFECF, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0xFED1, 0xFED3, 0xFED5, 0xFED7, 0xFED9, 0xFEDB,
|
||||
// A0-AF
|
||||
0xFEDD, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0xFEDF, 0xFEE1, 0xFEE3, 0xFEE5, 0xFEE7, 0xFEE9,
|
||||
// B0-BF
|
||||
0xFEEB, 0xFEED, 0xFEEF, 0xFEF1, 0xFEF3, 0xFEF5, 0xFEF7, 0xFEF9,
|
||||
0xFEFB, 0x00AE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x060C, 0x061B, 0x061F, 0x0628, 0x062A, 0x062B,
|
||||
// D0-DF
|
||||
0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631,
|
||||
// E0-EF
|
||||
0x005C, 0x0632, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x0639, 0x063A, 0x0641, 0x0642, 0x0643, 0x009F,
|
||||
};
|
||||
|
||||
public Cp420() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "420", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 424 (Hebrew with Lowercase EBCDIC).
|
||||
* CCSID / CPGID: 424, GCSGID: 941.
|
||||
*/
|
||||
public class Cp424 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "424";
|
||||
public static final String DESCRIPTION = "Hebrew (with Lowercase) EBCDIC";
|
||||
public static final int CPGID = 424;
|
||||
public static final int GCSGID = 941;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6,
|
||||
0x05D7, 0x05D8, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
|
||||
0x05E0, 0x05E1, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
|
||||
0x05E8, 0x05E9, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x05EA, 0x00A0, 0x2017, 0x00B8, 0x00A8, 0x00B4, 0x00AA, 0x00BA,
|
||||
0x00DF, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF
|
||||
0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF
|
||||
0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp424() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "424", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 500 (International / Western Europe Latin-1).
|
||||
* CCSID / CPGID: 500, GCSGID: 697.
|
||||
*/
|
||||
public class Cp500 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "500";
|
||||
public static final String DESCRIPTION = "International / Western Europe Latin-1";
|
||||
public static final int CPGID = 500;
|
||||
public static final int GCSGID = 697;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5,
|
||||
0x00E7, 0x00F1, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x00EC, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5,
|
||||
0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF
|
||||
0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF
|
||||
0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp500() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "500", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 803 (Hebrew Old / Standard EBCDIC).
|
||||
* CCSID / CPGID: 803, GCSGID: 1147.
|
||||
*/
|
||||
public class Cp803 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "803";
|
||||
public static final String DESCRIPTION = "Hebrew Old / Standard EBCDIC";
|
||||
public static final int CPGID = 803;
|
||||
public static final int GCSGID = 1147;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5,
|
||||
0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x00EC, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5,
|
||||
0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x00D8, 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6,
|
||||
0x05D7, 0x05D8, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F
|
||||
0x00B0, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
|
||||
0x05E0, 0x05E1, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF
|
||||
0x00B5, 0x007E, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
|
||||
0x05E8, 0x05E9, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x05EA, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF
|
||||
0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF
|
||||
0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp803() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "803", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 838 (Thai EBCDIC).
|
||||
* CCSID / CPGID: 838, GCSGID: 1176.
|
||||
*/
|
||||
public class Cp838 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "838";
|
||||
public static final String DESCRIPTION = "Thai EBCDIC";
|
||||
public static final int CPGID = 838;
|
||||
public static final int GCSGID = 1176;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07,
|
||||
0x0E08, 0x0E09, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F, 0x0E10,
|
||||
0x0E11, 0x0E12, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17, 0x0E18,
|
||||
0x0E19, 0x0E1A, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F, 0x0E20, 0x0E21, 0x0E22,
|
||||
0x0E23, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x0E24, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x0E25, 0x0E26, 0x0E27, 0x0E28, 0x0E29, 0x0E2A,
|
||||
// 90-9F
|
||||
0x0E2B, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F, 0x0E30, 0x0E31,
|
||||
// A0-AF
|
||||
0x0E32, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, 0x0E38,
|
||||
// B0-BF
|
||||
0x0E39, 0x0E3A, 0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45,
|
||||
0x0E46, 0x0E47, 0x00AC, 0x007C, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B,
|
||||
// C0-CF
|
||||
0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F, 0x0E50, 0x0E51,
|
||||
// D0-DF
|
||||
0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57,
|
||||
// E0-EF
|
||||
0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x00D3, 0x00D5,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp838() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "838", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 870 (Latin-2 / Eastern Europe: Polish, Czech, Slovak, Hungarian, etc.).
|
||||
* CCSID / CPGID: 870, GCSGID: 959.
|
||||
*/
|
||||
public class Cp870 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "870";
|
||||
public static final String DESCRIPTION = "Eastern Europe / Latin-2 - EBCDIC";
|
||||
public static final int CPGID = 870;
|
||||
public static final int GCSGID = 959;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x00A0, 0x0103, 0x00E4, 0x016F, 0x0165, 0x0105, 0x0155,
|
||||
0x0107, 0x00E9, 0x015B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
|
||||
0x010D, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x00C2, 0x00C4, 0x016E, 0x0164, 0x0104, 0x0154,
|
||||
0x0106, 0x00C9, 0x015A, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
|
||||
0x010C, 0x0060, 0x003A, 0x0161, 0x017C, 0x00FD, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x005B, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x0160, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF
|
||||
0x017B, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF
|
||||
0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp870() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "870", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 871 (Iceland).
|
||||
* CCSID / CPGID: 871, GCSGID: 697.
|
||||
*/
|
||||
public class Cp871 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "871";
|
||||
public static final String DESCRIPTION = "Iceland - EBCDIC";
|
||||
public static final int CPGID = 871;
|
||||
public static final int GCSGID = 697;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5,
|
||||
0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x00EC, 0x00DF, 0x00A4, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5,
|
||||
0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x00CC, 0x0060, 0x003A, 0x00FE, 0x00F0, 0x00FD, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x005B, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x00DE, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF
|
||||
0x00D0, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF
|
||||
0x00DD, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp871() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "871", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 875 (Greece - Greek EBCDIC).
|
||||
* CCSID / CPGID: 875, GCSGID: 925.
|
||||
*/
|
||||
public class Cp875 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "875";
|
||||
public static final String DESCRIPTION = "Greece - Greek EBCDIC";
|
||||
public static final int CPGID = 875;
|
||||
public static final int GCSGID = 925;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x00A0, 0x03AC, 0x03AD, 0x03AE, 0x03AF, 0x03CC, 0x03CD,
|
||||
0x03CE, 0x00DF, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x0390, 0x03B0, 0x03CA, 0x03CB, 0x0390, 0x0385, 0x0386,
|
||||
0x0388, 0x0389, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x038A, 0x038C, 0x038E, 0x038F, 0x03AA, 0x03AB,
|
||||
0x00A9, 0x00AE, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
|
||||
0x03B8, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x03B9, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
|
||||
// 90-9F
|
||||
0x03C0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6,
|
||||
// A0-AF
|
||||
0x03C7, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x03C8, 0x03C9, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396,
|
||||
// D0-DF
|
||||
0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C,
|
||||
// E0-EF
|
||||
0x005C, 0x039D, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, 0x03A4,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x009F,
|
||||
};
|
||||
|
||||
public Cp875() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "875", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 880 (Cyrillic Russian EBCDIC).
|
||||
* CCSID / CPGID: 880, GCSGID: 960.
|
||||
*/
|
||||
public class Cp880 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "880";
|
||||
public static final String DESCRIPTION = "Cyrillic Russian EBCDIC";
|
||||
public static final int CPGID = 880;
|
||||
public static final int GCSGID = 960;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x00A0, 0x044F, 0x044E, 0x044D, 0x044C, 0x044B, 0x044A,
|
||||
0x0449, 0x0448, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x0447, 0x0446, 0x0445, 0x0444, 0x0443, 0x0442, 0x0441,
|
||||
0x0440, 0x043F, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x043E, 0x043D, 0x043C, 0x043B, 0x043A, 0x0439,
|
||||
0x0438, 0x0437, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x0436, 0x0435, 0x0434, 0x0433, 0x0432, 0x0431, 0x0430, 0x0451,
|
||||
0x045E, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x045F, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x0456, 0x0458, 0x0459, 0x045A, 0x045C, 0x045B,
|
||||
// 90-9F
|
||||
0x0402, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x0403, 0x0404, 0x0405, 0x0406, 0x0408, 0x0409,
|
||||
// A0-AF
|
||||
0x040A, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x040C, 0x040B, 0x040F, 0x040E, 0x0401, 0x00AE,
|
||||
// B0-BF
|
||||
0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424,
|
||||
// D0-DF
|
||||
0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A,
|
||||
// E0-EF
|
||||
0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 0x0410,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x009F,
|
||||
};
|
||||
|
||||
public Cp880() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "880", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 905 (Turkey - Turkish Latin-3 EBCDIC).
|
||||
* CCSID / CPGID: 905, GCSGID: 1151.
|
||||
*/
|
||||
public class Cp905 extends AbstractCodePage {
|
||||
|
||||
public static final String ID = "905";
|
||||
public static final String DESCRIPTION = "Turkey - Turkish Latin-3 EBCDIC";
|
||||
public static final int CPGID = 905;
|
||||
public static final int GCSGID = 1151;
|
||||
|
||||
public static final int[] MAPPING = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F
|
||||
0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5,
|
||||
0x00E7, 0x00F1, 0x011E, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021,
|
||||
// 50-5F
|
||||
0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x00EC, 0x00DF, 0x0130, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5,
|
||||
0x00C7, 0x00D1, 0x015E, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022,
|
||||
// 80-8F
|
||||
0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF
|
||||
0x011F, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF
|
||||
0x0131, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF
|
||||
0x015F, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
|
||||
public Cp905() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "905", MAPPING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 930 (Japanese Katakana Mixed DBCS: host SBCS CP290 / DBCS CP300).
|
||||
* CCSID / CPGID: 930, GCSGID: 1172.
|
||||
*/
|
||||
public class Cp930 extends AbstractDBCSCodePage {
|
||||
|
||||
public static final String ID = "930";
|
||||
public static final String DESCRIPTION = "Japanese Katakana Mixed DBCS";
|
||||
public static final int CPGID = 930;
|
||||
public static final int GCSGID = 1172;
|
||||
|
||||
public Cp930() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, Cp037.MAPPING, "x-IBM930");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 933 (Korean Mixed DBCS).
|
||||
* CCSID / CPGID: 933, GCSGID: 1173.
|
||||
*/
|
||||
public class Cp933 extends AbstractDBCSCodePage {
|
||||
|
||||
public static final String ID = "933";
|
||||
public static final String DESCRIPTION = "Korean Mixed DBCS";
|
||||
public static final int CPGID = 933;
|
||||
public static final int GCSGID = 1173;
|
||||
|
||||
public Cp933() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, Cp037.MAPPING, "x-IBM933");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 935 / 1388 (Simplified Chinese Mixed DBCS).
|
||||
* CCSID / CPGID: 935, GCSGID: 1175.
|
||||
*/
|
||||
public class Cp935 extends AbstractDBCSCodePage {
|
||||
|
||||
public static final String ID = "935";
|
||||
public static final String DESCRIPTION = "Simplified Chinese Mixed DBCS";
|
||||
public static final int CPGID = 935;
|
||||
public static final int GCSGID = 1175;
|
||||
|
||||
public Cp935() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, Cp037.MAPPING, "x-IBM935");
|
||||
}
|
||||
|
||||
public static class Cp1388 extends AbstractDBCSCodePage {
|
||||
public static final String ID = "1388";
|
||||
public static final String DESCRIPTION = "Simplified Chinese Extended Mixed DBCS";
|
||||
public static final int CPGID = 1388;
|
||||
public static final int GCSGID = 1175;
|
||||
|
||||
public Cp1388() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, Cp037.MAPPING, "x-IBM1388");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 937 / 1371 (Traditional Chinese Mixed DBCS).
|
||||
* CCSID / CPGID: 937, GCSGID: 1174.
|
||||
*/
|
||||
public class Cp937 extends AbstractDBCSCodePage {
|
||||
|
||||
public static final String ID = "937";
|
||||
public static final String DESCRIPTION = "Traditional Chinese Mixed DBCS";
|
||||
public static final int CPGID = 937;
|
||||
public static final int GCSGID = 1174;
|
||||
|
||||
public Cp937() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, Cp037.MAPPING, "x-IBM937");
|
||||
}
|
||||
|
||||
public static class Cp1371 extends AbstractDBCSCodePage {
|
||||
public static final String ID = "1371";
|
||||
public static final String DESCRIPTION = "Traditional Chinese Extended Mixed DBCS";
|
||||
public static final int CPGID = 1371;
|
||||
public static final int GCSGID = 1174;
|
||||
|
||||
public Cp1371() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, Cp037.MAPPING, "x-IBM1371");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
/**
|
||||
* IBM Code Page 939 (Japanese Latin Mixed DBCS: host SBCS CP1027 / DBCS CP300).
|
||||
* CCSID / CPGID: 939, GCSGID: 1172.
|
||||
*/
|
||||
public class Cp939 extends AbstractDBCSCodePage {
|
||||
|
||||
public static final String ID = "939";
|
||||
public static final String DESCRIPTION = "Japanese Latin Mixed DBCS";
|
||||
public static final int CPGID = 939;
|
||||
public static final int GCSGID = 1172;
|
||||
|
||||
public Cp939() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, Cp037.MAPPING, "x-IBM939");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Euro currency symbol (€) enabled EBCDIC code pages (CP1140 through CP1149).
|
||||
*/
|
||||
public class CpEuroVariants {
|
||||
|
||||
public static class Cp1140 extends AbstractCodePage {
|
||||
public static final String ID = "1140";
|
||||
public static final String DESCRIPTION = "US / Canada Euro - EBCDIC";
|
||||
public static final int CPGID = 1140;
|
||||
public static final int GCSGID = 695;
|
||||
|
||||
public Cp1140() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1140", modifyEuro(Cp037.MAPPING, 0x9F));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Cp1141 extends AbstractCodePage {
|
||||
public static final String ID = "1141";
|
||||
public static final String DESCRIPTION = "Germany / Austria Euro - EBCDIC";
|
||||
public static final int CPGID = 1141;
|
||||
public static final int GCSGID = 695;
|
||||
|
||||
public Cp1141() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1141", modifyEuro(Cp273.MAPPING, 0x9F));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Cp1142 extends AbstractCodePage {
|
||||
public static final String ID = "1142";
|
||||
public static final String DESCRIPTION = "Denmark / Norway Euro - EBCDIC";
|
||||
public static final int CPGID = 1142;
|
||||
public static final int GCSGID = 695;
|
||||
|
||||
public Cp1142() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1142", modifyEuro(Cp277.MAPPING, 0x5A));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Cp1143 extends AbstractCodePage {
|
||||
public static final String ID = "1143";
|
||||
public static final String DESCRIPTION = "Sweden / Finland Euro - EBCDIC";
|
||||
public static final int CPGID = 1143;
|
||||
public static final int GCSGID = 695;
|
||||
|
||||
public Cp1143() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1143", modifyEuro(Cp278.MAPPING, 0x5A));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Cp1144 extends AbstractCodePage {
|
||||
public static final String ID = "1144";
|
||||
public static final String DESCRIPTION = "Italy Euro - EBCDIC";
|
||||
public static final int CPGID = 1144;
|
||||
public static final int GCSGID = 695;
|
||||
|
||||
public Cp1144() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1144", modifyEuro(Cp280.MAPPING, 0x9F));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Cp1145 extends AbstractCodePage {
|
||||
public static final String ID = "1145";
|
||||
public static final String DESCRIPTION = "Spain / Latin America Euro - EBCDIC";
|
||||
public static final int CPGID = 1145;
|
||||
public static final int GCSGID = 695;
|
||||
|
||||
public Cp1145() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1145", modifyEuro(Cp284.MAPPING, 0x9F));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Cp1146 extends AbstractCodePage {
|
||||
public static final String ID = "1146";
|
||||
public static final String DESCRIPTION = "United Kingdom Euro - EBCDIC";
|
||||
public static final int CPGID = 1146;
|
||||
public static final int GCSGID = 695;
|
||||
|
||||
public Cp1146() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1146", modifyEuro(Cp285.MAPPING, 0x9F));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Cp1147 extends AbstractCodePage {
|
||||
public static final String ID = "1147";
|
||||
public static final String DESCRIPTION = "France Euro - EBCDIC";
|
||||
public static final int CPGID = 1147;
|
||||
public static final int GCSGID = 695;
|
||||
|
||||
public Cp1147() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1147", modifyEuro(Cp297.MAPPING, 0x9F));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Cp1148 extends AbstractCodePage {
|
||||
public static final String ID = "1148";
|
||||
public static final String DESCRIPTION = "International Latin-1 Euro - EBCDIC";
|
||||
public static final int CPGID = 1148;
|
||||
public static final int GCSGID = 695;
|
||||
|
||||
public Cp1148() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1148", modifyEuro(Cp500.MAPPING, 0x9F));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Cp1149 extends AbstractCodePage {
|
||||
public static final String ID = "1149";
|
||||
public static final String DESCRIPTION = "Iceland Euro - EBCDIC";
|
||||
public static final int CPGID = 1149;
|
||||
public static final int GCSGID = 695;
|
||||
|
||||
public Cp1149() {
|
||||
super(ID, DESCRIPTION, CPGID, GCSGID, "1149", modifyEuro(Cp871.MAPPING, 0x9F));
|
||||
}
|
||||
}
|
||||
|
||||
private static int[] modifyEuro(int[] baseMapping, int euroPosition) {
|
||||
int[] copy = Arrays.copyOf(baseMapping, 256);
|
||||
copy[euroPosition & 0xFF] = '\u20AC'; // '€'
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
@@ -1,113 +1,246 @@
|
||||
package haus.nightmare.lib3270j.charset;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* EBCDIC ↔ Unicode translator.
|
||||
* EBCDIC ↔ Unicode character translator conforming to IBM Host On-Demand (HoD v14).
|
||||
* Modular architecture delegating to pluggable CodePage implementations (SBCS and DBCS).
|
||||
* Supports custom per-instance character translation override tables and complete
|
||||
* IBM 3270 APL / Graphic Escape (GA23-0059) character mappings.
|
||||
* Default: Code Page 037 (US/Canada EBCDIC).
|
||||
*/
|
||||
public class EbcdicTranslator {
|
||||
|
||||
/**
|
||||
* EBCDIC Code Page 037 to Unicode mapping.
|
||||
* Index is the EBCDIC byte value (0x00-0xFF), value is the Unicode codepoint.
|
||||
* EBCDIC Code Page 037 to Unicode mapping table preserved for legacy static access.
|
||||
*/
|
||||
private static final int[] CP037_TO_UNICODE = {
|
||||
// 00-0F
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F,
|
||||
0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||
// 10-1F
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087,
|
||||
0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||
// 20-2F
|
||||
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B,
|
||||
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007,
|
||||
// 30-3F
|
||||
0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
|
||||
0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A,
|
||||
// 40-4F (space, accent chars, punctuation)
|
||||
0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5,
|
||||
0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C,
|
||||
// 50-5F
|
||||
0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF,
|
||||
0x00EC, 0x00DF, 0x0021, 0x0024, 0x002A, 0x0029, 0x003B, 0x00AC,
|
||||
// 60-6F
|
||||
0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5,
|
||||
0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F,
|
||||
// 70-7F
|
||||
0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF,
|
||||
0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022,
|
||||
// 80-8F (lowercase a-i)
|
||||
0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1,
|
||||
// 90-9F (lowercase j-r)
|
||||
0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070,
|
||||
0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4,
|
||||
// A0-AF (lowercase s-z)
|
||||
0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
|
||||
0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE,
|
||||
// B0-BF
|
||||
0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC,
|
||||
0x00BD, 0x00BE, 0x005B, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7,
|
||||
// C0-CF (uppercase A-I)
|
||||
0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5,
|
||||
// D0-DF (uppercase J-R)
|
||||
0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
|
||||
0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF,
|
||||
// E0-EF (uppercase S-Z)
|
||||
0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5,
|
||||
// F0-FF (digits 0-9)
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F,
|
||||
};
|
||||
public static final int[] CP037_TO_UNICODE = Cp037.MAPPING;
|
||||
|
||||
private CodePage activeCodePage;
|
||||
private final Map<Integer, Character> customEbcdicToUnicode = new ConcurrentHashMap<>();
|
||||
private final Map<Character, Integer> customUnicodeToEbcdic = new ConcurrentHashMap<>();
|
||||
|
||||
public EbcdicTranslator() {
|
||||
this("037");
|
||||
}
|
||||
|
||||
public EbcdicTranslator(String codePageId) {
|
||||
setCodePage(codePageId);
|
||||
}
|
||||
|
||||
public EbcdicTranslator(CodePage codePage) {
|
||||
setCodePage(codePage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unicode to EBCDIC Code Page 037 mapping (for basic Latin + Latin-1).
|
||||
* Index is the Unicode codepoint (0x00-0xFF), value is the EBCDIC byte (-1 if unmappable).
|
||||
* Set active code page by identifier or alias (e.g. "037", "1047", "500", "273", "1140", "930").
|
||||
*/
|
||||
private static final int[] UNICODE_TO_CP037 = new int[256];
|
||||
public synchronized void setCodePage(String codePageId) {
|
||||
this.activeCodePage = CodePageRegistry.getCodePage(codePageId);
|
||||
}
|
||||
|
||||
static {
|
||||
// Build reverse mapping
|
||||
java.util.Arrays.fill(UNICODE_TO_CP037, -1);
|
||||
for (int i = 0; i < 256; i++) {
|
||||
int unicode = CP037_TO_UNICODE[i];
|
||||
if (unicode < 256) {
|
||||
UNICODE_TO_CP037[unicode] = i;
|
||||
/**
|
||||
* Set active code page directly.
|
||||
*/
|
||||
public synchronized void setCodePage(CodePage codePage) {
|
||||
if (codePage != null) {
|
||||
this.activeCodePage = codePage;
|
||||
} else {
|
||||
this.activeCodePage = CodePageRegistry.getCodePage("037");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the active CodePage descriptor and translator.
|
||||
*/
|
||||
public synchronized CodePage getCodePage() {
|
||||
return activeCodePage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the active code page identifier string.
|
||||
*/
|
||||
public synchronized String getCodePageId() {
|
||||
return activeCodePage.getCodePageId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active Code Page Global ID (CPGID / CCSID) for Query Replies.
|
||||
*/
|
||||
public synchronized int getCpgid() {
|
||||
return activeCodePage.getCpgid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active Graphic Character Set Global ID (GCSGID) for Query Replies.
|
||||
*/
|
||||
public synchronized int getCgcsgid() {
|
||||
return activeCodePage.getCgcsgid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if active code page is Double-Byte / mixed DBCS.
|
||||
*/
|
||||
public synchronized boolean isDBCSCodePage() {
|
||||
return activeCodePage.isDBCS();
|
||||
}
|
||||
|
||||
public synchronized boolean isDBCS() {
|
||||
return isDBCSCodePage();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Custom Character Translation Overrides
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Set a bidirectional custom character translation override.
|
||||
* Maps an EBCDIC byte value to a Unicode character, and vice versa.
|
||||
*/
|
||||
public synchronized void setCustomOverride(int ebcdicByte, char unicodeChar) {
|
||||
int b = ebcdicByte & 0xFF;
|
||||
customEbcdicToUnicode.put(b, unicodeChar);
|
||||
customUnicodeToEbcdic.put(unicodeChar, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a one-way custom EBCDIC byte to Unicode character override.
|
||||
*/
|
||||
public synchronized void setCustomEbcdicToUnicodeOverride(int ebcdicByte, char unicodeChar) {
|
||||
customEbcdicToUnicode.put(ebcdicByte & 0xFF, unicodeChar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a one-way custom Unicode character to EBCDIC byte override.
|
||||
*/
|
||||
public synchronized void setCustomUnicodeToEbcdicOverride(char unicodeChar, int ebcdicByte) {
|
||||
customUnicodeToEbcdic.put(unicodeChar, ebcdicByte & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set custom translation overrides in bulk.
|
||||
*/
|
||||
public synchronized void setCustomOverrides(Map<Integer, Character> ebcToUni, Map<Character, Integer> uniToEbc) {
|
||||
if (ebcToUni != null) {
|
||||
for (Map.Entry<Integer, Character> entry : ebcToUni.entrySet()) {
|
||||
if (entry.getKey() != null && entry.getValue() != null) {
|
||||
setCustomOverride(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uniToEbc != null) {
|
||||
for (Map.Entry<Character, Integer> entry : uniToEbc.entrySet()) {
|
||||
if (entry.getKey() != null && entry.getValue() != null) {
|
||||
setCustomUnicodeToEbcdicOverride(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate EBCDIC byte to Unicode character.
|
||||
* Remove custom translation override for a specific EBCDIC byte.
|
||||
*/
|
||||
public char ebcdicToUnicode(int ebc) {
|
||||
return (char) CP037_TO_UNICODE[ebc & 0xFF];
|
||||
public synchronized void removeCustomOverride(int ebcdicByte) {
|
||||
Character removed = customEbcdicToUnicode.remove(ebcdicByte & 0xFF);
|
||||
if (removed != null) {
|
||||
customUnicodeToEbcdic.remove(removed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Static helper to translate EBCDIC byte to Unicode character.
|
||||
* Remove custom translation override for a specific Unicode character.
|
||||
*/
|
||||
public synchronized void removeCustomUnicodeOverride(char unicodeChar) {
|
||||
Integer removed = customUnicodeToEbcdic.remove(unicodeChar);
|
||||
if (removed != null) {
|
||||
customEbcdicToUnicode.remove(removed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all registered custom translation overrides.
|
||||
*/
|
||||
public synchronized void clearCustomOverrides() {
|
||||
customEbcdicToUnicode.clear();
|
||||
customUnicodeToEbcdic.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any custom translation overrides are active.
|
||||
*/
|
||||
public boolean hasCustomOverrides() {
|
||||
return !customEbcdicToUnicode.isEmpty() || !customUnicodeToEbcdic.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an unmodifiable view of active EBCDIC-to-Unicode custom overrides.
|
||||
*/
|
||||
public Map<Integer, Character> getCustomEbcdicToUnicodeOverrides() {
|
||||
return Collections.unmodifiableMap(new HashMap<>(customEbcdicToUnicode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an unmodifiable view of active Unicode-to-EBCDIC custom overrides.
|
||||
*/
|
||||
public Map<Character, Integer> getCustomUnicodeToEbcdicOverrides() {
|
||||
return Collections.unmodifiableMap(new HashMap<>(customUnicodeToEbcdic));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Translation Operations
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Translate double-byte EBCDIC pair (b1, b2) to Unicode.
|
||||
*/
|
||||
public synchronized char dbcsToUnicode(int b1, int b2) {
|
||||
return activeCodePage.dbcsToUnicode(b1, b2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate Unicode character to double-byte EBCDIC ((b1 << 8) | b2).
|
||||
*/
|
||||
public synchronized int unicodeToDbcs(char unicode) {
|
||||
return activeCodePage.unicodeToDbcs(unicode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate EBCDIC byte to Unicode character using active code page and custom overrides.
|
||||
*/
|
||||
public char ebcdicToUnicode(int ebc) {
|
||||
int b = ebc & 0xFF;
|
||||
Character custom = customEbcdicToUnicode.get(b);
|
||||
if (custom != null) {
|
||||
return custom;
|
||||
}
|
||||
return activeCodePage.ebcdicToUnicode(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static helper to translate EBCDIC byte to Unicode character (using standard CP037).
|
||||
*/
|
||||
public static char toUnicode(int ebc) {
|
||||
return (char) CP037_TO_UNICODE[ebc & 0xFF];
|
||||
}
|
||||
|
||||
/**
|
||||
* Static helper to translate EBCDIC byte to ASCII character.
|
||||
* Static helper to translate EBCDIC byte to ASCII character (using standard CP037).
|
||||
*/
|
||||
public static char ebcdicToAscii(int ebc) {
|
||||
return toUnicode(ebc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate Unicode character to EBCDIC byte.
|
||||
* Translate Unicode character to EBCDIC byte using active code page and custom overrides.
|
||||
* Returns -1 if the character cannot be mapped.
|
||||
*/
|
||||
public int unicodeToEbcdic(char unicode) {
|
||||
if (unicode < 256) {
|
||||
return UNICODE_TO_CP037[unicode];
|
||||
Integer custom = customUnicodeToEbcdic.get(unicode);
|
||||
if (custom != null) {
|
||||
return custom;
|
||||
}
|
||||
return -1;
|
||||
return activeCodePage.unicodeToEbcdic(unicode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,24 +252,140 @@ public class EbcdicTranslator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a byte array from EBCDIC to a Unicode string.
|
||||
* Translate a byte array from EBCDIC to a Unicode string using active code page and overrides.
|
||||
*/
|
||||
public String ebcdicToString(byte[] ebcdic, int offset, int length) {
|
||||
StringBuilder sb = new StringBuilder(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(ebcdicToUnicode(ebcdic[offset + i] & 0xFF));
|
||||
if (ebcdic == null || length <= 0) return "";
|
||||
if (hasCustomOverrides() || !(activeCodePage instanceof AbstractDBCSCodePage)) {
|
||||
char[] chars = new char[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
chars[i] = ebcdicToUnicode(ebcdic[offset + i]);
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
return sb.toString();
|
||||
return activeCodePage.ebcdicToString(ebcdic, offset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a Unicode string to EBCDIC byte array.
|
||||
* Translate a Unicode string to EBCDIC byte array using active code page and overrides.
|
||||
*/
|
||||
public byte[] stringToEbcdic(String s) {
|
||||
byte[] result = new byte[s.length()];
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
result[i] = unicodeToEbcdicSafe(s.charAt(i));
|
||||
if (s == null || s.isEmpty()) return new byte[0];
|
||||
if (hasCustomOverrides() || !(activeCodePage instanceof AbstractDBCSCodePage)) {
|
||||
byte[] bytes = new byte[s.length()];
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
bytes[i] = unicodeToEbcdicSafe(s.charAt(i));
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
return result;
|
||||
return activeCodePage.stringToEbcdic(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of all registered code page ID strings.
|
||||
*/
|
||||
public static String[] getAvailableCodePages() {
|
||||
return CodePageRegistry.getAvailableCodePageIds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all registered CodePage instances.
|
||||
*/
|
||||
public static List<CodePage> getAllCodePages() {
|
||||
return CodePageRegistry.getAvailableCodePages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map an IBM 3270 APL / Graphic Escape (GE) EBCDIC code point to its Unicode glyph.
|
||||
* Conforms to IBM 3270 APL / Text character set and GA23-0059 specification.
|
||||
*/
|
||||
public char mapAPL(int ebcdicCodePoint) {
|
||||
switch (ebcdicCodePoint & 0xFF) {
|
||||
// Standard Box-Drawing Lines, Corners, T-Junctions, and Crosses
|
||||
case 0xA2: return '\u2500'; // Horizontal Line 's' -> '─'
|
||||
case 0x85: return '\u2502'; // Vertical Line 'e' -> '│'
|
||||
case 0xC5: return '\u250C'; // Top Left 'E' -> '┌'
|
||||
case 0xD5: return '\u2510'; // Top Right 'N' -> '┐'
|
||||
case 0xC4: return '\u2514'; // Bottom Left 'D' -> '└'
|
||||
case 0xD4: return '\u2518'; // Bottom Right 'M' -> '┘'
|
||||
case 0xC6: return '\u251C'; // T-Junction Left 'F' -> '├'
|
||||
case 0xD6: return '\u2524'; // T-Junction Right 'O' -> '┤'
|
||||
case 0xC7: return '\u252C'; // T-Junction Top 'G' -> '┬'
|
||||
case 0xD7: return '\u2534'; // T-Junction Bottom 'P' -> '┴'
|
||||
case 0xCB: return '\u253C'; // Cross -> '┼'
|
||||
|
||||
// Mathematical Relations and Punctuation
|
||||
case 0x8C: return '\u2264'; // Less-than or equal '≤'
|
||||
case 0xAE: return '\u2265'; // Greater-than or equal '≥'
|
||||
case 0xBE: return '\u2260'; // Not equal '≠'
|
||||
case 0xAD: return '['; // Left bracket
|
||||
case 0xBD: return ']'; // Right bracket
|
||||
case 0x8D: return '{'; // Left brace
|
||||
case 0x9D: return '}'; // Right brace
|
||||
case 0xB0: return '\u00B0'; // Degree '°'
|
||||
case 0xB1: return '\u00B1'; // Plus-minus '±'
|
||||
case 0xB2: return '\u00B2'; // Superscript 2 '²'
|
||||
case 0xB3: return '\u00B3'; // Superscript 3 '³'
|
||||
case 0xAF: return '\u00AF'; // Overbar / High Minus '¯'
|
||||
case 0xBA: return '\u03A9'; // Omega 'Ω'
|
||||
case 0xBF: return '\u00B5'; // Micro 'µ'
|
||||
case 0x5F: return '\u00AC'; // Not sign '¬'
|
||||
|
||||
// IBM 3270 APL Operational & Structural Glyphs
|
||||
case 0x80: return '\u22C4'; // Diamond '⋄'
|
||||
case 0x81: return '\u237A'; // APL Alpha '⍺'
|
||||
case 0x82: return '\u22A5'; // Up Tack / Decode '⊥'
|
||||
case 0x83: return '\u2229'; // Intersection '∩'
|
||||
case 0x84: return '\u230A'; // Floor '⌊'
|
||||
case 0x86: return '\u2286'; // Subset or equal '⊆'
|
||||
case 0x87: return '\u2207'; // Del / Grad '∇'
|
||||
case 0x88: return '\u2206'; // Delta '∆'
|
||||
case 0x89: return '\u2373'; // Iota '⍳'
|
||||
case 0x8A: return '\u2192'; // Right Arrow '→'
|
||||
case 0x8B: return '\u235E'; // Quote Quad '⍞'
|
||||
case 0x8E: return '\u00D7'; // Multiply '×'
|
||||
case 0x8F: return '\u00F7'; // Divide '÷'
|
||||
case 0x90: return '\u235F'; // Circle Star / Log '⍟'
|
||||
case 0x91: return '\u2339'; // Quad Divide / Domino '⌹'
|
||||
case 0x92: return '\u22A4'; // Down Tack / Encode '⊤'
|
||||
case 0x93: return '\u222A'; // Union '∪'
|
||||
case 0x94: return '\u2308'; // Ceiling '⌈'
|
||||
case 0x95: return '\u2374'; // Rho / Shape '⍴'
|
||||
case 0x96: return '\u2375'; // APL Omega '⍵'
|
||||
case 0x97: return '\u2260'; // Not equal '≠'
|
||||
case 0x98: return '\u2377'; // Epsilon Underbar '⍷'
|
||||
case 0x99: return '\u25CB'; // Circle '○'
|
||||
case 0x9A: return '\u2190'; // Left Arrow '←'
|
||||
case 0x9B: return '\u2359'; // Delta Underbar '⍙'
|
||||
case 0x9C: return '\u234B'; // Grade Up '⍋'
|
||||
case 0x9E: return '\u2352'; // Grade Down '⍒'
|
||||
case 0x9F: return '\u235D'; // Lamp / Comment '⍝'
|
||||
case 0xA1: return '\u00A8'; // Diaeresis '¨'
|
||||
case 0xA4: return '\u2336'; // I-Beam '⌶'
|
||||
case 0xA5: return '\u2355'; // Thorn / Format '⍕'
|
||||
case 0xA6: return '\u2282'; // Left Shoe / Enclose '⊂'
|
||||
case 0xA7: return '\u2283'; // Right Shoe / Disclose '⊃'
|
||||
case 0xA8: return '\u2191'; // Up Arrow / Take '↑'
|
||||
case 0xA9: return '\u2193'; // Down Arrow / Drop '↓'
|
||||
case 0xAA: return '\u2395'; // Quad '⎕'
|
||||
case 0xAB: return '\u234E'; // Execute / Hydra '⍎'
|
||||
case 0xAC: return '\u2349'; // Transpose / Circle Slope '⍉'
|
||||
case 0xB4: return '\u2296'; // Circle Bar / Reverse '⊖'
|
||||
case 0xB5: return '\u236A'; // Comma Bar / Table '⍪'
|
||||
case 0xB6: return '\u236B'; // Del Tilde '⍫'
|
||||
case 0xB7: return '\u236C'; // Zilde '⍬'
|
||||
case 0xB9: return '\u233F'; // Slash Bar '⌿'
|
||||
case 0xBB: return '\u2340'; // Backslash Bar '⍀'
|
||||
case 0xBC: return '\u2338'; // Quad Equal '⌸'
|
||||
|
||||
default: return ebcdicToUnicode(ebcdicCodePoint & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate an IBM 3270 Graphic Escape (GE) / APL character code to Unicode.
|
||||
*/
|
||||
public char getAplGraphic(int ec) {
|
||||
return mapAPL(ec);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
package haus.nightmare.lib3270j.converters;
|
||||
|
||||
import haus.nightmare.lib3270j.charset.CodePage;
|
||||
import haus.nightmare.lib3270j.charset.CodePageRegistry;
|
||||
|
||||
/**
|
||||
* Concrete converter implementing mixed SBCS/DBCS byte-to-char conversion with transparent
|
||||
* Shift-Out (0x0E) and Shift-In (0x0F) state management.
|
||||
* Conforms to com.ibm.eNetwork.HOD.converters.ByteToCharDBCS_EBCDIC.
|
||||
*/
|
||||
public class ByteToCharDBCS_EBCDIC extends HODByteToCharConverter {
|
||||
|
||||
public ByteToCharDBCS_EBCDIC() {
|
||||
this(CodePageRegistry.getCodePage("930"));
|
||||
}
|
||||
|
||||
public ByteToCharDBCS_EBCDIC(CodePage codePage) {
|
||||
super(codePage != null ? codePage : CodePageRegistry.getCodePage("930"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convert(byte[] in, int inOff, int inEnd, char[] out, int outOff, int outEnd)
|
||||
throws HODCharConversionException {
|
||||
if (in == null || inEnd <= inOff) {
|
||||
return 0;
|
||||
}
|
||||
if (out == null) {
|
||||
throw new HODCharConversionException("Output buffer cannot be null");
|
||||
}
|
||||
|
||||
this.byteOff = inOff;
|
||||
this.charOff = outOff;
|
||||
|
||||
while (this.byteOff < inEnd || this.savedBytePresent) {
|
||||
int b;
|
||||
boolean isSaved = false;
|
||||
if (this.savedBytePresent) {
|
||||
b = this.savedByte & 0xFF;
|
||||
this.savedBytePresent = false;
|
||||
isSaved = true;
|
||||
} else {
|
||||
b = in[this.byteOff] & 0xFF;
|
||||
}
|
||||
|
||||
// Handle Shift-Out (0x0E) -> DBCS mode
|
||||
if (b == SO) {
|
||||
this.currentState = 1;
|
||||
if (this.preserveSOSI) {
|
||||
if (this.charOff >= outEnd) {
|
||||
throw new HODCharConversionException("Output buffer overflow writing SO at " + this.charOff);
|
||||
}
|
||||
out[this.charOff++] = (char) SO;
|
||||
}
|
||||
if (!isSaved) this.byteOff++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle Shift-In (0x0F) -> SBCS mode
|
||||
if (b == SI) {
|
||||
this.currentState = 0;
|
||||
if (this.preserveSOSI) {
|
||||
if (this.charOff >= outEnd) {
|
||||
throw new HODCharConversionException("Output buffer overflow writing SI at " + this.charOff);
|
||||
}
|
||||
out[this.charOff++] = (char) SI;
|
||||
}
|
||||
if (!isSaved) this.byteOff++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// SBCS mode conversion
|
||||
if (this.currentState == 0) {
|
||||
if (this.charOff >= outEnd) {
|
||||
throw new HODCharConversionException("Output buffer overflow at position " + this.charOff);
|
||||
}
|
||||
char c = this.codePage.ebcdicToUnicode(b);
|
||||
if (c == '\uFFFD') {
|
||||
if (this.subMode) {
|
||||
c = this.subChars[0];
|
||||
} else {
|
||||
this.badInputLength = 1;
|
||||
throw new HODCharConversionException("Unmappable byte 0x" + Integer.toHexString(b) + " at index " + this.byteOff);
|
||||
}
|
||||
}
|
||||
out[this.charOff++] = c;
|
||||
if (!isSaved) this.byteOff++;
|
||||
} else {
|
||||
// DBCS mode conversion (requires 2 bytes)
|
||||
int b2;
|
||||
if (isSaved) {
|
||||
if (this.byteOff >= inEnd) {
|
||||
// Incomplete DBCS pair at end of buffer
|
||||
this.savedByte = (byte) b;
|
||||
this.savedBytePresent = true;
|
||||
break;
|
||||
}
|
||||
b2 = in[this.byteOff++] & 0xFF;
|
||||
} else {
|
||||
if (this.byteOff + 1 >= inEnd) {
|
||||
// Trailing single byte inside DBCS shift
|
||||
this.savedByte = (byte) b;
|
||||
this.savedBytePresent = true;
|
||||
this.byteOff++;
|
||||
break;
|
||||
}
|
||||
b2 = in[this.byteOff + 1] & 0xFF;
|
||||
this.byteOff += 2;
|
||||
}
|
||||
|
||||
// Check for premature Shift-In
|
||||
if (b2 == SI) {
|
||||
this.currentState = 0;
|
||||
if (this.charOff >= outEnd) {
|
||||
throw new HODCharConversionException("Output buffer overflow at position " + this.charOff);
|
||||
}
|
||||
out[this.charOff++] = this.codePage.ebcdicToUnicode(b);
|
||||
if (this.preserveSOSI) {
|
||||
if (this.charOff >= outEnd) {
|
||||
throw new HODCharConversionException("Output buffer overflow at position " + this.charOff);
|
||||
}
|
||||
out[this.charOff++] = (char) SI;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
char c = this.codePage.dbcsToUnicode(b, b2);
|
||||
if (c == '?' || c == '\uFFFD') {
|
||||
if (this.subMode) {
|
||||
c = this.subChars[0];
|
||||
} else {
|
||||
this.badInputLength = 2;
|
||||
throw new HODCharConversionException("Unmappable DBCS pair 0x" + Integer.toHexString(b) + ", 0x" + Integer.toHexString(b2));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.charOff >= outEnd) {
|
||||
throw new HODCharConversionException("Output buffer overflow at position " + this.charOff);
|
||||
}
|
||||
out[this.charOff++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
return this.charOff - outOff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int flush(char[] out, int outOff, int outEnd) throws HODCharConversionException {
|
||||
int count = 0;
|
||||
if (this.savedBytePresent) {
|
||||
if (!this.subMode) {
|
||||
reset();
|
||||
this.badInputLength = 1;
|
||||
throw new HODCharConversionException("Unclosed trailing DBCS byte at end of input");
|
||||
}
|
||||
if (outOff < outEnd) {
|
||||
out[outOff] = this.subChars[0];
|
||||
count = 1;
|
||||
}
|
||||
}
|
||||
reset();
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.byteOff = 0;
|
||||
this.charOff = 0;
|
||||
this.currentState = 0;
|
||||
this.savedBytePresent = false;
|
||||
this.savedByte = 0;
|
||||
this.badInputLength = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCharacterEncoding() {
|
||||
return this.codePage != null ? "Cp" + this.codePage.getCodePageId() : "DBCS_EBCDIC";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package haus.nightmare.lib3270j.converters;
|
||||
|
||||
import haus.nightmare.lib3270j.charset.CodePage;
|
||||
import haus.nightmare.lib3270j.charset.CodePageRegistry;
|
||||
|
||||
/**
|
||||
* Concrete converter implementing single-byte character set (SBCS) byte-to-char conversion.
|
||||
* Conforms to com.ibm.eNetwork.HOD.converters.ByteToCharSingleByte.
|
||||
*/
|
||||
public class ByteToCharSingleByte extends HODByteToCharConverter {
|
||||
|
||||
public ByteToCharSingleByte() {
|
||||
this(CodePageRegistry.getDefault());
|
||||
}
|
||||
|
||||
public ByteToCharSingleByte(CodePage codePage) {
|
||||
super(codePage != null ? codePage : CodePageRegistry.getDefault());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convert(byte[] in, int inOff, int inEnd, char[] out, int outOff, int outEnd)
|
||||
throws HODCharConversionException {
|
||||
if (in == null || inEnd <= inOff) {
|
||||
return 0;
|
||||
}
|
||||
if (out == null) {
|
||||
throw new HODCharConversionException("Output buffer cannot be null");
|
||||
}
|
||||
|
||||
this.byteOff = inOff;
|
||||
this.charOff = outOff;
|
||||
|
||||
while (this.byteOff < inEnd) {
|
||||
if (this.charOff >= outEnd) {
|
||||
throw new HODCharConversionException("Output char buffer overflow at position " + this.charOff);
|
||||
}
|
||||
|
||||
int b = in[this.byteOff] & 0xFF;
|
||||
char c = this.codePage.ebcdicToUnicode(b);
|
||||
|
||||
if (c == '\uFFFD') {
|
||||
if (this.subMode) {
|
||||
c = this.subChars[0];
|
||||
} else {
|
||||
this.badInputLength = 1;
|
||||
throw new HODCharConversionException("Unmappable byte 0x" + Integer.toHexString(b) + " at input index " + this.byteOff);
|
||||
}
|
||||
}
|
||||
|
||||
out[this.charOff++] = c;
|
||||
this.byteOff++;
|
||||
}
|
||||
|
||||
return this.charOff - outOff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int flush(char[] out, int outOff, int outEnd) {
|
||||
reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.byteOff = 0;
|
||||
this.charOff = 0;
|
||||
this.badInputLength = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCharacterEncoding() {
|
||||
return this.codePage != null ? "Cp" + this.codePage.getCodePageId() : "SingleByte";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package haus.nightmare.lib3270j.converters;
|
||||
|
||||
import haus.nightmare.lib3270j.charset.CodePage;
|
||||
import haus.nightmare.lib3270j.charset.CodePageRegistry;
|
||||
|
||||
/**
|
||||
* Concrete converter implementing mixed SBCS/DBCS char-to-byte conversion with transparent
|
||||
* Shift-Out (0x0E) and Shift-In (0x0F) state generation.
|
||||
* Conforms to com.ibm.eNetwork.HOD.converters.CharToByteDBCS_EBCDIC.
|
||||
*/
|
||||
public class CharToByteDBCS_EBCDIC extends HODCharToByteConverter {
|
||||
|
||||
public CharToByteDBCS_EBCDIC() {
|
||||
this(CodePageRegistry.getCodePage("930"));
|
||||
}
|
||||
|
||||
public CharToByteDBCS_EBCDIC(CodePage codePage) {
|
||||
super(codePage != null ? codePage : CodePageRegistry.getCodePage("930"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convert(char[] in, int inOff, int inEnd, byte[] out, int outOff, int outEnd)
|
||||
throws HODCharConversionException {
|
||||
if (in == null || inEnd <= inOff) {
|
||||
return 0;
|
||||
}
|
||||
if (out == null) {
|
||||
throw new HODCharConversionException("Output buffer cannot be null");
|
||||
}
|
||||
|
||||
this.charOff = inOff;
|
||||
this.byteOff = outOff;
|
||||
|
||||
while (this.charOff < inEnd) {
|
||||
char c = in[this.charOff];
|
||||
|
||||
// Explicit Shift-Out control character handling
|
||||
if (c == (char) SO) {
|
||||
if (this.currentState == 0) {
|
||||
if (this.byteOff >= outEnd) {
|
||||
throw new HODCharConversionException("Output buffer overflow writing SO at " + this.byteOff);
|
||||
}
|
||||
out[this.byteOff++] = SO;
|
||||
this.currentState = 1;
|
||||
}
|
||||
this.charOff++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Explicit Shift-In control character handling
|
||||
if (c == (char) SI) {
|
||||
if (this.currentState == 1) {
|
||||
if (this.byteOff >= outEnd) {
|
||||
throw new HODCharConversionException("Output buffer overflow writing SI at " + this.byteOff);
|
||||
}
|
||||
out[this.byteOff++] = SI;
|
||||
this.currentState = 0;
|
||||
}
|
||||
this.charOff++;
|
||||
continue;
|
||||
}
|
||||
|
||||
int dbcs = this.codePage.unicodeToDbcs(c);
|
||||
|
||||
if (dbcs >= 0) {
|
||||
// Character is DBCS: ensure in DBCS mode
|
||||
int needed = (this.currentState == 0) ? 3 : 2;
|
||||
if (this.byteOff + needed > outEnd) {
|
||||
throw new HODCharConversionException("Output buffer overflow writing DBCS character at " + this.byteOff);
|
||||
}
|
||||
|
||||
if (this.currentState == 0) {
|
||||
out[this.byteOff++] = SO;
|
||||
this.currentState = 1;
|
||||
}
|
||||
|
||||
out[this.byteOff++] = (byte) ((dbcs >> 8) & 0xFF);
|
||||
out[this.byteOff++] = (byte) (dbcs & 0xFF);
|
||||
} else {
|
||||
// Character is SBCS: ensure in SBCS mode
|
||||
int needed = (this.currentState == 1) ? 2 : 1;
|
||||
if (this.byteOff + needed > outEnd) {
|
||||
throw new HODCharConversionException("Output buffer overflow writing SBCS character at " + this.byteOff);
|
||||
}
|
||||
|
||||
if (this.currentState == 1) {
|
||||
out[this.byteOff++] = SI;
|
||||
this.currentState = 0;
|
||||
}
|
||||
|
||||
int ebc = this.codePage.unicodeToEbcdic(c);
|
||||
if (ebc < 0) {
|
||||
if (this.subMode) {
|
||||
out[this.byteOff++] = this.subBytes[0];
|
||||
} else {
|
||||
this.badInputLength = 1;
|
||||
throw new HODCharConversionException("Unmappable character '\\u" + Integer.toHexString(c) + "' at input index " + this.charOff);
|
||||
}
|
||||
} else {
|
||||
out[this.byteOff++] = (byte) (ebc & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
this.charOff++;
|
||||
}
|
||||
|
||||
return this.byteOff - outOff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int flush(byte[] out, int outOff, int outEnd) throws HODCharConversionException {
|
||||
int flushed = 0;
|
||||
if (this.currentState == 1) {
|
||||
if (outOff >= outEnd) {
|
||||
throw new HODCharConversionException("Output buffer overflow during flush at " + outOff);
|
||||
}
|
||||
out[outOff] = SI;
|
||||
flushed = 1;
|
||||
this.currentState = 0;
|
||||
}
|
||||
reset();
|
||||
return flushed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.charOff = 0;
|
||||
this.byteOff = 0;
|
||||
this.currentState = 0;
|
||||
this.badInputLength = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCharacterEncoding() {
|
||||
return this.codePage != null ? "Cp" + this.codePage.getCodePageId() : "DBCS_EBCDIC";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package haus.nightmare.lib3270j.converters;
|
||||
|
||||
import haus.nightmare.lib3270j.charset.CodePage;
|
||||
import haus.nightmare.lib3270j.charset.CodePageRegistry;
|
||||
|
||||
/**
|
||||
* Concrete converter implementing single-byte character set (SBCS) char-to-byte conversion.
|
||||
* Conforms to com.ibm.eNetwork.HOD.converters.CharToByteSingleByte.
|
||||
*/
|
||||
public class CharToByteSingleByte extends HODCharToByteConverter {
|
||||
|
||||
public CharToByteSingleByte() {
|
||||
this(CodePageRegistry.getDefault());
|
||||
}
|
||||
|
||||
public CharToByteSingleByte(CodePage codePage) {
|
||||
super(codePage != null ? codePage : CodePageRegistry.getDefault());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convert(char[] in, int inOff, int inEnd, byte[] out, int outOff, int outEnd)
|
||||
throws HODCharConversionException {
|
||||
if (in == null || inEnd <= inOff) {
|
||||
return 0;
|
||||
}
|
||||
if (out == null) {
|
||||
throw new HODCharConversionException("Output buffer cannot be null");
|
||||
}
|
||||
|
||||
this.charOff = inOff;
|
||||
this.byteOff = outOff;
|
||||
|
||||
while (this.charOff < inEnd) {
|
||||
if (this.byteOff >= outEnd) {
|
||||
throw new HODCharConversionException("Output byte buffer overflow at position " + this.byteOff);
|
||||
}
|
||||
|
||||
char c = in[this.charOff];
|
||||
int ebc = this.codePage.unicodeToEbcdic(c);
|
||||
|
||||
if (ebc < 0) {
|
||||
if (this.subMode) {
|
||||
out[this.byteOff++] = this.subBytes[0];
|
||||
} else {
|
||||
this.badInputLength = 1;
|
||||
throw new HODCharConversionException("Unmappable character '\\u" + Integer.toHexString(c) + "' at input index " + this.charOff);
|
||||
}
|
||||
} else {
|
||||
out[this.byteOff++] = (byte) (ebc & 0xFF);
|
||||
}
|
||||
|
||||
this.charOff++;
|
||||
}
|
||||
|
||||
return this.byteOff - outOff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int flush(byte[] out, int outOff, int outEnd) {
|
||||
reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.charOff = 0;
|
||||
this.byteOff = 0;
|
||||
this.badInputLength = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCharacterEncoding() {
|
||||
return this.codePage != null ? "Cp" + this.codePage.getCodePageId() : "SingleByte";
|
||||
}
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
package haus.nightmare.lib3270j.converters;
|
||||
|
||||
import haus.nightmare.lib3270j.charset.CodePage;
|
||||
import haus.nightmare.lib3270j.charset.CodePageRegistry;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* High-performance adapter and bridge converting byte streams (EBCDIC, ASCII, ISO, UTF)
|
||||
* to Unicode characters conforming to IBM Host On-Demand (HoD) converter specifications.
|
||||
* <p>
|
||||
* Supports dynamic factory resolution for all 275 HoD converter class names and transparent
|
||||
* Shift-In (0x0F) / Shift-Out (0x0E) DBCS transitions.
|
||||
*/
|
||||
public abstract class HODByteToCharConverter {
|
||||
|
||||
public static final int SO = 0x0E;
|
||||
public static final int SI = 0x0F;
|
||||
|
||||
protected int byteOff = 0;
|
||||
protected int charOff = 0;
|
||||
protected int badInputLength = 0;
|
||||
protected boolean subMode = true;
|
||||
protected char[] subChars = new char[]{'\uFFFD'};
|
||||
protected CodePage codePage;
|
||||
|
||||
// DBCS State
|
||||
protected int currentState = 0; // 0 = SBCS, 1 = DBCS
|
||||
protected boolean savedBytePresent = false;
|
||||
protected byte savedByte = 0;
|
||||
protected boolean preserveSOSI = false;
|
||||
|
||||
public HODByteToCharConverter() {
|
||||
}
|
||||
|
||||
public HODByteToCharConverter(CodePage codePage) {
|
||||
this.codePage = Objects.requireNonNull(codePage, "codePage cannot be null");
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up and instantiate a ByteToChar converter matching the given encoding,
|
||||
* codepage ID, alias, or HoD converter class name (e.g. "Cp037", "037", "ByteToCharCp1047", "ConverterFT1047").
|
||||
*
|
||||
* @param encoding encoding identifier or class name
|
||||
* @return initialized HODByteToCharConverter instance
|
||||
* @throws HODUnsupportedCodepageException if the codepage cannot be resolved
|
||||
*/
|
||||
public static HODByteToCharConverter getHODConverter(String encoding) throws HODUnsupportedCodepageException {
|
||||
if (encoding == null || encoding.trim().isEmpty()) {
|
||||
throw new HODUnsupportedCodepageException("Encoding name cannot be null or empty");
|
||||
}
|
||||
|
||||
CodePage cp = CodePageRegistry.resolveCodePage(encoding);
|
||||
if (cp == null) {
|
||||
throw new HODUnsupportedCodepageException("Unsupported codepage / converter: " + encoding);
|
||||
}
|
||||
|
||||
if (cp.isDBCS()) {
|
||||
return new ByteToCharDBCS_EBCDIC(cp);
|
||||
} else {
|
||||
return new ByteToCharSingleByte(cp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard converter lookup alias conforming to Java/HoD converter factory patterns.
|
||||
*/
|
||||
public static HODByteToCharConverter getConverter(String encoding) throws HODUnsupportedCodepageException {
|
||||
return getHODConverter(encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an array of bytes into an array of characters.
|
||||
*
|
||||
* @param in source byte buffer
|
||||
* @param inOff start offset in input buffer
|
||||
* @param inEnd end offset in input buffer (exclusive)
|
||||
* @param out destination char buffer
|
||||
* @param outOff start offset in output buffer
|
||||
* @param outEnd end offset in output buffer (exclusive)
|
||||
* @return number of characters converted and written into out
|
||||
* @throws HODCharConversionException if an unmappable byte is encountered with substitution disabled,
|
||||
* or if the output buffer overflows
|
||||
*/
|
||||
public abstract int convert(byte[] in, int inOff, int inEnd, char[] out, int outOff, int outEnd)
|
||||
throws HODCharConversionException;
|
||||
|
||||
/**
|
||||
* Overload supporting an extra boolean flag matching HoD CFR decompiled signature.
|
||||
*/
|
||||
public int convert(byte[] in, int inOff, int inEnd, char[] out, int outOff, int outEnd, boolean bl)
|
||||
throws HODCharConversionException {
|
||||
return convert(in, inOff, inEnd, out, outOff, outEnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush any buffered / trailing state into the output char buffer.
|
||||
*
|
||||
* @param out destination char buffer
|
||||
* @param outOff start offset
|
||||
* @param outEnd end offset
|
||||
* @return number of characters flushed
|
||||
* @throws HODCharConversionException if flush fails or trailing incomplete sequence cannot be converted
|
||||
*/
|
||||
public abstract int flush(char[] out, int outOff, int outEnd) throws HODCharConversionException;
|
||||
|
||||
/**
|
||||
* Reset converter state and offsets to defaults.
|
||||
*/
|
||||
public abstract void reset();
|
||||
|
||||
/**
|
||||
* Get the canonical character encoding name (e.g. "Cp037", "Cp930").
|
||||
*/
|
||||
public abstract String getCharacterEncoding();
|
||||
|
||||
/**
|
||||
* Convert an entire byte array to a char array matching IBM HoD's hodConvertAll API.
|
||||
*/
|
||||
public char[] hodConvertAll(byte[] in) throws HODCharConversionException {
|
||||
if (in == null) return new char[0];
|
||||
reset();
|
||||
char[] buf = new char[Math.max(16, in.length * 2 + 16)];
|
||||
int converted = convert(in, 0, in.length, buf, 0, buf.length);
|
||||
int flushed = flush(buf, converted, buf.length);
|
||||
int total = converted + flushed;
|
||||
char[] result = new char[total];
|
||||
System.arraycopy(buf, 0, result, 0, total);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience alias for hodConvertAll.
|
||||
*/
|
||||
public char[] convertAll(byte[] in) throws HODCharConversionException {
|
||||
return hodConvertAll(in);
|
||||
}
|
||||
|
||||
public void setSubstitutionMode(boolean mode) {
|
||||
this.subMode = mode;
|
||||
}
|
||||
|
||||
public boolean getSubstitutionMode() {
|
||||
return this.subMode;
|
||||
}
|
||||
|
||||
public void setSubstitutionChars(char[] subChars) {
|
||||
if (subChars != null && subChars.length > 0) {
|
||||
this.subChars = subChars;
|
||||
}
|
||||
}
|
||||
|
||||
public char[] getSubstitutionChars() {
|
||||
return this.subChars;
|
||||
}
|
||||
|
||||
public int getBadInputLength() {
|
||||
return this.badInputLength;
|
||||
}
|
||||
|
||||
public int nextByteIndex() {
|
||||
return this.byteOff;
|
||||
}
|
||||
|
||||
public int nextCharIndex() {
|
||||
return this.charOff;
|
||||
}
|
||||
|
||||
public CodePage getCodePage() {
|
||||
return this.codePage;
|
||||
}
|
||||
|
||||
public void setPreserveSOSI(boolean preserve) {
|
||||
this.preserveSOSI = preserve;
|
||||
}
|
||||
|
||||
public boolean isPreserveSOSI() {
|
||||
return this.preserveSOSI;
|
||||
}
|
||||
|
||||
public int getCurrentState() {
|
||||
return this.currentState;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package haus.nightmare.lib3270j.converters;
|
||||
|
||||
import java.io.CharConversionException;
|
||||
|
||||
/**
|
||||
* Exception thrown when character conversion fails during Host On-Demand converter processing.
|
||||
* Conforms to com.ibm.eNetwork.HOD.common.HODCharConversionException.
|
||||
*/
|
||||
public class HODCharConversionException extends CharConversionException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public HODCharConversionException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public HODCharConversionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
package haus.nightmare.lib3270j.converters;
|
||||
|
||||
import haus.nightmare.lib3270j.charset.CodePage;
|
||||
import haus.nightmare.lib3270j.charset.CodePageRegistry;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* High-performance adapter and bridge converting Unicode characters to byte streams
|
||||
* (EBCDIC, ASCII, ISO, UTF) conforming to IBM Host On-Demand (HoD) converter specifications.
|
||||
* <p>
|
||||
* Supports dynamic factory resolution for all 275 HoD converter class names and transparent
|
||||
* Shift-In (0x0F) / Shift-Out (0x0E) DBCS transitions.
|
||||
*/
|
||||
public abstract class HODCharToByteConverter {
|
||||
|
||||
public static final byte SO = 0x0E;
|
||||
public static final byte SI = 0x0F;
|
||||
|
||||
protected int byteOff = 0;
|
||||
protected int charOff = 0;
|
||||
protected int badInputLength = 0;
|
||||
protected boolean subMode = true;
|
||||
protected byte[] subBytes = new byte[]{(byte) 0x6F}; // 0x6F '?' in EBCDIC (or safe fallback)
|
||||
protected CodePage codePage;
|
||||
|
||||
// DBCS State
|
||||
protected int currentState = 0; // 0 = SBCS, 1 = DBCS
|
||||
|
||||
public HODCharToByteConverter() {
|
||||
}
|
||||
|
||||
public HODCharToByteConverter(CodePage codePage) {
|
||||
this.codePage = Objects.requireNonNull(codePage, "codePage cannot be null");
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up and instantiate a CharToByte converter matching the given encoding,
|
||||
* codepage ID, alias, or HoD converter class name (e.g. "Cp037", "037", "CharToByteCp1047", "ConverterFT1047").
|
||||
*
|
||||
* @param encoding encoding identifier or class name
|
||||
* @return initialized HODCharToByteConverter instance
|
||||
* @throws HODUnsupportedCodepageException if the codepage cannot be resolved
|
||||
*/
|
||||
public static HODCharToByteConverter getHODConverter(String encoding) throws HODUnsupportedCodepageException {
|
||||
if (encoding == null || encoding.trim().isEmpty()) {
|
||||
throw new HODUnsupportedCodepageException("Encoding name cannot be null or empty");
|
||||
}
|
||||
|
||||
CodePage cp = CodePageRegistry.resolveCodePage(encoding);
|
||||
if (cp == null) {
|
||||
throw new HODUnsupportedCodepageException("Unsupported codepage / converter: " + encoding);
|
||||
}
|
||||
|
||||
if (cp.isDBCS()) {
|
||||
return new CharToByteDBCS_EBCDIC(cp);
|
||||
} else {
|
||||
return new CharToByteSingleByte(cp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard converter lookup alias conforming to Java/HoD converter factory patterns.
|
||||
*/
|
||||
public static HODCharToByteConverter getConverter(String encoding) throws HODUnsupportedCodepageException {
|
||||
return getHODConverter(encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an array of characters into an array of bytes.
|
||||
*
|
||||
* @param in source char buffer
|
||||
* @param inOff start offset in input buffer
|
||||
* @param inEnd end offset in input buffer (exclusive)
|
||||
* @param out destination byte buffer
|
||||
* @param outOff start offset in output buffer
|
||||
* @param outEnd end offset in output buffer (exclusive)
|
||||
* @return number of bytes converted and written into out
|
||||
* @throws HODCharConversionException if an unmappable char is encountered with substitution disabled,
|
||||
* or if the output buffer overflows
|
||||
*/
|
||||
public abstract int convert(char[] in, int inOff, int inEnd, byte[] out, int outOff, int outEnd)
|
||||
throws HODCharConversionException;
|
||||
|
||||
/**
|
||||
* Flush any buffered / trailing shift state into the output byte buffer.
|
||||
*
|
||||
* @param out destination byte buffer
|
||||
* @param outOff start offset
|
||||
* @param outEnd end offset
|
||||
* @return number of bytes flushed
|
||||
* @throws HODCharConversionException if output buffer overflows
|
||||
*/
|
||||
public abstract int flush(byte[] out, int outOff, int outEnd) throws HODCharConversionException;
|
||||
|
||||
/**
|
||||
* Reset converter state and offsets to defaults.
|
||||
*/
|
||||
public abstract void reset();
|
||||
|
||||
/**
|
||||
* Get the canonical character encoding name (e.g. "Cp037", "Cp930").
|
||||
*/
|
||||
public abstract String getCharacterEncoding();
|
||||
|
||||
/**
|
||||
* Convert an entire char array to a byte array matching IBM HoD's hodConvertAll API.
|
||||
*/
|
||||
public byte[] hodConvertAll(char[] in) throws HODCharConversionException {
|
||||
if (in == null) return new byte[0];
|
||||
reset();
|
||||
byte[] buf = new byte[Math.max(16, in.length * 3 + 16)];
|
||||
int converted = convert(in, 0, in.length, buf, 0, buf.length);
|
||||
int flushed = flush(buf, converted, buf.length);
|
||||
int total = converted + flushed;
|
||||
byte[] result = new byte[total];
|
||||
System.arraycopy(buf, 0, result, 0, total);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience alias for hodConvertAll.
|
||||
*/
|
||||
public byte[] convertAll(char[] in) throws HODCharConversionException {
|
||||
return hodConvertAll(in);
|
||||
}
|
||||
|
||||
public void setSubstitutionMode(boolean mode) {
|
||||
this.subMode = mode;
|
||||
}
|
||||
|
||||
public boolean getSubstitutionMode() {
|
||||
return this.subMode;
|
||||
}
|
||||
|
||||
public void setSubstitutionBytes(byte[] subBytes) {
|
||||
if (subBytes != null && subBytes.length > 0) {
|
||||
this.subBytes = subBytes;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getSubstitutionBytes() {
|
||||
return this.subBytes;
|
||||
}
|
||||
|
||||
public int getBadInputLength() {
|
||||
return this.badInputLength;
|
||||
}
|
||||
|
||||
public int nextByteIndex() {
|
||||
return this.byteOff;
|
||||
}
|
||||
|
||||
public int nextCharIndex() {
|
||||
return this.charOff;
|
||||
}
|
||||
|
||||
public int getMaxBytesPerChar() {
|
||||
return (codePage != null && codePage.isDBCS()) ? 3 : 1; // At most 3 bytes (SO + 2-byte DBCS)
|
||||
}
|
||||
|
||||
public CodePage getCodePage() {
|
||||
return this.codePage;
|
||||
}
|
||||
|
||||
public int getCurrentState() {
|
||||
return this.currentState;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package haus.nightmare.lib3270j.converters;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* Exception thrown when a requested character encoding or codepage identifier cannot be resolved.
|
||||
* Conforms to com.ibm.eNetwork.HOD.common.HODUnsupportedCodepageException.
|
||||
*/
|
||||
public class HODUnsupportedCodepageException extends UnsupportedEncodingException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public HODUnsupportedCodepageException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public HODUnsupportedCodepageException(String encoding) {
|
||||
super(encoding);
|
||||
}
|
||||
}
|
||||
+848
-131
File diff suppressed because it is too large
Load Diff
+276
-120
@@ -15,16 +15,17 @@ public class QueryReplyBuilder {
|
||||
|
||||
private static final Logger log = Logger.getLogger(QueryReplyBuilder.class.getName());
|
||||
|
||||
// Canned values from 3279-2 (matching sf.c)
|
||||
private static final int SW_3279_2 = 0x09;
|
||||
private static final int SH_3279_2 = 0x0c;
|
||||
private static final int Xr_3279_2 = 0x000a02e5;
|
||||
private static final int Yr_3279_2 = 0x0002006f;
|
||||
|
||||
// Usable Area physical dimensions matching IBM Host On-Demand DS3270.java (Inches, 96 dpi: 0x00010060)
|
||||
private static final int Xr_HOD = 0x00010060;
|
||||
private static final int Yr_HOD = 0x00010060;
|
||||
|
||||
private final ScreenBuffer screen;
|
||||
private GraphicsMode graphicsMode = GraphicsMode.BOTH;
|
||||
|
||||
// Base query reply codes (text mode)
|
||||
// Base query reply codes (text mode, matches HOD DS3270.java queryEquiv)
|
||||
private static final int[] SUPPORTED_QR_BASE = {
|
||||
QR_SUMMARY, // 0x80 — summary must list itself
|
||||
QR_USABLE_AREA, // 0x81
|
||||
@@ -34,10 +35,11 @@ public class QueryReplyBuilder {
|
||||
QR_HIGHLIGHTING, // 0x87
|
||||
QR_REPLY_MODES, // 0x88
|
||||
QR_DDM, // 0x95 - Distributed Data Management (file transfer)
|
||||
QR_IMP_PART, // 0xa6
|
||||
QR_AUXDA, // 0x99 - Auxiliary Devices
|
||||
QR_IMP_PART, // 0xa6 - Implicit Partition Sizes
|
||||
};
|
||||
|
||||
// Vector graphics query reply codes matching HOD DS3270.java line 1723
|
||||
// Vector graphics query reply codes matching HOD QueryReply3270Constants.java QR_3270_WITHOUT_DCBS_SUMMARY_STRING
|
||||
private static final int[] SUPPORTED_QR_VECTOR = {
|
||||
QR_SUMMARY, // 0x80
|
||||
QR_USABLE_AREA, // 0x81
|
||||
@@ -46,17 +48,17 @@ public class QueryReplyBuilder {
|
||||
QR_COLOR, // 0x86
|
||||
QR_HIGHLIGHTING, // 0x87
|
||||
QR_REPLY_MODES, // 0x88
|
||||
QR_SAVE_RESTORE, // 0x8c
|
||||
QR_OUTLINING, // 0x8c
|
||||
QR_DDM, // 0x95
|
||||
QR_TRANSPARENCY, // 0x99
|
||||
QR_AUXDA, // 0x99
|
||||
QR_IMP_PART, // 0xa6
|
||||
QR_RPQ_NAMES, // 0xa8
|
||||
QR_GRAPHICS, // 0xb0
|
||||
QR_GIMAGE, // 0xb1
|
||||
QR_AUX_DEV, // 0xb2
|
||||
QR_OEM_FMT, // 0xb3
|
||||
QR_GCOLOR, // 0xb4
|
||||
QR_GSYMBOLS, // 0xb6
|
||||
QR_TRANSPARENCY, // 0xa8
|
||||
QR_SEGMENT, // 0xb0
|
||||
QR_PROCEDURE, // 0xb1
|
||||
QR_LINETYPE, // 0xb2
|
||||
QR_PORT, // 0xb3
|
||||
QR_GRCOLOR, // 0xb4
|
||||
QR_GRSYMBOLSET, // 0xb6
|
||||
};
|
||||
|
||||
public QueryReplyBuilder(ScreenBuffer screen) {
|
||||
@@ -77,7 +79,8 @@ public class QueryReplyBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build all query replies as a single AID_SF + structured field response.
|
||||
* Build base query replies in response to a generic Read Partition Query (0x02).
|
||||
* Returns base text/presentation summary structured fields (matches HOD DS3270.java line 1723).
|
||||
*/
|
||||
public byte[] buildAllQueryReplies(int maxCols, int maxRows, int bufferSize) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(512);
|
||||
@@ -85,62 +88,90 @@ public class QueryReplyBuilder {
|
||||
// AID byte for structured field
|
||||
out.write(AID_SF);
|
||||
|
||||
// Summary
|
||||
// Summary (0x80) - lists all supported capabilities
|
||||
appendQueryReply(out, QR_SUMMARY, buildSummary());
|
||||
|
||||
// Usable Area
|
||||
// Usable Area (0x81)
|
||||
appendQueryReply(out, QR_USABLE_AREA, buildUsableArea(maxCols, maxRows, bufferSize));
|
||||
|
||||
// Alpha Partitions
|
||||
// Alpha Partitions (0x84)
|
||||
appendQueryReply(out, QR_ALPHA_PART, buildAlphaPartitions(maxRows));
|
||||
|
||||
// Character Sets
|
||||
// Character Sets (0x85)
|
||||
appendQueryReply(out, QR_CHARSETS, buildCharsets());
|
||||
|
||||
// Color
|
||||
// Color (0x86)
|
||||
appendQueryReply(out, QR_COLOR, buildColor());
|
||||
|
||||
// Highlighting
|
||||
// Highlighting (0x87)
|
||||
appendQueryReply(out, QR_HIGHLIGHTING, buildHighlighting());
|
||||
|
||||
// Reply Modes (0x88)
|
||||
appendQueryReply(out, QR_REPLY_MODES, buildReplyModes());
|
||||
|
||||
if (graphicsMode.isVectorGraphicsEnabled()) {
|
||||
// Save/Restore (0x8C)
|
||||
appendQueryReply(out, QR_SAVE_RESTORE, buildSaveRestore());
|
||||
boolean isDbcs = (screen != null && screen.getTranslator() != null && screen.getTranslator().isDBCS());
|
||||
if (isDbcs) {
|
||||
// Outlining (0x8C)
|
||||
appendQueryReply(out, QR_OUTLINING, buildOutlining());
|
||||
// DBCS Asia (0x91)
|
||||
appendQueryReply(out, QR_DBCS_ASIA, buildDbcsAsia());
|
||||
}
|
||||
|
||||
// Distributed Data Management (0x95)
|
||||
appendQueryReply(out, QR_DDM, buildDdm(4096));
|
||||
|
||||
if (graphicsMode.isVectorGraphicsEnabled()) {
|
||||
// Transparency (0x99)
|
||||
appendQueryReply(out, QR_TRANSPARENCY, buildTransparency());
|
||||
}
|
||||
// Auxiliary Devices (0x99)
|
||||
appendQueryReply(out, QR_AUXDA, buildAuxDa());
|
||||
|
||||
// Implicit Partition (0xA6)
|
||||
appendQueryReply(out, QR_IMP_PART, buildImplicitPartition(maxCols, maxRows));
|
||||
|
||||
// Vector Graphics QRs if enabled
|
||||
log.info("Built " + out.size() + " bytes of base query replies (graphicsMode=" + graphicsMode + ")");
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build complete query replies including vector graphics (when SF_RPQ_ALL 0x80 is requested).
|
||||
*/
|
||||
public byte[] buildCompleteQueryReplies(int maxCols, int maxRows, int bufferSize) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(512);
|
||||
out.write(AID_SF);
|
||||
|
||||
appendQueryReply(out, QR_SUMMARY, buildSummary());
|
||||
appendQueryReply(out, QR_USABLE_AREA, buildUsableArea(maxCols, maxRows, bufferSize));
|
||||
appendQueryReply(out, QR_ALPHA_PART, buildAlphaPartitions(maxRows));
|
||||
appendQueryReply(out, QR_CHARSETS, buildCharsets());
|
||||
appendQueryReply(out, QR_COLOR, buildColor());
|
||||
appendQueryReply(out, QR_HIGHLIGHTING, buildHighlighting());
|
||||
appendQueryReply(out, QR_REPLY_MODES, buildReplyModes());
|
||||
|
||||
appendQueryReply(out, QR_OUTLINING, buildOutlining());
|
||||
boolean isDbcs = (screen != null && screen.getTranslator() != null && screen.getTranslator().isDBCS());
|
||||
if (isDbcs) {
|
||||
appendQueryReply(out, QR_DBCS_ASIA, buildDbcsAsia());
|
||||
}
|
||||
appendQueryReply(out, QR_DDM, buildDdm(4096));
|
||||
appendQueryReply(out, QR_AUXDA, buildAuxDa());
|
||||
appendQueryReply(out, QR_IMP_PART, buildImplicitPartition(maxCols, maxRows));
|
||||
|
||||
if (graphicsMode.isVectorGraphicsEnabled()) {
|
||||
appendQueryReply(out, QR_RPQ_NAMES, buildRpqNames()); // 0xA8
|
||||
appendQueryReply(out, QR_GRAPHICS, buildGraphics(maxCols, maxRows)); // 0xB0
|
||||
appendQueryReply(out, QR_GIMAGE, buildGImage(maxCols, maxRows)); // 0xB1
|
||||
appendQueryReply(out, QR_AUX_DEV, buildAuxDev()); // 0xB2
|
||||
appendOemFmt(out); // 0xB3
|
||||
appendQueryReply(out, QR_GCOLOR, buildGColor()); // 0xB4
|
||||
appendQueryReply(out, QR_GSYMBOLS, buildGSymbols()); // 0xB6
|
||||
appendQueryReply(out, QR_TRANSPARENCY, buildTransparency()); // 0xA8
|
||||
appendQueryReply(out, QR_SEGMENT, buildSegment(maxCols, maxRows)); // 0xB0
|
||||
appendQueryReply(out, QR_PROCEDURE, buildProcedure(maxCols, maxRows)); // 0xB1
|
||||
appendQueryReply(out, QR_LINETYPE, buildLineType()); // 0xB2
|
||||
appendPort(out); // 0xB3
|
||||
appendQueryReply(out, QR_GRCOLOR, buildGrColor()); // 0xB4
|
||||
appendQueryReply(out, QR_GRSYMBOLSET, buildGrSymbolSet()); // 0xB6
|
||||
}
|
||||
|
||||
log.info("Built " + out.size() + " bytes of all query replies (graphicsMode=" + graphicsMode + ")");
|
||||
log.info("Built " + out.size() + " bytes of complete query replies (graphicsMode=" + graphicsMode + ")");
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build specific query replies in response to a Read Partition Query List (SF_RPQ_LIST).
|
||||
* For any unsupported requested query code, emits a QR_NULL (0xFF) structured field
|
||||
* matching x3270 sf.c behavior.
|
||||
* matching HOD DS3270.java line 1835.
|
||||
*/
|
||||
public byte[] buildQueryReplies(byte[] requestedCodes, int maxCols, int maxRows, int bufferSize) {
|
||||
if (requestedCodes == null || requestedCodes.length == 0) {
|
||||
@@ -174,72 +205,70 @@ public class QueryReplyBuilder {
|
||||
case QR_REPLY_MODES:
|
||||
appendQueryReply(out, QR_REPLY_MODES, buildReplyModes());
|
||||
break;
|
||||
case QR_SAVE_RESTORE:
|
||||
if (graphicsMode.isVectorGraphicsEnabled()) {
|
||||
appendQueryReply(out, QR_SAVE_RESTORE, buildSaveRestore());
|
||||
case QR_OUTLINING: // 0x8C
|
||||
appendQueryReply(out, QR_OUTLINING, buildOutlining());
|
||||
break;
|
||||
case QR_DBCS_ASIA: // 0x91
|
||||
if (screen != null && screen.getTranslator() != null && screen.getTranslator().isDBCS()) {
|
||||
appendQueryReply(out, QR_DBCS_ASIA, buildDbcsAsia());
|
||||
} else {
|
||||
appendQueryReply(out, QR_NULL, new byte[0]);
|
||||
}
|
||||
break;
|
||||
case QR_DDM:
|
||||
case QR_DDM: // 0x95
|
||||
appendQueryReply(out, QR_DDM, buildDdm(4096));
|
||||
break;
|
||||
case QR_TRANSPARENCY:
|
||||
case QR_AUXDA: // 0x99
|
||||
appendQueryReply(out, QR_AUXDA, buildAuxDa());
|
||||
break;
|
||||
case QR_IMP_PART: // 0xA6
|
||||
appendQueryReply(out, QR_IMP_PART, buildImplicitPartition(maxCols, maxRows));
|
||||
break;
|
||||
case QR_TRANSPARENCY: // 0xA8
|
||||
if (graphicsMode.isVectorGraphicsEnabled()) {
|
||||
appendQueryReply(out, QR_TRANSPARENCY, buildTransparency());
|
||||
} else {
|
||||
appendQueryReply(out, QR_NULL, new byte[0]);
|
||||
}
|
||||
break;
|
||||
case QR_IMP_PART:
|
||||
appendQueryReply(out, QR_IMP_PART, buildImplicitPartition(maxCols, maxRows));
|
||||
break;
|
||||
case QR_RPQ_NAMES:
|
||||
case QR_RPQNAMES:
|
||||
case QR_SEGMENT: // 0xB0
|
||||
if (graphicsMode.isVectorGraphicsEnabled()) {
|
||||
appendQueryReply(out, code, buildRpqNames());
|
||||
appendQueryReply(out, QR_SEGMENT, buildSegment(maxCols, maxRows));
|
||||
} else {
|
||||
appendQueryReply(out, QR_NULL, new byte[0]);
|
||||
}
|
||||
break;
|
||||
case QR_GRAPHICS:
|
||||
case QR_PROCEDURE: // 0xB1
|
||||
if (graphicsMode.isVectorGraphicsEnabled()) {
|
||||
appendQueryReply(out, QR_GRAPHICS, buildGraphics(maxCols, maxRows));
|
||||
appendQueryReply(out, QR_PROCEDURE, buildProcedure(maxCols, maxRows));
|
||||
} else {
|
||||
appendQueryReply(out, QR_NULL, new byte[0]);
|
||||
}
|
||||
break;
|
||||
case QR_GIMAGE:
|
||||
case QR_LINETYPE: // 0xB2
|
||||
if (graphicsMode.isVectorGraphicsEnabled()) {
|
||||
appendQueryReply(out, QR_GIMAGE, buildGImage(maxCols, maxRows));
|
||||
appendQueryReply(out, QR_LINETYPE, buildLineType());
|
||||
} else {
|
||||
appendQueryReply(out, QR_NULL, new byte[0]);
|
||||
}
|
||||
break;
|
||||
case QR_AUX_DEV:
|
||||
case QR_PORT: // 0xB3
|
||||
if (graphicsMode.isVectorGraphicsEnabled()) {
|
||||
appendQueryReply(out, QR_AUX_DEV, buildAuxDev());
|
||||
appendPort(out);
|
||||
} else {
|
||||
appendQueryReply(out, QR_NULL, new byte[0]);
|
||||
}
|
||||
break;
|
||||
case QR_OEM_FMT:
|
||||
case QR_GRCOLOR: // 0xB4
|
||||
if (graphicsMode.isVectorGraphicsEnabled()) {
|
||||
appendOemFmt(out);
|
||||
appendQueryReply(out, QR_GRCOLOR, buildGrColor());
|
||||
} else {
|
||||
appendQueryReply(out, QR_NULL, new byte[0]);
|
||||
}
|
||||
break;
|
||||
case QR_GCOLOR:
|
||||
case QR_GRSYMBOLSET: // 0xB6
|
||||
if (graphicsMode.isVectorGraphicsEnabled()) {
|
||||
appendQueryReply(out, QR_GCOLOR, buildGColor());
|
||||
} else {
|
||||
appendQueryReply(out, QR_NULL, new byte[0]);
|
||||
}
|
||||
break;
|
||||
case QR_GSYMBOLS:
|
||||
if (graphicsMode.isVectorGraphicsEnabled()) {
|
||||
appendQueryReply(out, QR_GSYMBOLS, buildGSymbols());
|
||||
appendQueryReply(out, QR_GRSYMBOLSET, buildGrSymbolSet());
|
||||
} else {
|
||||
appendQueryReply(out, QR_NULL, new byte[0]);
|
||||
}
|
||||
@@ -265,34 +294,44 @@ public class QueryReplyBuilder {
|
||||
out.write(data, 0, data.length);
|
||||
}
|
||||
|
||||
private byte[] buildSummary() {
|
||||
public byte[] buildSummary() {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int[] codes = graphicsMode.isVectorGraphicsEnabled() ? SUPPORTED_QR_VECTOR : SUPPORTED_QR_BASE;
|
||||
boolean isDbcs = (screen != null && screen.getTranslator() != null && screen.getTranslator().isDBCS());
|
||||
for (int code : codes) {
|
||||
out.write(code);
|
||||
if (isDbcs && code == QR_OUTLINING) {
|
||||
out.write(QR_DBCS_ASIA); // 0x91
|
||||
}
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private byte[] buildUsableArea(int maxCols, int maxRows, int bufferSize) {
|
||||
public byte[] buildUsableArea() {
|
||||
int maxCols = (screen != null) ? screen.getMaxCols() : MODEL_2_COLS;
|
||||
int maxRows = (screen != null) ? screen.getMaxRows() : MODEL_2_ROWS;
|
||||
return buildUsableArea(maxCols, maxRows, maxCols * maxRows);
|
||||
}
|
||||
|
||||
public byte[] buildUsableArea(int maxCols, int maxRows, int bufferSize) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(19);
|
||||
out.write(0x01); // 12/14-bit addressing
|
||||
out.write(graphicsMode.isVectorGraphicsEnabled() ? 0x03 : 0x01); // 12/14-bit addressing + graphics flag (matching HOD DS3270.java)
|
||||
out.write(0x00); // no special character features
|
||||
out.write((maxCols >> 8) & 0xFF); // usable width high
|
||||
out.write(maxCols & 0xFF); // usable width low
|
||||
out.write((maxRows >> 8) & 0xFF); // usable height high
|
||||
out.write(maxRows & 0xFF); // usable height low
|
||||
out.write(0x01); // units (mm)
|
||||
// Xr (4 bytes) - canned from 3279-2
|
||||
out.write((Xr_3279_2 >> 24) & 0xFF);
|
||||
out.write((Xr_3279_2 >> 16) & 0xFF);
|
||||
out.write((Xr_3279_2 >> 8) & 0xFF);
|
||||
out.write(Xr_3279_2 & 0xFF);
|
||||
// Yr (4 bytes) - canned from 3279-2
|
||||
out.write((Yr_3279_2 >> 24) & 0xFF);
|
||||
out.write((Yr_3279_2 >> 16) & 0xFF);
|
||||
out.write((Yr_3279_2 >> 8) & 0xFF);
|
||||
out.write(Yr_3279_2 & 0xFF);
|
||||
out.write(0x00); // units (0x00 = inches, matching IBM Host On-Demand QR_USEAREA_STRING)
|
||||
// Xr (4 bytes) - matching IBM Host On-Demand QR_USEAREA_STRING (96 DPI)
|
||||
out.write((Xr_HOD >> 24) & 0xFF);
|
||||
out.write((Xr_HOD >> 16) & 0xFF);
|
||||
out.write((Xr_HOD >> 8) & 0xFF);
|
||||
out.write(Xr_HOD & 0xFF);
|
||||
// Yr (4 bytes) - matching IBM Host On-Demand QR_USEAREA_STRING (96 DPI)
|
||||
out.write((Yr_HOD >> 24) & 0xFF);
|
||||
out.write((Yr_HOD >> 16) & 0xFF);
|
||||
out.write((Yr_HOD >> 8) & 0xFF);
|
||||
out.write(Yr_HOD & 0xFF);
|
||||
int charW = getCharWidth();
|
||||
int charH = getCharHeight();
|
||||
out.write(charW); // AW
|
||||
@@ -304,6 +343,9 @@ public class QueryReplyBuilder {
|
||||
}
|
||||
|
||||
public int getCharWidth() {
|
||||
if (screen != null && screen.getTranslator() != null && screen.getTranslator().isDBCS()) {
|
||||
return 12;
|
||||
}
|
||||
return SW_3279_2; // 9
|
||||
}
|
||||
|
||||
@@ -323,8 +365,14 @@ public class QueryReplyBuilder {
|
||||
return graphicsMode.isVectorGraphicsEnabled() ? 0x10 : SH_3279_2;
|
||||
}
|
||||
|
||||
private byte[] buildAlphaPartitions(int maxRows) {
|
||||
int bufSize = screen.getMaxCols() * screen.getMaxRows();
|
||||
public byte[] buildAlphaPartitions() {
|
||||
int maxRows = (screen != null) ? screen.getMaxRows() : MODEL_2_ROWS;
|
||||
return buildAlphaPartitions(maxRows);
|
||||
}
|
||||
|
||||
public byte[] buildAlphaPartitions(int maxRows) {
|
||||
int maxCols = (screen != null) ? screen.getMaxCols() : MODEL_2_COLS;
|
||||
int bufSize = maxCols * maxRows;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(4);
|
||||
out.write(0x00); // max partitions (1 byte: 0x00 = 1 partition)
|
||||
out.write((bufSize >> 8) & 0xFF); // total partition storage high
|
||||
@@ -333,12 +381,19 @@ public class QueryReplyBuilder {
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private byte[] buildCharsets() {
|
||||
public byte[] buildCharsets() {
|
||||
int charW = getCharWidth();
|
||||
int charH = getCharHeight();
|
||||
|
||||
if (graphicsMode.isProgrammedSymbolsEnabled()) {
|
||||
// Programmed Symbols mode (3279 PS with LoadPS 0x0A, flags1 = 0xA2 for GE + PS + CGCSGID)
|
||||
int cgcsgid = 0x02B9;
|
||||
int cpgid = 0x0025;
|
||||
if (screen != null && screen.getTranslator() != null) {
|
||||
cgcsgid = screen.getTranslator().getCgcsgid();
|
||||
cpgid = screen.getTranslator().getCpgid();
|
||||
}
|
||||
|
||||
if (graphicsMode == GraphicsMode.PROGRAMMED_SYMBOLS) {
|
||||
// Programmed Symbols only mode (3279 PS with LoadPS 0x0A, flags1 = 0xA2 for GE + PS + CGCSGID)
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(65);
|
||||
out.write(0xa2); // flags: GE (0x80), PS/Loadable Charsets (0x20), CGCSGID present (0x02)
|
||||
out.write(0x00); // more flags
|
||||
@@ -349,8 +404,10 @@ public class QueryReplyBuilder {
|
||||
out.write(0x00); // Load PS device type (low)
|
||||
out.write(0x00); // reserved
|
||||
out.write(0x07); // DL = 7 bytes per descriptor
|
||||
// Descriptor 1 (SET 0): default character set (non-loadable, single plane, CP037)
|
||||
out.write(0x00); out.write(0x10); out.write(0x00); out.write(0x02); out.write(0xb9); out.write(0x00); out.write(0x25);
|
||||
// Descriptor 1 (SET 0): default character set (non-loadable, single plane, active codepage)
|
||||
out.write(0x00); out.write(0x10); out.write(0x00);
|
||||
out.write((cgcsgid >> 8) & 0xFF); out.write(cgcsgid & 0xFF);
|
||||
out.write((cpgid >> 8) & 0xFF); out.write(cpgid & 0xFF);
|
||||
// Descriptor 2 (SET 1): APL/GE character set
|
||||
out.write(0x01); out.write(0x00); out.write(0xf1); out.write(0x03); out.write(0xc3); out.write(0x01); out.write(0x36);
|
||||
// Loadable Single-Plane PS Sets (PSA, PSB: Slots 2, 3) - Flags = 0x80 (Loadable, single plane)
|
||||
@@ -375,14 +432,16 @@ public class QueryReplyBuilder {
|
||||
out.write(0x00);
|
||||
out.write(0x00);
|
||||
out.write(0x07); // DL = 7
|
||||
// Set 0 (Base EBCDIC - Non-loadable, single plane, CP037)
|
||||
out.write(0x00); out.write(0x10); out.write(0x00); out.write(0x02); out.write(0xb9); out.write(0x00); out.write(0x25);
|
||||
// Set 0 (Base EBCDIC - Non-loadable, single plane, active codepage)
|
||||
out.write(0x00); out.write(0x10); out.write(0x00);
|
||||
out.write((cgcsgid >> 8) & 0xFF); out.write(cgcsgid & 0xFF);
|
||||
out.write((cpgid >> 8) & 0xFF); out.write(cpgid & 0xFF);
|
||||
// Set 1 (APL/Text)
|
||||
out.write(0x01); out.write(0x00); out.write(0xf1); out.write(0x03); out.write(0xc3); out.write(0x01); out.write(0x36);
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private byte[] buildColor() {
|
||||
public byte[] buildColor() {
|
||||
// Alphanumeric Color (matches HOD: 8 pairs, F1..F7 + default F4 green, 18 bytes payload / 22 bytes total)
|
||||
return new byte[] {
|
||||
0x00, 0x08, 0x00, (byte) 0xF4,
|
||||
@@ -396,7 +455,7 @@ public class QueryReplyBuilder {
|
||||
};
|
||||
}
|
||||
|
||||
private byte[] buildHighlighting() {
|
||||
public byte[] buildHighlighting() {
|
||||
// Highlighting (matches HOD: 4 pairs: default F0, Blink F1, Reverse F2, Underscore F4, 9 bytes payload / 13 bytes total)
|
||||
return new byte[] {
|
||||
0x04, 0x00, (byte) 0xF0,
|
||||
@@ -406,11 +465,15 @@ public class QueryReplyBuilder {
|
||||
};
|
||||
}
|
||||
|
||||
private byte[] buildReplyModes() {
|
||||
public byte[] buildReplyModes() {
|
||||
return new byte[] { SF_SRM_FIELD, SF_SRM_XFIELD, SF_SRM_CHAR };
|
||||
}
|
||||
|
||||
private byte[] buildDdm(int bufferSize) {
|
||||
public byte[] buildDdm() {
|
||||
return buildDdm(4096);
|
||||
}
|
||||
|
||||
public byte[] buildDdm(int bufferSize) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(8);
|
||||
out.write(0x00); // reserved
|
||||
out.write(0x00); // reserved
|
||||
@@ -423,7 +486,13 @@ public class QueryReplyBuilder {
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private byte[] buildImplicitPartition(int maxCols, int maxRows) {
|
||||
public byte[] buildImplicitPartition() {
|
||||
int maxCols = (screen != null) ? screen.getMaxCols() : MODEL_2_COLS;
|
||||
int maxRows = (screen != null) ? screen.getMaxRows() : MODEL_2_ROWS;
|
||||
return buildImplicitPartition(maxCols, maxRows);
|
||||
}
|
||||
|
||||
public byte[] buildImplicitPartition(int maxCols, int maxRows) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(22);
|
||||
// Implicit partition sizes, 2 self-defining parameters
|
||||
|
||||
@@ -447,7 +516,34 @@ public class QueryReplyBuilder {
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private byte[] buildGraphics(int maxCols, int maxRows) {
|
||||
public byte[] buildOutlining() {
|
||||
// HOD QueryReply3270Constants.java QR_OUTLINING_STRING ("\u0000\n\u0081\u008c\u0000\u0000\u0000\u0000\u0000\u0000")
|
||||
return new byte[]{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
}
|
||||
|
||||
public byte[] buildDbcsAsia() {
|
||||
// HOD QueryReply3270Constants.java QR_DBCS_ASIA_STRING ("\u0000\u000b\u0081\u0091\u0000\u0003\u0001\u0080\u0003\u0002\u0001")
|
||||
return new byte[]{ 0x00, 0x03, 0x01, (byte) 0x80, 0x03, 0x02, 0x01 };
|
||||
}
|
||||
|
||||
public byte[] buildAuxDa() {
|
||||
// HOD QueryReply3270Constants.java QR_AUXDA_STRING ("\u0000\u0006\u0081\u0099\u0000\u0000")
|
||||
return new byte[]{ 0x00, 0x00 };
|
||||
}
|
||||
|
||||
public byte[] buildTransparency() {
|
||||
// HOD QueryReply3270Constants.java QR_TRANSPARENCY_STRING ("\u0000\t\u0081\u00a8\u0002\u0000\u00f0\u00ff\u00ff")
|
||||
return new byte[]{ 0x02, 0x00, (byte) 0xF0, (byte) 0xFF, (byte) 0xFF };
|
||||
}
|
||||
|
||||
public byte[] buildSegment() {
|
||||
int maxCols = (screen != null) ? screen.getMaxCols() : MODEL_2_COLS;
|
||||
int maxRows = (screen != null) ? screen.getMaxRows() : MODEL_2_ROWS;
|
||||
return buildSegment(maxCols, maxRows);
|
||||
}
|
||||
|
||||
public byte[] buildSegment(int maxCols, int maxRows) {
|
||||
// HOD QueryReply3270Constants.java QR_SEGMENT_STRING ("\u0000\u000b\u0081\u00b0\u0080\u0002\u0000\u0000\u0000\u00fc\u0000")
|
||||
return new byte[]{
|
||||
(byte) 0x80, 0x02,
|
||||
0x00, 0x00,
|
||||
@@ -456,7 +552,22 @@ public class QueryReplyBuilder {
|
||||
};
|
||||
}
|
||||
|
||||
private byte[] buildGImage(int maxCols, int maxRows) {
|
||||
public byte[] buildGraphics() {
|
||||
return buildSegment();
|
||||
}
|
||||
|
||||
public byte[] buildGraphics(int maxCols, int maxRows) {
|
||||
return buildSegment(maxCols, maxRows);
|
||||
}
|
||||
|
||||
public byte[] buildProcedure() {
|
||||
int maxCols = (screen != null) ? screen.getMaxCols() : MODEL_2_COLS;
|
||||
int maxRows = (screen != null) ? screen.getMaxRows() : MODEL_2_ROWS;
|
||||
return buildProcedure(maxCols, maxRows);
|
||||
}
|
||||
|
||||
public byte[] buildProcedure(int maxCols, int maxRows) {
|
||||
// HOD QueryReply3270Constants.java QR_PROCEDURE_STRING ("\u0000\u0015\u0081\u00b1\u0000\u0001\u0000\u0000\u0000\u00fc\u0000\u0006@\u0006@\u0006\u0001\u00ff\u00ff\u00ff\u00f0")
|
||||
return new byte[]{
|
||||
0x00, 0x01,
|
||||
0x00, 0x00,
|
||||
@@ -467,7 +578,16 @@ public class QueryReplyBuilder {
|
||||
};
|
||||
}
|
||||
|
||||
private byte[] buildAuxDev() {
|
||||
public byte[] buildGImage() {
|
||||
return buildProcedure();
|
||||
}
|
||||
|
||||
public byte[] buildGImage(int maxCols, int maxRows) {
|
||||
return buildProcedure(maxCols, maxRows);
|
||||
}
|
||||
|
||||
public byte[] buildLineType() {
|
||||
// HOD QueryReply3270Constants.java QR_LINETYPE_STRING ("\u0000\u0018\u0081\u00b2\u0000\t\u0000\u0007\u0001\u0001\u0002\u0002\u0003\u0003\u0004\u0004\u0005\u0005\u0006\u0006\u0007\u0007\b\b")
|
||||
return new byte[]{
|
||||
0x00, 0x09, 0x00, 0x07,
|
||||
0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04,
|
||||
@@ -475,38 +595,46 @@ public class QueryReplyBuilder {
|
||||
};
|
||||
}
|
||||
|
||||
private byte[] buildSaveRestore() {
|
||||
// HOD DS3270.java line 1768: 6 bytes payload
|
||||
return new byte[]{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
public byte[] buildAuxDev() {
|
||||
return buildLineType();
|
||||
}
|
||||
|
||||
private byte[] buildTransparency() {
|
||||
// HOD DS3270.java line 1782: 2 bytes payload
|
||||
return new byte[]{ 0x00, 0x00 };
|
||||
public byte[] buildAuxDevice() {
|
||||
return buildLineType();
|
||||
}
|
||||
|
||||
private byte[] buildRpqNames() {
|
||||
// HOD DS3270.java line 1798: 5 bytes payload
|
||||
return new byte[]{ 0x02, 0x00, (byte) 0xF0, (byte) 0xFF, (byte) 0xFF };
|
||||
}
|
||||
|
||||
private void appendOemFmt(ByteArrayOutputStream out) {
|
||||
// HOD DS3270.java line 1814: 4 distinct OEM format sub-fields
|
||||
appendQueryReply(out, QR_OEM_FMT, new byte[]{
|
||||
public void appendPort(ByteArrayOutputStream out) {
|
||||
// HOD QueryReply3270Constants.java QR_PORT_STRING (4 OEM format sub-fields, 64 bytes total)
|
||||
appendQueryReply(out, QR_PORT, new byte[]{
|
||||
0x00, 0x03, 0x02, (byte) 0x90, 0x09, 0x01, 0x00, 0x03, 0x02, (byte) 0x80, 0x00, 0x7F, (byte) 0xFF
|
||||
});
|
||||
appendQueryReply(out, QR_OEM_FMT, new byte[]{
|
||||
appendQueryReply(out, QR_PORT, new byte[]{
|
||||
0x00, 0x04, 0x08, 0x40, 0x07, 0x03, 0x00, 0x03, (byte) 0x80, 0x00, 0x02
|
||||
});
|
||||
appendQueryReply(out, QR_OEM_FMT, new byte[]{
|
||||
appendQueryReply(out, QR_PORT, new byte[]{
|
||||
0x00, 0x05, 0x02, (byte) 0x80, 0x09, 0x01, 0x00, 0x01, 0x02, (byte) 0x80, 0x00, 0x7F, (byte) 0xFF
|
||||
});
|
||||
appendQueryReply(out, QR_OEM_FMT, new byte[]{
|
||||
appendQueryReply(out, QR_PORT, new byte[]{
|
||||
0x00, 0x07, 0x08, 0x40, 0x07, 0x03, 0x00, 0x01, 0x00, 0x00, 0x1C
|
||||
});
|
||||
}
|
||||
|
||||
private byte[] buildGColor() {
|
||||
public void appendOemFmt(ByteArrayOutputStream out) {
|
||||
appendPort(out);
|
||||
}
|
||||
|
||||
public byte[] buildPort() {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(70);
|
||||
appendPort(out);
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
public byte[] buildOemFormat() {
|
||||
return buildPort();
|
||||
}
|
||||
|
||||
public byte[] buildGrColor() {
|
||||
// HOD QueryReply3270Constants.java QR_GRCOLOR_STRING (109 bytes total)
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(110);
|
||||
out.write(0x00); out.write(0x04); out.write(0x00); out.write(0xFF); out.write(0xFF);
|
||||
out.write(0x00); out.write(0x10); out.write(0x00); out.write(0x10);
|
||||
@@ -526,11 +654,39 @@ public class QueryReplyBuilder {
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private byte[] buildGSymbols() {
|
||||
public byte[] buildGraphicColor() {
|
||||
return buildGrColor();
|
||||
}
|
||||
|
||||
public byte[] buildGColor() {
|
||||
return buildGrColor();
|
||||
}
|
||||
|
||||
public byte[] buildGrSymbolSet() {
|
||||
int charW = getCharWidth();
|
||||
int charH = getCharHeight();
|
||||
int cgcsgid = 0x02B9;
|
||||
int cpgid = 0x0025;
|
||||
if (screen != null && screen.getTranslator() != null) {
|
||||
cgcsgid = screen.getTranslator().getCgcsgid();
|
||||
cpgid = screen.getTranslator().getCpgid();
|
||||
}
|
||||
return new byte[]{
|
||||
0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x12,
|
||||
0x01, 0x00, 0x00, (byte) 0xF0, (byte) 0xC1, (byte) 0xC1, 0x00, 0x00,
|
||||
0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
0x00, 0x00, (byte) charW, (byte) charH, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x12,
|
||||
0x01, 0x00, 0x00, (byte) 0xF0, (byte) ((cgcsgid >> 8) & 0xFF), (byte) (cgcsgid & 0xFF), 0x00, 0x00,
|
||||
(byte) charW, (byte) charH, 0x00, 0x00, (byte) 0xF0, (byte) ((cgcsgid >> 8) & 0xFF), (byte) (cgcsgid & 0xFF), 0x00, 0x00
|
||||
};
|
||||
}
|
||||
|
||||
public byte[] buildGSymbols() {
|
||||
return buildGrSymbolSet();
|
||||
}
|
||||
|
||||
public byte[] buildSaveRestore() {
|
||||
return buildOutlining();
|
||||
}
|
||||
|
||||
public byte[] buildRpqNames() {
|
||||
return buildTransparency();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
import haus.nightmare.lib3270j.Telnet3270Client;
|
||||
|
||||
/**
|
||||
* IBM Host On-Demand ECLConnection drop-in compatibility class.
|
||||
*/
|
||||
public class ECLConnection extends haus.nightmare.lib3270j.ecl.ECLConnection {
|
||||
|
||||
public ECLConnection() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ECLConnection(haus.nightmare.lib3270j.ecl.ECLSession session) {
|
||||
super((haus.nightmare.lib3270j.ecl.ECLSession) session, session != null ? session.getClient() : null);
|
||||
}
|
||||
|
||||
public ECLConnection(String host, int port) {
|
||||
super(host, port);
|
||||
}
|
||||
|
||||
public ECLConnection(java.util.Properties props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
public ECLConnection(haus.nightmare.lib3270j.ecl.ECLSession session, Telnet3270Client client) {
|
||||
super(session, client);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
/**
|
||||
* IBM Host On-Demand ECLConstants compatibility interface.
|
||||
*/
|
||||
public interface ECLConstants extends haus.nightmare.lib3270j.ecl.ECLConstants {
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
/**
|
||||
* Drop-in IBM Host On-Demand compatible facade for ECLErr.
|
||||
*/
|
||||
public class ECLErr extends haus.nightmare.lib3270j.ecl.ECLErr {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ECLErr() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ECLErr(String text) {
|
||||
super(text);
|
||||
}
|
||||
|
||||
public ECLErr(String tag, String id, String text) {
|
||||
super(tag, id, text);
|
||||
}
|
||||
|
||||
public ECLErr(String tag, String id, String text, String extra) {
|
||||
super(tag, id, text, extra);
|
||||
}
|
||||
|
||||
public ECLErr(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ECLErr(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
/**
|
||||
* IBM Host On-Demand ECLErrors compatibility interface.
|
||||
*/
|
||||
public interface ECLErrors extends haus.nightmare.lib3270j.ecl.ECLErrors {
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
/**
|
||||
* IBM Host On-Demand ECLException compatibility class.
|
||||
*/
|
||||
public class ECLException extends haus.nightmare.lib3270j.ecl.ECLException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ECLException(int errorCode, String message) {
|
||||
super(errorCode, message);
|
||||
}
|
||||
|
||||
public ECLException(int errorCode, String message, Throwable cause) {
|
||||
super(errorCode, message, cause);
|
||||
}
|
||||
|
||||
public ECLException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
/**
|
||||
* IBM Host On-Demand ECLField drop-in compatibility class.
|
||||
*/
|
||||
public class ECLField extends haus.nightmare.lib3270j.ecl.ECLField {
|
||||
|
||||
public ECLField(haus.nightmare.lib3270j.ecl.ECLPS ps, int startPos, int dataStart, int endPos, int length, byte attribute) {
|
||||
super(ps, startPos, dataStart, endPos, length, attribute);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
|
||||
/**
|
||||
* IBM Host On-Demand ECLFieldList drop-in compatibility class.
|
||||
*/
|
||||
public class ECLFieldList extends haus.nightmare.lib3270j.ecl.ECLFieldList {
|
||||
|
||||
public ECLFieldList(haus.nightmare.lib3270j.ecl.ECLPS ps, ScreenBuffer screen) {
|
||||
super(ps, screen);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
import haus.nightmare.lib3270j.input.InputProcessor;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
import haus.nightmare.lib3270j.telnet.TelnetFSM;
|
||||
|
||||
/**
|
||||
* IBM Host On-Demand ECLOIA drop-in compatibility class.
|
||||
*/
|
||||
public class ECLOIA extends haus.nightmare.lib3270j.ecl.ECLOIA {
|
||||
|
||||
public ECLOIA(ScreenBuffer screen, InputProcessor inputProcessor, TelnetFSM fsm) {
|
||||
super(screen, inputProcessor, fsm);
|
||||
}
|
||||
|
||||
public ECLOIA(haus.nightmare.lib3270j.eNetwork.ECL.ECLSession session) {
|
||||
super(session);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
/**
|
||||
* Drop-in IBM Host On-Demand compatible facade for ECLOIANotify.
|
||||
*/
|
||||
public interface ECLOIANotify extends haus.nightmare.lib3270j.ecl.ECLOIANotify {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import haus.nightmare.lib3270j.input.InputProcessor;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
|
||||
/**
|
||||
* IBM Host On-Demand ECLPS drop-in compatibility class.
|
||||
*/
|
||||
public class ECLPS extends haus.nightmare.lib3270j.ecl.ECLPS {
|
||||
|
||||
public ECLPS(ScreenBuffer screen, InputProcessor inputProcessor, EbcdicTranslator translator) {
|
||||
super(screen, inputProcessor, translator);
|
||||
}
|
||||
|
||||
public ECLPS(haus.nightmare.lib3270j.eNetwork.ECL.ECLSession session) {
|
||||
super(session);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
/**
|
||||
* Drop-in IBM Host On-Demand compatible facade for ECLPSBIDIServices.
|
||||
*/
|
||||
public interface ECLPSBIDIServices extends haus.nightmare.lib3270j.ecl.ECLPSBIDIServices {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
/**
|
||||
* Drop-in IBM Host On-Demand compatible facade for ECLPSGraphicsServices.
|
||||
*/
|
||||
public interface ECLPSGraphicsServices extends haus.nightmare.lib3270j.ecl.ECLPSGraphicsServices {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
/**
|
||||
* Drop-in IBM Host On-Demand compatible facade for ECLPSHindiServices.
|
||||
*/
|
||||
public interface ECLPSHindiServices extends haus.nightmare.lib3270j.ecl.ECLPSHindiServices {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
/**
|
||||
* Drop-in IBM Host On-Demand compatible facade for ECLPSTHAIServices.
|
||||
*/
|
||||
public interface ECLPSTHAIServices extends haus.nightmare.lib3270j.ecl.ECLPSTHAIServices {
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
/**
|
||||
* Drop-in IBM Host On-Demand compatible facade for ECLPSUpdate.
|
||||
*/
|
||||
public class ECLPSUpdate extends haus.nightmare.lib3270j.ecl.ECLPSUpdate {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ECLPSUpdate(haus.nightmare.lib3270j.ecl.ECLPS ps, int startRow, int startCol,
|
||||
int endRow, int endCol, int start, int end, boolean fullUpdate, String text) {
|
||||
super(ps, startRow, startCol, endRow, endCol, start, end, fullUpdate, text);
|
||||
}
|
||||
|
||||
public ECLPSUpdate(int startRow, int startCol, int endRow, int endCol, boolean fullUpdate) {
|
||||
super(startRow, startCol, endRow, endCol, fullUpdate);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
/**
|
||||
* IBM Host On-Demand ECLScreenDesc drop-in compatibility class.
|
||||
*/
|
||||
public class ECLScreenDesc extends haus.nightmare.lib3270j.ecl.ECLScreenDesc {
|
||||
|
||||
public ECLScreenDesc() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
/**
|
||||
* Drop-in IBM Host On-Demand compatible facade for ECLScreenNotify.
|
||||
*/
|
||||
public interface ECLScreenNotify extends haus.nightmare.lib3270j.ecl.ECLScreenNotify {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
/**
|
||||
* Drop-in IBM Host On-Demand compatible facade for ECLScreenReco.
|
||||
*/
|
||||
public class ECLScreenReco extends haus.nightmare.lib3270j.ecl.ECLScreenReco {
|
||||
|
||||
public ECLScreenReco() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ECLScreenReco(haus.nightmare.lib3270j.ecl.ECLSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
public ECLScreenReco(haus.nightmare.lib3270j.ecl.ECLPS ps) {
|
||||
super(ps);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
/**
|
||||
* Drop-in IBM Host On-Demand compatible facade for ECLScreenRecoEvent.
|
||||
*/
|
||||
public class ECLScreenRecoEvent extends haus.nightmare.lib3270j.ecl.ECLScreenRecoEvent {
|
||||
|
||||
public ECLScreenRecoEvent(haus.nightmare.lib3270j.ecl.ECLScreenReco source,
|
||||
haus.nightmare.lib3270j.ecl.ECLScreenDesc screenDesc,
|
||||
haus.nightmare.lib3270j.ecl.ECLPS ps) {
|
||||
super(source, screenDesc, ps);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
import haus.nightmare.lib3270j.ConnectionConfig;
|
||||
import haus.nightmare.lib3270j.Telnet3270Client;
|
||||
import haus.nightmare.lib3270j.TerminalModel;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* IBM Host On-Demand ECLSession drop-in compatibility class.
|
||||
*/
|
||||
public class ECLSession extends haus.nightmare.lib3270j.ecl.ECLSession {
|
||||
|
||||
public ECLSession() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ECLSession(ConnectionConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
public ECLSession(String host, int port, TerminalModel model) {
|
||||
super(host, port, model);
|
||||
}
|
||||
|
||||
public ECLSession(String host, int port, TerminalModel model, boolean useTls) {
|
||||
super(host, port, model, useTls);
|
||||
}
|
||||
|
||||
public ECLSession(Properties props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
public ECLSession(Telnet3270Client client) {
|
||||
super(client);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL;
|
||||
|
||||
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||
import haus.nightmare.lib3270j.datastream.DataStreamProcessor;
|
||||
import haus.nightmare.lib3270j.input.InputProcessor;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
|
||||
/**
|
||||
* IBM Host On-Demand ECLXfer drop-in compatibility class.
|
||||
*/
|
||||
public class ECLXfer extends haus.nightmare.lib3270j.ecl.ECLXfer {
|
||||
|
||||
public ECLXfer(ScreenBuffer screen, InputProcessor input, DataStreamProcessor dsProcessor, EbcdicTranslator translator) {
|
||||
super(screen, input, dsProcessor, translator);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL.bidi;
|
||||
|
||||
import haus.nightmare.lib3270j.input.InputProcessor;
|
||||
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||
import haus.nightmare.lib3270j.telnet.TelnetFSM;
|
||||
|
||||
/**
|
||||
* Drop-in IBM Host On-Demand compatible facade for ECLOIABIDI.
|
||||
*/
|
||||
public class ECLOIABIDI extends haus.nightmare.lib3270j.ecl.ECLOIABIDI {
|
||||
|
||||
public ECLOIABIDI(ScreenBuffer screen, InputProcessor inputProcessor, TelnetFSM fsm) {
|
||||
super(screen, inputProcessor, fsm);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL.event;
|
||||
|
||||
import haus.nightmare.lib3270j.ConnectionState;
|
||||
|
||||
/**
|
||||
* IBM Host On-Demand ECLCommEvent compatibility class.
|
||||
*/
|
||||
public class ECLCommEvent extends haus.nightmare.lib3270j.ecl.ECLCommEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ECLCommEvent(Object source, int eventType, ConnectionState oldState, ConnectionState newState,
|
||||
String message, String deviceType, String deviceName) {
|
||||
super(source, eventType, oldState, newState, message, deviceType, deviceName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package haus.nightmare.lib3270j.eNetwork.ECL.event;
|
||||
|
||||
/**
|
||||
* IBM Host On-Demand ECLCommListener compatibility interface.
|
||||
*/
|
||||
public interface ECLCommListener extends haus.nightmare.lib3270j.ecl.ECLCommListener {
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user