Overnight churnings
This commit is contained in:
@@ -145,12 +145,12 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
|
|
||||||
// 3. View menu
|
// 3. View menu
|
||||||
JMenu viewMenu = createMenu("View");
|
JMenu viewMenu = createMenu("View");
|
||||||
viewMenu.add(createMenuItem("Font Size +", KeyEvent.VK_EQUALS, () -> adjustFontSize(2)));
|
viewMenu.add(createMenuItem("Font Size +", KeyEvent.VK_EQUALS, () -> adjustFontSize(2), true));
|
||||||
viewMenu.add(createMenuItem("Font Size -", KeyEvent.VK_MINUS, () -> adjustFontSize(-2)));
|
viewMenu.add(createMenuItem("Font Size -", KeyEvent.VK_MINUS, () -> adjustFontSize(-2), true));
|
||||||
viewMenu.add(createMenuItem("Reset Font", KeyEvent.VK_0, () -> {
|
viewMenu.add(createMenuItem("Reset Font", KeyEvent.VK_0, () -> {
|
||||||
terminalPanel.setFontSize(16);
|
terminalPanel.setFontSize(16);
|
||||||
terminalPanel.guardedPack();
|
terminalPanel.guardedPack();
|
||||||
}));
|
}, true));
|
||||||
viewMenu.addSeparator();
|
viewMenu.addSeparator();
|
||||||
|
|
||||||
// UI Theme Submenu
|
// UI Theme Submenu
|
||||||
@@ -234,17 +234,17 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
client.sendEnter();
|
client.sendEnter();
|
||||||
terminalPanel.repaint();
|
terminalPanel.repaint();
|
||||||
}
|
}
|
||||||
}));
|
}, true));
|
||||||
actionsMenu.add(createMenuItem("Clear", KeyEvent.VK_K, () -> {
|
actionsMenu.add(createMenuItem("Clear", KeyEvent.VK_K, () -> {
|
||||||
if (client != null)
|
if (client != null)
|
||||||
client.sendClear();
|
client.sendClear();
|
||||||
terminalPanel.repaint();
|
terminalPanel.repaint();
|
||||||
}));
|
}, true));
|
||||||
actionsMenu.add(createMenuItem("Reset", KeyEvent.VK_R, () -> {
|
actionsMenu.add(createMenuItem("Reset", KeyEvent.VK_R, () -> {
|
||||||
if (client != null)
|
if (client != null)
|
||||||
client.reset();
|
client.reset();
|
||||||
terminalPanel.repaint();
|
terminalPanel.repaint();
|
||||||
}));
|
}, true));
|
||||||
actionsMenu.add(createMenuItem("Erase Input", KeyEvent.VK_E, () -> {
|
actionsMenu.add(createMenuItem("Erase Input", KeyEvent.VK_E, () -> {
|
||||||
if (client != null && client.getConnectionState().isFullSession()) {
|
if (client != null && client.getConnectionState().isFullSession()) {
|
||||||
client.eraseInput();
|
client.eraseInput();
|
||||||
@@ -272,7 +272,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
}, true));
|
}, true));
|
||||||
actionsMenu.addSeparator();
|
actionsMenu.addSeparator();
|
||||||
actionsMenu.add(createMenuItem("Run Keystrokes / Script...", -1, this::showScriptDialog));
|
actionsMenu.add(createMenuItem("Run Keystrokes / Script...", -1, this::showScriptDialog));
|
||||||
actionsMenu.add(createMenuItem("File Transfer...", KeyEvent.VK_T, this::showFileTransferDialog));
|
actionsMenu.add(createMenuItem("File Transfer...", KeyEvent.VK_T, this::showFileTransferDialog, true));
|
||||||
menuBar.add(actionsMenu);
|
menuBar.add(actionsMenu);
|
||||||
|
|
||||||
// 5. Help menu
|
// 5. Help menu
|
||||||
@@ -706,18 +706,18 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
"Insert — Toggle insert mode\n" +
|
"Insert — Toggle insert mode\n" +
|
||||||
"Escape — Reset\n" +
|
"Escape — Reset\n" +
|
||||||
"PageUp/Down — PF7/PF8\n" +
|
"PageUp/Down — PF7/PF8\n" +
|
||||||
"Alt+C / Ctrl+K — Clear\n" +
|
"Alt+C / Alt+K — Clear\n" +
|
||||||
"Alt+E — Erase Input\n" +
|
"Alt+E — Erase Input\n" +
|
||||||
"Alt+A — Attention\n" +
|
"Alt+A — Attention\n" +
|
||||||
"Alt+S — System Request\n" +
|
"Alt+S — System Request\n" +
|
||||||
"Alt+Q — Cursor Select\n" +
|
"Alt+Q — Cursor Select\n" +
|
||||||
"Alt+L — Toggle Light Pen\n" +
|
"Alt+L — Toggle Light Pen\n" +
|
||||||
|
"Alt+T — File Transfer\n" +
|
||||||
|
"Alt+=/-/0 — Font size +/-/reset\n" +
|
||||||
"Cmd/Ctrl+F — Find on Screen\n" +
|
"Cmd/Ctrl+F — Find on Screen\n" +
|
||||||
"Cmd/Ctrl+G / F3— Find Next\n" +
|
"Cmd/Ctrl+G / F3— Find Next\n" +
|
||||||
"Cmd/Ctrl+T — File Transfer\n" +
|
|
||||||
"Cmd/Ctrl+D — Disconnect\n" +
|
"Cmd/Ctrl+D — Disconnect\n" +
|
||||||
"Cmd/Ctrl+Q — Quit\n" +
|
"Cmd/Ctrl+Q — Quit";
|
||||||
"Cmd/Ctrl+=/-/0 — Font size +/-/reset";
|
|
||||||
|
|
||||||
JTextArea area = new JTextArea(text);
|
JTextArea area = new JTextArea(text);
|
||||||
area.setEditable(false);
|
area.setEditable(false);
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package haus.nightmare.j3270.ui;
|
||||||
|
|
||||||
|
import haus.nightmare.j3270.J3270App;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
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 = new J3270App();
|
||||||
|
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 use ALT_DOWN_MASK
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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.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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,6 +40,8 @@ public interface ECLConstants {
|
|||||||
// Search directions
|
// Search directions
|
||||||
int SEARCH_FORWARD = 1;
|
int SEARCH_FORWARD = 1;
|
||||||
int SEARCH_BACKWARD = 2;
|
int SEARCH_BACKWARD = 2;
|
||||||
|
int DIR_FORWARD = 1;
|
||||||
|
int DIR_BACKWARD = 2;
|
||||||
|
|
||||||
// OIA Input Inhibited Reason Codes
|
// OIA Input Inhibited Reason Codes
|
||||||
int INHIBIT_NOT_INHIBITED = 0;
|
int INHIBIT_NOT_INHIBITED = 0;
|
||||||
|
|||||||
@@ -28,35 +28,43 @@ public class ECLField {
|
|||||||
|
|
||||||
/** Buffer address of the field attribute character. */
|
/** Buffer address of the field attribute character. */
|
||||||
public int getStart() { return startPos; }
|
public int getStart() { return startPos; }
|
||||||
|
public int GetStart() { return getStart(); }
|
||||||
|
|
||||||
/** First buffer address of the field data (start + 1). */
|
/** First buffer address of the field data (start + 1). */
|
||||||
public int getDataStart() { return dataStart; }
|
public int getDataStart() { return dataStart; }
|
||||||
|
public int GetDataStart() { return getDataStart(); }
|
||||||
|
|
||||||
/** Last buffer address of the field data inclusive. */
|
/** Last buffer address of the field data inclusive. */
|
||||||
public int getEnd() { return endPos; }
|
public int getEnd() { return endPos; }
|
||||||
|
public int GetEnd() { return getEnd(); }
|
||||||
|
|
||||||
/** Number of data characters in the field. */
|
/** Number of data characters in the field. */
|
||||||
public int getLength() { return length; }
|
public int getLength() { return length; }
|
||||||
|
public int GetLength() { return getLength(); }
|
||||||
|
|
||||||
public int getStartRow() {
|
public int getStartRow() {
|
||||||
int cols = ps.getCols();
|
int cols = ps.getCols();
|
||||||
return cols > 0 ? startPos / cols : 0;
|
return cols > 0 ? startPos / cols : 0;
|
||||||
}
|
}
|
||||||
|
public int GetStartRow() { return getStartRow(); }
|
||||||
|
|
||||||
public int getStartCol() {
|
public int getStartCol() {
|
||||||
int cols = ps.getCols();
|
int cols = ps.getCols();
|
||||||
return cols > 0 ? startPos % cols : 0;
|
return cols > 0 ? startPos % cols : 0;
|
||||||
}
|
}
|
||||||
|
public int GetStartCol() { return getStartCol(); }
|
||||||
|
|
||||||
public int getEndRow() {
|
public int getEndRow() {
|
||||||
int cols = ps.getCols();
|
int cols = ps.getCols();
|
||||||
return cols > 0 ? endPos / cols : 0;
|
return cols > 0 ? endPos / cols : 0;
|
||||||
}
|
}
|
||||||
|
public int GetEndRow() { return getEndRow(); }
|
||||||
|
|
||||||
public int getEndCol() {
|
public int getEndCol() {
|
||||||
int cols = ps.getCols();
|
int cols = ps.getCols();
|
||||||
return cols > 0 ? endPos % cols : 0;
|
return cols > 0 ? endPos % cols : 0;
|
||||||
}
|
}
|
||||||
|
public int GetEndCol() { return getEndCol(); }
|
||||||
|
|
||||||
private byte getLiveAttribute() {
|
private byte getLiveAttribute() {
|
||||||
if (ps != null && ps.getScreenBuffer() != null) {
|
if (ps != null && ps.getScreenBuffer() != null) {
|
||||||
@@ -71,34 +79,42 @@ public class ECLField {
|
|||||||
public boolean isModified() {
|
public boolean isModified() {
|
||||||
return faIsModified(getLiveAttribute() & 0xFF);
|
return faIsModified(getLiveAttribute() & 0xFF);
|
||||||
}
|
}
|
||||||
|
public boolean IsModified() { return isModified(); }
|
||||||
|
|
||||||
public boolean isProtected() {
|
public boolean isProtected() {
|
||||||
return faIsProtected(getLiveAttribute() & 0xFF);
|
return faIsProtected(getLiveAttribute() & 0xFF);
|
||||||
}
|
}
|
||||||
|
public boolean IsProtected() { return isProtected(); }
|
||||||
|
|
||||||
public boolean isNumeric() {
|
public boolean isNumeric() {
|
||||||
return faIsNumeric(getLiveAttribute() & 0xFF);
|
return faIsNumeric(getLiveAttribute() & 0xFF);
|
||||||
}
|
}
|
||||||
|
public boolean IsNumeric() { return isNumeric(); }
|
||||||
|
|
||||||
public boolean isHighIntensity() {
|
public boolean isHighIntensity() {
|
||||||
return faIsHigh(getLiveAttribute() & 0xFF);
|
return faIsHigh(getLiveAttribute() & 0xFF);
|
||||||
}
|
}
|
||||||
|
public boolean IsHighIntensity() { return isHighIntensity(); }
|
||||||
|
|
||||||
public boolean isHidden() {
|
public boolean isHidden() {
|
||||||
return faIsZero(getLiveAttribute() & 0xFF);
|
return faIsZero(getLiveAttribute() & 0xFF);
|
||||||
}
|
}
|
||||||
|
public boolean IsHidden() { return isHidden(); }
|
||||||
|
|
||||||
public boolean isDisplay() {
|
public boolean isDisplay() {
|
||||||
return !isHidden();
|
return !isHidden();
|
||||||
}
|
}
|
||||||
|
public boolean IsDisplay() { return isDisplay(); }
|
||||||
|
|
||||||
public boolean isPenSelectable() {
|
public boolean isPenSelectable() {
|
||||||
return faIsSelectable(getLiveAttribute() & 0xFF);
|
return faIsSelectable(getLiveAttribute() & 0xFF);
|
||||||
}
|
}
|
||||||
|
public boolean IsPenSelectable() { return isPenSelectable(); }
|
||||||
|
|
||||||
public short getAttribute() {
|
public short getAttribute() {
|
||||||
return (short) (getLiveAttribute() & 0xFF);
|
return (short) (getLiveAttribute() & 0xFF);
|
||||||
}
|
}
|
||||||
|
public short GetAttribute() { return getAttribute(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the text contents of this field as a String.
|
* Get the text contents of this field as a String.
|
||||||
@@ -107,6 +123,7 @@ public class ECLField {
|
|||||||
if (length <= 0) return "";
|
if (length <= 0) return "";
|
||||||
return ps.getString(dataStart, length);
|
return ps.getString(dataStart, length);
|
||||||
}
|
}
|
||||||
|
public String GetText() { return getText(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the text contents of this field.
|
* Set the text contents of this field.
|
||||||
@@ -115,6 +132,7 @@ public class ECLField {
|
|||||||
if (isProtected() || length <= 0) return;
|
if (isProtected() || length <= 0) return;
|
||||||
ps.setText(text, dataStart);
|
ps.setText(text, dataStart);
|
||||||
}
|
}
|
||||||
|
public void SetText(String text) { setText(text); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get selector light pen type.
|
* Get selector light pen type.
|
||||||
@@ -129,6 +147,7 @@ public class ECLField {
|
|||||||
}
|
}
|
||||||
return ' ';
|
return ' ';
|
||||||
}
|
}
|
||||||
|
public char GetSelectorPenType() { return getSelectorPenType(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actuate lightpen selection on this field ('?' -> '>').
|
* Actuate lightpen selection on this field ('?' -> '>').
|
||||||
@@ -140,6 +159,7 @@ public class ECLField {
|
|||||||
setText(">" + (t.length() > 1 ? t.substring(1) : ""));
|
setText(">" + (t.length() > 1 ? t.substring(1) : ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public void SelectField() { selectField(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deselect lightpen selection on this field ('>' -> '?').
|
* Deselect lightpen selection on this field ('>' -> '?').
|
||||||
@@ -151,16 +171,19 @@ public class ECLField {
|
|||||||
setText("?" + (t.length() > 1 ? t.substring(1) : ""));
|
setText("?" + (t.length() > 1 ? t.substring(1) : ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public void DeSelectField() { deSelectField(); }
|
||||||
|
|
||||||
/** Return true if this field wraps from bottom of screen to top. */
|
/** Return true if this field wraps from bottom of screen to top. */
|
||||||
public boolean isWrapped() {
|
public boolean isWrapped() {
|
||||||
return startPos > endPos;
|
return startPos > endPos;
|
||||||
}
|
}
|
||||||
|
public boolean IsWrapped() { return isWrapped(); }
|
||||||
|
|
||||||
/** Last data buffer address (same as getEnd). */
|
/** Last data buffer address (same as getEnd). */
|
||||||
public int getDataEnd() {
|
public int getDataEnd() {
|
||||||
return endPos;
|
return endPos;
|
||||||
}
|
}
|
||||||
|
public int GetDataEnd() { return getDataEnd(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the specified buffer address is contained within this field (including its FA).
|
* Check if the specified buffer address is contained within this field (including its FA).
|
||||||
@@ -178,6 +201,7 @@ public class ECLField {
|
|||||||
return pos >= startPos || pos <= endPos;
|
return pos >= startPos || pos <= endPos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public boolean Contains(int pos) { return contains(pos); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the specified row and column is contained within this field.
|
* Check if the specified row and column is contained within this field.
|
||||||
@@ -187,6 +211,7 @@ public class ECLField {
|
|||||||
int cols = ps.getCols();
|
int cols = ps.getCols();
|
||||||
return contains(row * cols + col);
|
return contains(row * cols + col);
|
||||||
}
|
}
|
||||||
|
public boolean Contains(int row, int col) { return contains(row, col); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the Modified Data Tag (MDT) for this field.
|
* Set the Modified Data Tag (MDT) for this field.
|
||||||
@@ -205,6 +230,7 @@ public class ECLField {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public void SetModified(boolean modified) { setModified(modified); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erase all character data within this field to nulls.
|
* Erase all character data within this field to nulls.
|
||||||
@@ -225,6 +251,20 @@ public class ECLField {
|
|||||||
sb.markAllChanged();
|
sb.markAllChanged();
|
||||||
sb.updateDisplaySnapshot();
|
sb.updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
public void Erase() { erase(); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof ECLField)) return false;
|
||||||
|
ECLField other = (ECLField) o;
|
||||||
|
return this.startPos == other.startPos && this.endPos == other.endPos && this.length == other.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return java.util.Objects.hash(startPos, endPos, length);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|||||||
@@ -70,27 +70,38 @@ public class ECLFieldList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Refresh() { refresh(); }
|
||||||
|
|
||||||
public synchronized int getFieldCount() {
|
public synchronized int getFieldCount() {
|
||||||
return fields.size();
|
return fields.size();
|
||||||
}
|
}
|
||||||
|
public int GetFieldCount() { return getFieldCount(); }
|
||||||
|
|
||||||
public synchronized List<ECLField> getFields() {
|
public synchronized List<ECLField> getFields() {
|
||||||
return Collections.unmodifiableList(new ArrayList<>(fields));
|
return Collections.unmodifiableList(new ArrayList<>(fields));
|
||||||
}
|
}
|
||||||
|
public List<ECLField> GetFields() { return getFields(); }
|
||||||
|
|
||||||
public synchronized ECLField getFirstField() {
|
public synchronized ECLField getFirstField() {
|
||||||
if (fields.isEmpty()) return null;
|
if (fields.isEmpty()) return null;
|
||||||
return fields.get(0);
|
return fields.get(0);
|
||||||
}
|
}
|
||||||
|
public ECLField GetFirstField() { return getFirstField(); }
|
||||||
|
|
||||||
public synchronized ECLField getNextField(ECLField prev) {
|
public synchronized ECLField getNextField(ECLField prev) {
|
||||||
if (prev == null || fields.isEmpty()) return getFirstField();
|
if (prev == null || fields.isEmpty()) return getFirstField();
|
||||||
int idx = fields.indexOf(prev);
|
for (int i = 0; i < fields.size(); i++) {
|
||||||
if (idx >= 0 && idx + 1 < fields.size()) {
|
ECLField f = fields.get(i);
|
||||||
return fields.get(idx + 1);
|
if (f.equals(prev) || f.getStart() == prev.getStart()) {
|
||||||
|
if (i + 1 < fields.size()) {
|
||||||
|
return fields.get(i + 1);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public ECLField GetNextField(ECLField prev) { return getNextField(prev); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the field that contains the specified buffer position.
|
* Find the field that contains the specified buffer position.
|
||||||
@@ -113,6 +124,7 @@ public class ECLFieldList {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
public ECLField FindField(int pos) { return findField(pos); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the field at the specified 0-indexed row and column.
|
* Find the field at the specified 0-indexed row and column.
|
||||||
@@ -122,20 +134,26 @@ public class ECLFieldList {
|
|||||||
int cols = screen.getCols();
|
int cols = screen.getCols();
|
||||||
return findField(row * cols + col);
|
return findField(row * cols + col);
|
||||||
}
|
}
|
||||||
|
public ECLField FindField(int row, int col) { return findField(row, col); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the field preceding the given field in the field list.
|
* Get the field preceding the given field in the field list.
|
||||||
*/
|
*/
|
||||||
public synchronized ECLField getPreviousField(ECLField next) {
|
public synchronized ECLField getPreviousField(ECLField next) {
|
||||||
if (next == null || fields.isEmpty()) return null;
|
if (next == null || fields.isEmpty()) return null;
|
||||||
int idx = fields.indexOf(next);
|
for (int i = 0; i < fields.size(); i++) {
|
||||||
if (idx > 0) {
|
ECLField f = fields.get(i);
|
||||||
return fields.get(idx - 1);
|
if (f.equals(next) || f.getStart() == next.getStart()) {
|
||||||
} else if (idx == 0) {
|
if (i > 0) {
|
||||||
|
return fields.get(i - 1);
|
||||||
|
} else {
|
||||||
return fields.get(fields.size() - 1);
|
return fields.get(fields.size() - 1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
public ECLField GetPreviousField(ECLField next) { return getPreviousField(next); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the field at the given buffer position (alias for findField).
|
* Find the field at the given buffer position (alias for findField).
|
||||||
@@ -143,6 +161,7 @@ public class ECLFieldList {
|
|||||||
public ECLField findFieldAt(int pos) {
|
public ECLField findFieldAt(int pos) {
|
||||||
return findField(pos);
|
return findField(pos);
|
||||||
}
|
}
|
||||||
|
public ECLField FindFieldAt(int pos) { return findFieldAt(pos); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the field at the given row and column.
|
* Find the field at the given row and column.
|
||||||
@@ -150,6 +169,7 @@ public class ECLFieldList {
|
|||||||
public ECLField findFieldAt(int row, int col) {
|
public ECLField findFieldAt(int row, int col) {
|
||||||
return findField(row, col);
|
return findField(row, col);
|
||||||
}
|
}
|
||||||
|
public ECLField FindFieldAt(int row, int col) { return findFieldAt(row, col); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the field preceding the one at the given buffer position.
|
* Find the field preceding the one at the given buffer position.
|
||||||
@@ -159,6 +179,7 @@ public class ECLFieldList {
|
|||||||
if (curr == null) return null;
|
if (curr == null) return null;
|
||||||
return getPreviousField(curr);
|
return getPreviousField(curr);
|
||||||
}
|
}
|
||||||
|
public ECLField FindPrevField(int pos) { return findPrevField(pos); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the field succeeding the one at the given buffer position.
|
* Find the field succeeding the one at the given buffer position.
|
||||||
@@ -168,6 +189,7 @@ public class ECLFieldList {
|
|||||||
if (curr == null) return null;
|
if (curr == null) return null;
|
||||||
return getNextField(curr);
|
return getNextField(curr);
|
||||||
}
|
}
|
||||||
|
public ECLField FindNextField(int pos) { return findNextField(pos); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find field containing the given text string.
|
* Find field containing the given text string.
|
||||||
@@ -202,4 +224,5 @@ public class ECLFieldList {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
public ECLField FindField(String text, int startPos) { return findField(text, startPos); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,24 +85,52 @@ public class ECLOIA implements ECLConstants {
|
|||||||
return inputProcessor != null && inputProcessor.isInsertMode();
|
return inputProcessor != null && inputProcessor.isInsertMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean IsInsertMode() {
|
||||||
|
return isInsertMode();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isNumeric() {
|
public boolean isNumeric() {
|
||||||
if (screen == null || !screen.isFormatted()) return false;
|
if (screen == null || !screen.isFormatted()) return false;
|
||||||
byte fa = screen.getFieldAttributeAt(screen.getCursorAddress());
|
byte fa = screen.getFieldAttributeAt(screen.getCursorAddress());
|
||||||
return faIsNumeric(fa & 0xFF);
|
return faIsNumeric(fa & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean IsNumeric() {
|
||||||
|
return isNumeric();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isAlphanumeric() {
|
public boolean isAlphanumeric() {
|
||||||
return !isNumeric();
|
return !isNumeric();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean IsAlphanumeric() {
|
||||||
|
return isAlphanumeric();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDBCS() {
|
||||||
|
return getAlphanumericType() == TYPE_DBCS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean IsDBCS() {
|
||||||
|
return isDBCS();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isMessageWaiting() {
|
public boolean isMessageWaiting() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean IsMessageWaiting() {
|
||||||
|
return isMessageWaiting();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isCommError() {
|
public boolean isCommError() {
|
||||||
return fsm != null && fsm.getConnectionState() != null && !fsm.getConnectionState().isConnected();
|
return fsm != null && fsm.getConnectionState() != null && !fsm.getConnectionState().isConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean IsCommError() {
|
||||||
|
return isCommError();
|
||||||
|
}
|
||||||
|
|
||||||
private int inhibitOverride = -1;
|
private int inhibitOverride = -1;
|
||||||
|
|
||||||
public void setInputInhibited(int reason) {
|
public void setInputInhibited(int reason) {
|
||||||
@@ -112,6 +140,10 @@ public class ECLOIA implements ECLConstants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetInputInhibited(int reason) {
|
||||||
|
setInputInhibited(reason);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the alphanumeric character entry type allowed at current cursor position.
|
* Get the alphanumeric character entry type allowed at current cursor position.
|
||||||
* Returns TYPE_ALPHANUMERIC (0), TYPE_NUMERIC (1), or TYPE_DBCS (2).
|
* Returns TYPE_ALPHANUMERIC (0), TYPE_NUMERIC (1), or TYPE_DBCS (2).
|
||||||
@@ -132,6 +164,10 @@ public class ECLOIA implements ECLConstants {
|
|||||||
return TYPE_ALPHANUMERIC;
|
return TYPE_ALPHANUMERIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int GetAlphanumericType() {
|
||||||
|
return getAlphanumericType();
|
||||||
|
}
|
||||||
|
|
||||||
public String getAlphanumericTypeString() {
|
public String getAlphanumericTypeString() {
|
||||||
switch (getAlphanumericType()) {
|
switch (getAlphanumericType()) {
|
||||||
case TYPE_NUMERIC: return "N";
|
case TYPE_NUMERIC: return "N";
|
||||||
@@ -141,38 +177,74 @@ public class ECLOIA implements ECLConstants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String GetAlphanumericTypeString() {
|
||||||
|
return getAlphanumericTypeString();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isXSystem() {
|
public boolean isXSystem() {
|
||||||
return getInputInhibited() == INHIBIT_SYSTEM_LOCK;
|
return getInputInhibited() == INHIBIT_SYSTEM_LOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean IsXSystem() {
|
||||||
|
return isXSystem();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isXProt() {
|
public boolean isXProt() {
|
||||||
return getInputInhibited() == INHIBIT_PROTECTED_FIELD;
|
return getInputInhibited() == INHIBIT_PROTECTED_FIELD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean IsXProt() {
|
||||||
|
return isXProt();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isXNum() {
|
public boolean isXNum() {
|
||||||
return getInputInhibited() == INHIBIT_NUMERIC_ONLY;
|
return getInputInhibited() == INHIBIT_NUMERIC_ONLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean IsXNum() {
|
||||||
|
return isXNum();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isXWait() {
|
public boolean isXWait() {
|
||||||
return isXSystem();
|
return isXSystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean IsXWait() {
|
||||||
|
return isXWait();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isXInsert() {
|
public boolean isXInsert() {
|
||||||
return isInsertMode();
|
return isInsertMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean IsXInsert() {
|
||||||
|
return isXInsert();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isXComm() {
|
public boolean isXComm() {
|
||||||
return isCommError() || getInputInhibited() == INHIBIT_COMM_CHECK;
|
return isCommError() || getInputInhibited() == INHIBIT_COMM_CHECK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean IsXComm() {
|
||||||
|
return isXComm();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isXOverflow() {
|
public boolean isXOverflow() {
|
||||||
return getInputInhibited() == INHIBIT_OVERFLOW;
|
return getInputInhibited() == INHIBIT_OVERFLOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean IsXOverflow() {
|
||||||
|
return isXOverflow();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isXOperatorDue() {
|
public boolean isXOperatorDue() {
|
||||||
return getInputInhibited() == INHIBIT_OPERATOR_DUE;
|
return getInputInhibited() == INHIBIT_OPERATOR_DUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean IsXOperatorDue() {
|
||||||
|
return isXOperatorDue();
|
||||||
|
}
|
||||||
|
|
||||||
public String getStatusString() {
|
public String getStatusString() {
|
||||||
int inhibit = getInputInhibited();
|
int inhibit = getInputInhibited();
|
||||||
switch (inhibit) {
|
switch (inhibit) {
|
||||||
@@ -188,6 +260,10 @@ public class ECLOIA implements ECLConstants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String GetStatusString() {
|
||||||
|
return getStatusString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current Input Inhibited code.
|
* Get the current Input Inhibited code.
|
||||||
* Returns one of INHIBIT_* constants from ECLConstants.
|
* Returns one of INHIBIT_* constants from ECLConstants.
|
||||||
@@ -205,6 +281,18 @@ public class ECLOIA implements ECLConstants {
|
|||||||
return INHIBIT_NOT_INHIBITED;
|
return INHIBIT_NOT_INHIBITED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int GetInputInhibited() {
|
||||||
|
return getInputInhibited();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInputInhibitedType() {
|
||||||
|
return getInputInhibited();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetInputInhibitedType() {
|
||||||
|
return getInputInhibited();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Block until the keyboard becomes unlocked and ready for user input, or timeout expires.
|
* Block until the keyboard becomes unlocked and ready for user input, or timeout expires.
|
||||||
* @return true if keyboard unlocked, false if timeout occurred.
|
* @return true if keyboard unlocked, false if timeout occurred.
|
||||||
|
|||||||
@@ -205,21 +205,75 @@ public class ECLPS implements ECLConstants {
|
|||||||
return SearchPSExt(text, 1, getSize(), SEARCH_FORWARD, false, true);
|
return SearchPSExt(text, 1, getSize(), SEARCH_FORWARD, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int searchPS(String text) {
|
||||||
|
return SearchPS(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SearchPS(String text, int startPos) {
|
||||||
|
return SearchPSExt(text, startPos, getSize(), SEARCH_FORWARD, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int searchPS(String text, int startPos) {
|
||||||
|
return SearchPS(text, startPos);
|
||||||
|
}
|
||||||
|
|
||||||
public int SearchPS(String text, int startRow, int startCol) {
|
public int SearchPS(String text, int startRow, int startCol) {
|
||||||
int pos = (startRow - 1) * getCols() + startCol;
|
int pos = (startRow - 1) * getCols() + startCol;
|
||||||
return SearchPSExt(text, pos, getSize(), SEARCH_FORWARD, false, true);
|
return SearchPSExt(text, pos, getSize(), SEARCH_FORWARD, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int searchPS(String text, int startRow, int startCol) {
|
||||||
|
return SearchPS(text, startRow, startCol);
|
||||||
|
}
|
||||||
|
|
||||||
public int SearchPS(String text, int startRow, int startCol, int dir) {
|
public int SearchPS(String text, int startRow, int startCol, int dir) {
|
||||||
int pos = (startRow - 1) * getCols() + startCol;
|
int pos = (startRow - 1) * getCols() + startCol;
|
||||||
return SearchPSExt(text, pos, getSize(), dir, false, true);
|
return SearchPSExt(text, pos, getSize(), dir, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int searchPS(String text, int startRow, int startCol, int dir) {
|
||||||
|
return SearchPS(text, startRow, startCol, dir);
|
||||||
|
}
|
||||||
|
|
||||||
public int SearchPS(String text, int startRow, int startCol, int dir, boolean ignoreCase) {
|
public int SearchPS(String text, int startRow, int startCol, int dir, boolean ignoreCase) {
|
||||||
int pos = (startRow - 1) * getCols() + startCol;
|
int pos = (startRow - 1) * getCols() + startCol;
|
||||||
return SearchPSExt(text, pos, getSize(), dir, ignoreCase, true);
|
return SearchPSExt(text, pos, getSize(), dir, ignoreCase, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int searchPS(String text, int startRow, int startCol, int dir, boolean ignoreCase) {
|
||||||
|
return SearchPS(text, startRow, startCol, dir, ignoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SearchPS(String text, int startRow, int startCol, int endRow, int endCol) {
|
||||||
|
int sPos = (startRow - 1) * getCols() + startCol;
|
||||||
|
int ePos = (endRow - 1) * getCols() + endCol;
|
||||||
|
return SearchPSExt(text, sPos, ePos, SEARCH_FORWARD, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int searchPS(String text, int startRow, int startCol, int endRow, int endCol) {
|
||||||
|
return SearchPS(text, startRow, startCol, endRow, endCol);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SearchPS(String text, int startRow, int startCol, int endRow, int endCol, int dir) {
|
||||||
|
int sPos = (startRow - 1) * getCols() + startCol;
|
||||||
|
int ePos = (endRow - 1) * getCols() + endCol;
|
||||||
|
return SearchPSExt(text, sPos, ePos, dir, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int searchPS(String text, int startRow, int startCol, int endRow, int endCol, int dir) {
|
||||||
|
return SearchPS(text, startRow, startCol, endRow, endCol, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SearchPS(String text, int startRow, int startCol, int endRow, int endCol, int dir, boolean ignoreCase) {
|
||||||
|
int sPos = (startRow - 1) * getCols() + startCol;
|
||||||
|
int ePos = (endRow - 1) * getCols() + endCol;
|
||||||
|
return SearchPSExt(text, sPos, ePos, dir, ignoreCase, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int searchPS(String text, int startRow, int startCol, int endRow, int endCol, int dir, boolean ignoreCase) {
|
||||||
|
return SearchPS(text, startRow, startCol, endRow, endCol, dir, ignoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SearchPS extended method conforming to IBM ECL specification.
|
* SearchPS extended method conforming to IBM ECL specification.
|
||||||
* Uses 1-based positions and returns 1-based index (or 0 if not found).
|
* Uses 1-based positions and returns 1-based index (or 0 if not found).
|
||||||
@@ -263,6 +317,21 @@ public class ECLPS implements ECLConstants {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int searchPSExt(String text, int startPos, int endPos, int dir, boolean ignoreCase, boolean wrap) {
|
||||||
|
return SearchPSExt(text, startPos, endPos, dir, ignoreCase, wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SearchPSExt(String text, int startRow, int startCol, int endRow, int endCol, int dir, boolean ignoreCase, boolean wrap) {
|
||||||
|
int cols = getCols();
|
||||||
|
int sPos = (startRow - 1) * cols + startCol;
|
||||||
|
int ePos = (endRow - 1) * cols + endCol;
|
||||||
|
return SearchPSExt(text, sPos, ePos, dir, ignoreCase, wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int searchPSExt(String text, int startRow, int startCol, int endRow, int endCol, int dir, boolean ignoreCase, boolean wrap) {
|
||||||
|
return SearchPSExt(text, startRow, startCol, endRow, endCol, dir, ignoreCase, wrap);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean matchesAt(String screenText, String target, int pos, int size) {
|
private boolean matchesAt(String screenText, String target, int pos, int size) {
|
||||||
int len = target.length();
|
int len = target.length();
|
||||||
for (int j = 0; j < len; j++) {
|
for (int j = 0; j < len; j++) {
|
||||||
@@ -303,6 +372,10 @@ public class ECLPS implements ECLConstants {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String CopyString(int sRow, int sCol, int eRow, int eCol) {
|
||||||
|
return copyString(sRow, sCol, eRow, eCol);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Paste a multi-line rectangular block of text starting at (row, col).
|
* Paste a multi-line rectangular block of text starting at (row, col).
|
||||||
*/
|
*/
|
||||||
@@ -320,6 +393,12 @@ public class ECLPS implements ECLConstants {
|
|||||||
String line = lines[i];
|
String line = lines[i];
|
||||||
for (int c = 0; c < line.length() && (col + c) < cols; c++) {
|
for (int c = 0; c < line.length() && (col + c) < cols; c++) {
|
||||||
int pos = targetRow * cols + (col + c);
|
int pos = targetRow * cols + (col + c);
|
||||||
|
if (screen.isFormatted()) {
|
||||||
|
byte fa = screen.getFieldAttributeAt(pos);
|
||||||
|
if (faIsProtected(fa & 0xFF) || screen.getCell(pos).isFieldAttribute()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
setCursorPos(pos);
|
setCursorPos(pos);
|
||||||
if (inputProcessor != null) {
|
if (inputProcessor != null) {
|
||||||
inputProcessor.typeCharacter(line.charAt(c));
|
inputProcessor.typeCharacter(line.charAt(c));
|
||||||
@@ -330,6 +409,18 @@ public class ECLPS implements ECLConstants {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int PasteString(String text, int row, int col) {
|
||||||
|
return pasteString(text, row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int pasteRectangular(String text, int row, int col) {
|
||||||
|
return pasteString(text, row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int PasteRectangular(String text, int row, int col) {
|
||||||
|
return pasteString(text, row, col);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Paste text with line wrapping across unprotected fields.
|
* Paste text with line wrapping across unprotected fields.
|
||||||
*/
|
*/
|
||||||
@@ -365,6 +456,108 @@ public class ECLPS implements ECLConstants {
|
|||||||
return charsPasted;
|
return charsPasted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int PasteLineWrap(String text, int startPos, int endCol, boolean wordWrap) {
|
||||||
|
return pasteLineWrap(text, startPos, endCol, wordWrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== Entry Assist & DOC Mode Operations ==========
|
||||||
|
|
||||||
|
public boolean isEntryAssistDOCmode() { return screen != null && screen.isEntryAssistDOCmode(); }
|
||||||
|
public boolean IsEntryAssistDOCmode() { return isEntryAssistDOCmode(); }
|
||||||
|
public void setEntryAssistDOCmode(boolean bl) { if (screen != null) screen.setEntryAssistDOCmode(bl); }
|
||||||
|
public void SetEntryAssistDOCmode(boolean bl) { setEntryAssistDOCmode(bl); }
|
||||||
|
|
||||||
|
public boolean isEntryAssistWordWrap() { return screen != null && screen.isEntryAssistWordWrap(); }
|
||||||
|
public boolean IsEntryAssistWordWrap() { return isEntryAssistWordWrap(); }
|
||||||
|
public void setEntryAssistWordWrap(boolean bl) { if (screen != null) screen.setEntryAssistWordWrap(bl); }
|
||||||
|
public void SetEntryAssistWordWrap(boolean bl) { setEntryAssistWordWrap(bl); }
|
||||||
|
|
||||||
|
public int getEntryAssistStartColumn() { return screen != null ? screen.getEntryAssistStartColumn() : 0; }
|
||||||
|
public int GetEntryAssistStartColumn() { return getEntryAssistStartColumn(); }
|
||||||
|
public void setEntryAssistStartColumn(int n) { if (screen != null) screen.setEntryAssistStartColumn(n); }
|
||||||
|
public void SetEntryAssistStartColumn(int n) { setEntryAssistStartColumn(n); }
|
||||||
|
|
||||||
|
public int getEntryAssistEndColumn() { return screen != null ? screen.getEntryAssistEndColumn() : 0; }
|
||||||
|
public int GetEntryAssistEndColumn() { return getEntryAssistEndColumn(); }
|
||||||
|
public void setEntryAssistEndColumn(int n) { if (screen != null) screen.setEntryAssistEndColumn(n); }
|
||||||
|
public void SetEntryAssistEndColumn(int n) { setEntryAssistEndColumn(n); }
|
||||||
|
|
||||||
|
public int[] getEntryAssistTabStops() { return screen != null ? screen.getEntryAssistTabStops() : null; }
|
||||||
|
public int[] GetEntryAssistTabStops() { return getEntryAssistTabStops(); }
|
||||||
|
public void setEntryAssistTabStops(int[] stops) { if (screen != null) screen.setEntryAssistTabStops(stops); }
|
||||||
|
public void SetEntryAssistTabStops(int[] stops) { setEntryAssistTabStops(stops); }
|
||||||
|
|
||||||
|
public void processWordTab(boolean forward) { if (inputProcessor != null) inputProcessor.processWordTab(forward); else if (screen != null) screen.processWordTab(forward); }
|
||||||
|
public void ProcessWordTab(boolean forward) { processWordTab(forward); }
|
||||||
|
public void wordTab(boolean forward) { processWordTab(forward); }
|
||||||
|
public void WordTab(boolean forward) { processWordTab(forward); }
|
||||||
|
|
||||||
|
public void processDeleteWord() { if (inputProcessor != null) inputProcessor.processDeleteWord(); else if (screen != null) screen.processDeleteWord(); }
|
||||||
|
public void ProcessDeleteWord() { processDeleteWord(); }
|
||||||
|
public void deleteWord() { processDeleteWord(); }
|
||||||
|
public void DeleteWord() { processDeleteWord(); }
|
||||||
|
|
||||||
|
public void processWordLeft() { if (inputProcessor != null) inputProcessor.processWordLeft(); }
|
||||||
|
public void ProcessWordLeft() { processWordLeft(); }
|
||||||
|
public void wordLeft() { processWordLeft(); }
|
||||||
|
public void WordLeft() { processWordLeft(); }
|
||||||
|
|
||||||
|
public void processWordRight() { if (inputProcessor != null) inputProcessor.processWordRight(); }
|
||||||
|
public void ProcessWordRight() { processWordRight(); }
|
||||||
|
public void wordRight() { processWordRight(); }
|
||||||
|
public void WordRight() { processWordRight(); }
|
||||||
|
|
||||||
|
public void processFieldEnd() { if (inputProcessor != null) inputProcessor.processFieldEnd(); }
|
||||||
|
public void ProcessFieldEnd() { processFieldEnd(); }
|
||||||
|
public void fieldEnd() { processFieldEnd(); }
|
||||||
|
public void FieldEnd() { processFieldEnd(); }
|
||||||
|
|
||||||
|
// ========== Field Management Accessors ==========
|
||||||
|
|
||||||
|
public ECLField getField(int pos) {
|
||||||
|
return getFieldList().findField(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ECLField GetField(int pos) {
|
||||||
|
return getField(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ECLField getField(int row, int col) {
|
||||||
|
return getFieldList().findField(row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ECLField GetField(int row, int col) {
|
||||||
|
return getField(row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ECLField getFirstField() {
|
||||||
|
return getFieldList().getFirstField();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ECLField GetFirstField() {
|
||||||
|
return getFirstField();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ECLField getNextField(ECLField prev) {
|
||||||
|
return getFieldList().getNextField(prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ECLField GetNextField(ECLField prev) {
|
||||||
|
return getNextField(prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ECLField getPreviousField(ECLField next) {
|
||||||
|
return getFieldList().getPreviousField(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ECLField GetPreviousField(ECLField next) {
|
||||||
|
return getPreviousField(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ECLFieldList GetFieldList() {
|
||||||
|
return getFieldList();
|
||||||
|
}
|
||||||
|
|
||||||
// ========== ECLPS Event Listener Management ==========
|
// ========== ECLPS Event Listener Management ==========
|
||||||
|
|
||||||
private final java.util.List<ECLPSListener> psListeners = new java.util.concurrent.CopyOnWriteArrayList<>();
|
private final java.util.List<ECLPSListener> psListeners = new java.util.concurrent.CopyOnWriteArrayList<>();
|
||||||
@@ -455,6 +648,29 @@ public class ECLPS implements ECLConstants {
|
|||||||
/**
|
/**
|
||||||
* Send keystrokes with configurable inter-character or inter-mnemonic delay.
|
* Send keystrokes with configurable inter-character or inter-mnemonic delay.
|
||||||
*/
|
*/
|
||||||
|
public void sendCharacters(String keys) {
|
||||||
|
sendCharacters(keys, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendCharacters(String keys) {
|
||||||
|
sendCharacters(keys, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendCharacters(String keys, int delayMs) {
|
||||||
|
sendCharacters(keys, delayMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendCharacters(String keys, int row, int col, int delayMs) {
|
||||||
|
if (row > 0 && col > 0) {
|
||||||
|
setCursorPos(row - 1, col - 1);
|
||||||
|
}
|
||||||
|
sendCharacters(keys, delayMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendCharacters(String keys, int row, int col, int delayMs) {
|
||||||
|
sendCharacters(keys, row, col, delayMs);
|
||||||
|
}
|
||||||
|
|
||||||
public void sendCharacters(String keys, int delayMs) {
|
public void sendCharacters(String keys, int delayMs) {
|
||||||
if (keys == null || keys.isEmpty()) return;
|
if (keys == null || keys.isEmpty()) return;
|
||||||
if (delayMs <= 0) {
|
if (delayMs <= 0) {
|
||||||
@@ -466,6 +682,11 @@ public class ECLPS implements ECLConstants {
|
|||||||
int len = keys.length();
|
int len = keys.length();
|
||||||
while (i < len) {
|
while (i < len) {
|
||||||
if (keys.charAt(i) == '[') {
|
if (keys.charAt(i) == '[') {
|
||||||
|
if (i + 1 < len && keys.charAt(i + 1) == '[') {
|
||||||
|
// Escaped bracket "[["
|
||||||
|
sendKeys("[");
|
||||||
|
i += 2;
|
||||||
|
} else {
|
||||||
int close = keys.indexOf(']', i);
|
int close = keys.indexOf(']', i);
|
||||||
if (close > i) {
|
if (close > i) {
|
||||||
String mnemonic = keys.substring(i, close + 1);
|
String mnemonic = keys.substring(i, close + 1);
|
||||||
@@ -475,6 +696,7 @@ public class ECLPS implements ECLConstants {
|
|||||||
sendKeys(keys.substring(i, i + 1));
|
sendKeys(keys.substring(i, i + 1));
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
sendKeys(keys.substring(i, i + 1));
|
sendKeys(keys.substring(i, i + 1));
|
||||||
i++;
|
i++;
|
||||||
|
|||||||
@@ -131,6 +131,22 @@ public class InputProcessor {
|
|||||||
int baddr = screen.getCursorAddress();
|
int baddr = screen.getCursorAddress();
|
||||||
baddr = ((baddr % size) + size) % size;
|
baddr = ((baddr % size) + size) % size;
|
||||||
|
|
||||||
|
// Entry Assist DOC mode / Word Wrap handling
|
||||||
|
if ((screen.isEntryAssistDOCmode() || screen.isEntryAssistWordWrap()) && !isNvtMode()) {
|
||||||
|
int curCol = baddr % screen.getCols();
|
||||||
|
int endCol = screen.getEntryAssistEndColumn();
|
||||||
|
int startCol = screen.getEntryAssistStartColumn();
|
||||||
|
if (curCol >= endCol) {
|
||||||
|
screen.handleWordWrap(baddr, ch);
|
||||||
|
baddr = screen.getCursorAddress();
|
||||||
|
if (ch == ' ' && (baddr % screen.getCols()) == startCol) {
|
||||||
|
screen.markAllChanged();
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (screen.isFormatted()) {
|
if (screen.isFormatted()) {
|
||||||
// Check if cursor is at a field attribute
|
// Check if cursor is at a field attribute
|
||||||
ExtendedAttribute ea = screen.getCell(baddr);
|
ExtendedAttribute ea = screen.getCell(baddr);
|
||||||
|
|||||||
@@ -909,19 +909,108 @@ public class ScreenBuffer {
|
|||||||
// ========== Entry Assist & DOC Mode Operations ==========
|
// ========== Entry Assist & DOC Mode Operations ==========
|
||||||
|
|
||||||
public boolean isEntryAssistDOCmode() { return docMode; }
|
public boolean isEntryAssistDOCmode() { return docMode; }
|
||||||
|
public boolean IsEntryAssistDOCmode() { return isEntryAssistDOCmode(); }
|
||||||
public void setEntryAssistDOCmode(boolean bl) { this.docMode = bl; }
|
public void setEntryAssistDOCmode(boolean bl) { this.docMode = bl; }
|
||||||
|
public void SetEntryAssistDOCmode(boolean bl) { setEntryAssistDOCmode(bl); }
|
||||||
|
|
||||||
public boolean isEntryAssistWordWrap() { return wordWrap; }
|
public boolean isEntryAssistWordWrap() { return wordWrap; }
|
||||||
|
public boolean IsEntryAssistWordWrap() { return isEntryAssistWordWrap(); }
|
||||||
public void setEntryAssistWordWrap(boolean bl) { this.wordWrap = bl; }
|
public void setEntryAssistWordWrap(boolean bl) { this.wordWrap = bl; }
|
||||||
|
public void SetEntryAssistWordWrap(boolean bl) { setEntryAssistWordWrap(bl); }
|
||||||
|
|
||||||
public int getEntryAssistStartColumn() { return docStartCol; }
|
public int getEntryAssistStartColumn() { return docStartCol; }
|
||||||
|
public int GetEntryAssistStartColumn() { return getEntryAssistStartColumn(); }
|
||||||
public void setEntryAssistStartColumn(int n) { this.docStartCol = Math.max(0, n); }
|
public void setEntryAssistStartColumn(int n) { this.docStartCol = Math.max(0, n); }
|
||||||
|
public void SetEntryAssistStartColumn(int n) { setEntryAssistStartColumn(n); }
|
||||||
|
|
||||||
public int getEntryAssistEndColumn() { return docEndCol >= 0 ? docEndCol : (cols - 1); }
|
public int getEntryAssistEndColumn() { return docEndCol >= 0 ? docEndCol : (cols - 1); }
|
||||||
|
public int GetEntryAssistEndColumn() { return getEntryAssistEndColumn(); }
|
||||||
public void setEntryAssistEndColumn(int n) { this.docEndCol = n; }
|
public void setEntryAssistEndColumn(int n) { this.docEndCol = n; }
|
||||||
|
public void SetEntryAssistEndColumn(int n) { setEntryAssistEndColumn(n); }
|
||||||
|
|
||||||
public int[] getEntryAssistTabStops() { return tabStops; }
|
public int[] getEntryAssistTabStops() { return tabStops; }
|
||||||
|
public int[] GetEntryAssistTabStops() { return getEntryAssistTabStops(); }
|
||||||
public void setEntryAssistTabStops(int[] stops) { this.tabStops = stops; }
|
public void setEntryAssistTabStops(int[] stops) { this.tabStops = stops; }
|
||||||
|
public void SetEntryAssistTabStops(int[] stops) { setEntryAssistTabStops(stops); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform Entry Assist word wrap if typing near/past end margin.
|
||||||
|
* Moves any partial word typed on the current line to the beginning of the next line (docStartCol).
|
||||||
|
*/
|
||||||
|
public synchronized boolean handleWordWrap(int curAddr, char typedChar) {
|
||||||
|
if (!docMode && !wordWrap) return false;
|
||||||
|
int size = rows * cols;
|
||||||
|
if (size <= 0) return false;
|
||||||
|
curAddr = ((curAddr % size) + size) % size;
|
||||||
|
|
||||||
|
int curRow = curAddr / cols;
|
||||||
|
int curCol = curAddr % cols;
|
||||||
|
int endCol = getEntryAssistEndColumn();
|
||||||
|
int startCol = getEntryAssistStartColumn();
|
||||||
|
|
||||||
|
if (curCol < endCol) return false;
|
||||||
|
|
||||||
|
if (typedChar == ' ') {
|
||||||
|
int nextRow = (curRow + 1) % rows;
|
||||||
|
setCursorPosition(nextRow, startCol);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan backwards to find the start of the current word on this row
|
||||||
|
int rowStartAddr = curRow * cols + startCol;
|
||||||
|
int scan = curAddr - 1;
|
||||||
|
while (scan >= rowStartAddr) {
|
||||||
|
ExtendedAttribute ea = getCell(scan);
|
||||||
|
if (ea.isFieldAttribute()) break;
|
||||||
|
int ec = ea.ec & 0xFF;
|
||||||
|
if (ec == 0 || ec == 0x40 || ea.ucs4 == ' ' || ea.ucs4 == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
scan--;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wordStartAddr = scan + 1;
|
||||||
|
int wordStartCol = wordStartAddr % cols;
|
||||||
|
|
||||||
|
if (wordStartCol > startCol && wordStartAddr < curAddr) {
|
||||||
|
int wordLen = curAddr - wordStartAddr;
|
||||||
|
ExtendedAttribute[] wordCells = new ExtendedAttribute[wordLen];
|
||||||
|
for (int i = 0; i < wordLen; i++) {
|
||||||
|
wordCells[i] = new ExtendedAttribute();
|
||||||
|
wordCells[i].copyFrom(getCell(wordStartAddr + i));
|
||||||
|
getCell(wordStartAddr + i).clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
int nextRow = (curRow + 1) % rows;
|
||||||
|
int targetAddr = nextRow * cols + startCol;
|
||||||
|
if (formatted) {
|
||||||
|
targetAddr = findNextUnprotected(targetAddr - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < wordLen; i++) {
|
||||||
|
int dst = (targetAddr + i) % size;
|
||||||
|
if (!getCell(dst).isFieldAttribute()) {
|
||||||
|
getCell(dst).copyFrom(wordCells[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setCursorAddress((targetAddr + wordLen) % size);
|
||||||
|
screenChanged = true;
|
||||||
|
updateDisplaySnapshot();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// Word spans entire line or starts at startCol, wrap to next line
|
||||||
|
int nextRow = (curRow + 1) % rows;
|
||||||
|
int targetAddr = nextRow * cols + startCol;
|
||||||
|
if (formatted) {
|
||||||
|
targetAddr = findNextUnprotected(targetAddr - 1);
|
||||||
|
}
|
||||||
|
setCursorAddress(targetAddr);
|
||||||
|
screenChanged = true;
|
||||||
|
updateDisplaySnapshot();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void processWordTab(boolean forward) {
|
public synchronized void processWordTab(boolean forward) {
|
||||||
int size = rows * cols;
|
int size = rows * cols;
|
||||||
@@ -1060,4 +1149,12 @@ public class ScreenBuffer {
|
|||||||
screenChanged = true;
|
screenChanged = true;
|
||||||
updateDisplaySnapshot();
|
updateDisplaySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inspect and balance Shift-Out (0x0E) and Shift-In (0x0F) markers, ensuring DBCS integrity.
|
||||||
|
*/
|
||||||
|
public synchronized void balanceSOSI() {
|
||||||
|
cleanAdjacentSISO(0);
|
||||||
|
processSOSI();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,11 +44,41 @@ public class TelnetConnection {
|
|||||||
*/
|
*/
|
||||||
public void connect() throws IOException {
|
public void connect() throws IOException {
|
||||||
ConnectionConfig.ProxyType proxyType = config.getProxyType();
|
ConnectionConfig.ProxyType proxyType = config.getProxyType();
|
||||||
boolean hasProxy = proxyType != null && proxyType != ConnectionConfig.ProxyType.NONE &&
|
String proxyHost = config.getProxyHost();
|
||||||
config.getProxyHost() != null && !config.getProxyHost().trim().isEmpty();
|
int proxyPort = config.getProxyPort();
|
||||||
|
String proxyUser = config.getProxyUsername();
|
||||||
|
String proxyPass = config.getProxyPassword();
|
||||||
|
|
||||||
String connectHost = hasProxy ? config.getProxyHost().trim() : config.getHost();
|
if ((proxyType == null || proxyType == ConnectionConfig.ProxyType.NONE) && (proxyHost == null || proxyHost.trim().isEmpty())) {
|
||||||
int connectPort = hasProxy ? (config.getProxyPort() > 0 ? config.getProxyPort() : (proxyType == ConnectionConfig.ProxyType.HTTP ? 8080 : 1080)) : config.getPort();
|
// Check JVM system properties (matching IBM HoD browser/system default fallback)
|
||||||
|
String sysSocks = System.getProperty("socksProxyHost");
|
||||||
|
String sysHttp = System.getProperty("http.proxyHost");
|
||||||
|
if (sysSocks != null && !sysSocks.trim().isEmpty()) {
|
||||||
|
proxyType = ConnectionConfig.ProxyType.SOCKS5;
|
||||||
|
proxyHost = sysSocks.trim();
|
||||||
|
String portStr = System.getProperty("socksProxyPort");
|
||||||
|
if (portStr != null) {
|
||||||
|
try { proxyPort = Integer.parseInt(portStr.trim()); } catch (NumberFormatException ignored) {}
|
||||||
|
}
|
||||||
|
proxyUser = System.getProperty("java.net.socks.username");
|
||||||
|
proxyPass = System.getProperty("java.net.socks.password");
|
||||||
|
} else if (sysHttp != null && !sysHttp.trim().isEmpty()) {
|
||||||
|
proxyType = ConnectionConfig.ProxyType.HTTP;
|
||||||
|
proxyHost = sysHttp.trim();
|
||||||
|
String portStr = System.getProperty("http.proxyPort");
|
||||||
|
if (portStr != null) {
|
||||||
|
try { proxyPort = Integer.parseInt(portStr.trim()); } catch (NumberFormatException ignored) {}
|
||||||
|
}
|
||||||
|
proxyUser = System.getProperty("http.proxyUser");
|
||||||
|
proxyPass = System.getProperty("http.proxyPassword");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasProxy = proxyType != null && proxyType != ConnectionConfig.ProxyType.NONE &&
|
||||||
|
proxyHost != null && !proxyHost.trim().isEmpty();
|
||||||
|
|
||||||
|
String connectHost = hasProxy ? proxyHost.trim() : config.getHost();
|
||||||
|
int connectPort = hasProxy ? (proxyPort > 0 ? proxyPort : (proxyType == ConnectionConfig.ProxyType.HTTP ? 8080 : 1080)) : config.getPort();
|
||||||
|
|
||||||
log.info("Connecting TCP socket to " + connectHost + ":" + connectPort +
|
log.info("Connecting TCP socket to " + connectHost + ":" + connectPort +
|
||||||
(hasProxy ? " (via " + proxyType + " proxy for " + config.getHost() + ":" + config.getPort() + ")" : "") +
|
(hasProxy ? " (via " + proxyType + " proxy for " + config.getHost() + ":" + config.getPort() + ")" : "") +
|
||||||
@@ -67,13 +97,13 @@ public class TelnetConnection {
|
|||||||
if (hasProxy) {
|
if (hasProxy) {
|
||||||
switch (proxyType) {
|
switch (proxyType) {
|
||||||
case HTTP:
|
case HTTP:
|
||||||
establishHttpProxy(rawSocket, config.getHost(), config.getPort(), config.getProxyUsername(), config.getProxyPassword());
|
establishHttpProxy(rawSocket, config.getHost(), config.getPort(), proxyUser, proxyPass);
|
||||||
break;
|
break;
|
||||||
case SOCKS4:
|
case SOCKS4:
|
||||||
establishSocks4Proxy(rawSocket, config.getHost(), config.getPort(), config.getProxyUsername());
|
establishSocks4Proxy(rawSocket, config.getHost(), config.getPort(), proxyUser);
|
||||||
break;
|
break;
|
||||||
case SOCKS5:
|
case SOCKS5:
|
||||||
establishSocks5Proxy(rawSocket, config.getHost(), config.getPort(), config.getProxyUsername(), config.getProxyPassword());
|
establishSocks5Proxy(rawSocket, config.getHost(), config.getPort(), proxyUser, proxyPass);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
package haus.nightmare.lib3270j.ecl;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import haus.nightmare.lib3270j.TerminalModel;
|
||||||
|
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||||
|
import haus.nightmare.lib3270j.input.InputProcessor;
|
||||||
|
import haus.nightmare.lib3270j.screen.ExtendedAttribute;
|
||||||
|
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||||
|
import static haus.nightmare.lib3270j.ecl.ECLConstants.*;
|
||||||
|
import static haus.nightmare.lib3270j.protocol.DS3270Constants.*;
|
||||||
|
|
||||||
|
public class ECLOIAPhase4Test {
|
||||||
|
|
||||||
|
private ScreenBuffer screen;
|
||||||
|
private EbcdicTranslator translator;
|
||||||
|
private InputProcessor input;
|
||||||
|
private ECLOIA oia;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup() {
|
||||||
|
translator = new EbcdicTranslator();
|
||||||
|
screen = new ScreenBuffer(TerminalModel.IBM_3278_2, translator);
|
||||||
|
input = new InputProcessor(screen, translator, null);
|
||||||
|
oia = new ECLOIA(screen, input, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInhibitionCodesAndStatusStrings() {
|
||||||
|
screen.erase(false);
|
||||||
|
assertEquals(INHIBIT_NOT_INHIBITED, oia.GetInputInhibited());
|
||||||
|
assertEquals(INHIBIT_NOT_INHIBITED, oia.GetInputInhibitedType());
|
||||||
|
assertEquals("READY", oia.GetStatusString());
|
||||||
|
|
||||||
|
// Test X-SYSTEM
|
||||||
|
oia.SetInputInhibited(INHIBIT_SYSTEM_LOCK);
|
||||||
|
assertTrue(oia.IsXSystem());
|
||||||
|
assertTrue(oia.IsXWait());
|
||||||
|
assertEquals("X-SYSTEM", oia.GetStatusString());
|
||||||
|
|
||||||
|
// Test X-NUM
|
||||||
|
oia.SetInputInhibited(INHIBIT_NUMERIC_ONLY);
|
||||||
|
assertTrue(oia.IsXNum());
|
||||||
|
assertEquals("X-NUM", oia.GetStatusString());
|
||||||
|
|
||||||
|
// Test X-PROT
|
||||||
|
oia.SetInputInhibited(INHIBIT_PROTECTED_FIELD);
|
||||||
|
assertTrue(oia.IsXProt());
|
||||||
|
assertEquals("X-PROT", oia.GetStatusString());
|
||||||
|
|
||||||
|
// Test X-OVERFLOW
|
||||||
|
oia.SetInputInhibited(INHIBIT_OVERFLOW);
|
||||||
|
assertTrue(oia.IsXOverflow());
|
||||||
|
assertEquals("X-OVERFLOW", oia.GetStatusString());
|
||||||
|
|
||||||
|
// Test X-COMM
|
||||||
|
oia.SetInputInhibited(INHIBIT_COMM_CHECK);
|
||||||
|
assertTrue(oia.IsXComm());
|
||||||
|
assertEquals("X-COMM", oia.GetStatusString());
|
||||||
|
|
||||||
|
// Test X-OP
|
||||||
|
oia.SetInputInhibited(INHIBIT_OPERATOR_DUE);
|
||||||
|
assertTrue(oia.IsXOperatorDue());
|
||||||
|
assertEquals("X-OP", oia.GetStatusString());
|
||||||
|
|
||||||
|
// Test X-INSERT
|
||||||
|
oia.SetInputInhibited(INHIBIT_NOT_INHIBITED);
|
||||||
|
input.setInsertMode(true);
|
||||||
|
assertTrue(oia.IsInsertMode());
|
||||||
|
assertTrue(oia.isXInsert());
|
||||||
|
assertEquals("X-INSERT", oia.GetStatusString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAlphanumericTypeGetters() {
|
||||||
|
screen.erase(false);
|
||||||
|
assertEquals(TYPE_ALPHANUMERIC, oia.GetAlphanumericType());
|
||||||
|
assertEquals("A", oia.GetAlphanumericTypeString());
|
||||||
|
assertTrue(oia.IsAlphanumeric());
|
||||||
|
assertFalse(oia.IsNumeric());
|
||||||
|
assertFalse(oia.IsDBCS());
|
||||||
|
|
||||||
|
// Numeric field
|
||||||
|
screen.setFieldAttribute(0, (byte) (FA_PRINTABLE | FA_NUMERIC));
|
||||||
|
screen.setCursorAddress(1);
|
||||||
|
assertEquals(TYPE_NUMERIC, oia.GetAlphanumericType());
|
||||||
|
assertEquals("N", oia.GetAlphanumericTypeString());
|
||||||
|
assertTrue(oia.IsNumeric());
|
||||||
|
assertFalse(oia.IsAlphanumeric());
|
||||||
|
|
||||||
|
// DBCS cell
|
||||||
|
ExtendedAttribute ea = screen.getCell(1);
|
||||||
|
ea.cs = ExtendedAttribute.CS_DBCS;
|
||||||
|
assertEquals(TYPE_DBCS, oia.GetAlphanumericType());
|
||||||
|
assertEquals("D", oia.GetAlphanumericTypeString());
|
||||||
|
assertTrue(oia.IsDBCS());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSynchronizationPrimitives() {
|
||||||
|
screen.erase(false);
|
||||||
|
assertTrue(oia.WaitForInput(100));
|
||||||
|
assertTrue(oia.WaitForSystemAvailable(100));
|
||||||
|
assertTrue(oia.WaitForAppAvailable(100));
|
||||||
|
|
||||||
|
// Lock keyboard, wait should timeout
|
||||||
|
input.setKeyboardLocked(true);
|
||||||
|
assertFalse(oia.WaitForInput(50));
|
||||||
|
input.setKeyboardLocked(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
package haus.nightmare.lib3270j.ecl;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import haus.nightmare.lib3270j.TerminalModel;
|
||||||
|
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||||
|
import haus.nightmare.lib3270j.input.InputProcessor;
|
||||||
|
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||||
|
import static haus.nightmare.lib3270j.ecl.ECLConstants.*;
|
||||||
|
import static haus.nightmare.lib3270j.protocol.DS3270Constants.*;
|
||||||
|
|
||||||
|
public class ECLPSPhase4Test {
|
||||||
|
|
||||||
|
private ScreenBuffer screen;
|
||||||
|
private EbcdicTranslator translator;
|
||||||
|
private InputProcessor input;
|
||||||
|
private ECLPS ps;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup() {
|
||||||
|
translator = new EbcdicTranslator();
|
||||||
|
screen = new ScreenBuffer(TerminalModel.IBM_3278_2, translator);
|
||||||
|
input = new InputProcessor(screen, translator, null);
|
||||||
|
ps = new ECLPS(screen, input, translator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchPSComprehensiveOverloads() {
|
||||||
|
screen.erase(false);
|
||||||
|
screen.setText("IBM Host On-Demand ECL Automation Presentation Space");
|
||||||
|
|
||||||
|
// 1-based indexing overloads
|
||||||
|
assertEquals(5, ps.SearchPS("Host"));
|
||||||
|
assertEquals(5, ps.searchPS("Host"));
|
||||||
|
assertEquals(5, ps.SearchPS("Host", 1));
|
||||||
|
assertEquals(5, ps.SearchPS("Host", 1, SEARCH_FORWARD));
|
||||||
|
assertEquals(5, ps.SearchPS("Host", 1, 1));
|
||||||
|
assertEquals(5, ps.SearchPS("host", 1, 1, SEARCH_FORWARD, true));
|
||||||
|
assertEquals(0, ps.SearchPS("host", 1, 1, SEARCH_FORWARD, false));
|
||||||
|
|
||||||
|
// Bounded range searches
|
||||||
|
assertEquals(5, ps.SearchPS("Host", 1, 1, 1, 30));
|
||||||
|
assertEquals(0, ps.SearchPS("Host", 1, 10, 1, 30)); // starts after "Host"
|
||||||
|
assertEquals(5, ps.SearchPS("host", 1, 1, 1, 30, DIR_FORWARD, true));
|
||||||
|
|
||||||
|
// SearchPSExt
|
||||||
|
int pos1 = ps.SearchPSExt("ECL", 1, 50, SEARCH_FORWARD, false, false);
|
||||||
|
assertEquals(20, pos1);
|
||||||
|
|
||||||
|
int pos2 = ps.SearchPSExt("ecl", 1, 1, 1, 50, DIR_FORWARD, true, false);
|
||||||
|
assertEquals(20, pos2);
|
||||||
|
|
||||||
|
// Backward search
|
||||||
|
screen.setChar(1, 10, 'Z');
|
||||||
|
screen.setChar(1, 11, 'O');
|
||||||
|
screen.setChar(1, 12, 'O');
|
||||||
|
|
||||||
|
int backFound = ps.SearchPSExt("ZOO", 200, 1, SEARCH_BACKWARD, false, false);
|
||||||
|
assertEquals(91, backFound); // row 1 col 10 is 80 + 10 + 1 = 91
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRectangularCopyAndPaste() {
|
||||||
|
screen.erase(false);
|
||||||
|
// Fill a small grid across rows 0, 1, 2
|
||||||
|
String r0 = "ROW0_DATA_TEST";
|
||||||
|
for (int c = 0; c < r0.length(); c++) screen.setChar(0, c, r0.charAt(c));
|
||||||
|
String r1 = "ROW1_DATA_TEST";
|
||||||
|
for (int c = 0; c < r1.length(); c++) screen.setChar(1, c, r1.charAt(c));
|
||||||
|
String r2 = "ROW2_DATA_TEST";
|
||||||
|
for (int c = 0; c < r2.length(); c++) screen.setChar(2, c, r2.charAt(c));
|
||||||
|
|
||||||
|
// Copy rectangular region rows 0..2, cols 0..3
|
||||||
|
String copied = ps.copyString(0, 0, 2, 3);
|
||||||
|
assertEquals("ROW0\nROW1\nROW2", copied);
|
||||||
|
|
||||||
|
// Test inverted coordinate normalization
|
||||||
|
String inverted = ps.CopyString(2, 3, 0, 0);
|
||||||
|
assertEquals("ROW0\nROW1\nROW2", inverted);
|
||||||
|
|
||||||
|
// Paste rectangular text
|
||||||
|
screen.erase(false);
|
||||||
|
screen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||||
|
screen.setFieldAttribute(80, (byte) FA_PRINTABLE);
|
||||||
|
screen.setFieldAttribute(160, (byte) FA_PRINTABLE);
|
||||||
|
|
||||||
|
int pasted = ps.PasteString("ABC\nDEF\nGHI", 0, 1);
|
||||||
|
assertEquals(9, pasted);
|
||||||
|
|
||||||
|
assertEquals("ABC", ps.getString(1, 3));
|
||||||
|
assertEquals("DEF", ps.getString(81, 3));
|
||||||
|
assertEquals("GHI", ps.getString(161, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSendCharactersWithMnemonicsAndEscaping() {
|
||||||
|
screen.erase(false);
|
||||||
|
screen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||||
|
screen.setFieldAttribute(20, (byte) FA_PRINTABLE);
|
||||||
|
screen.setCursorAddress(1);
|
||||||
|
|
||||||
|
// Test mnemonic [tab] and escaped bracket [[
|
||||||
|
ps.sendCharacters("A[[B[tab]C", 1);
|
||||||
|
|
||||||
|
assertEquals('A', screen.getChar(0, 1));
|
||||||
|
assertEquals('[', screen.getChar(0, 2));
|
||||||
|
assertEquals('B', screen.getChar(0, 3));
|
||||||
|
// After tab, at pos 21
|
||||||
|
assertEquals('C', screen.getChar(0, 21));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEntryAssistPassThroughMethods() {
|
||||||
|
screen.erase(false);
|
||||||
|
ps.SetEntryAssistDOCmode(true);
|
||||||
|
ps.SetEntryAssistWordWrap(true);
|
||||||
|
ps.SetEntryAssistStartColumn(4);
|
||||||
|
ps.SetEntryAssistEndColumn(72);
|
||||||
|
ps.SetEntryAssistTabStops(new int[]{ 8, 16, 24, 32 });
|
||||||
|
|
||||||
|
assertTrue(ps.IsEntryAssistDOCmode());
|
||||||
|
assertTrue(ps.IsEntryAssistWordWrap());
|
||||||
|
assertEquals(4, ps.GetEntryAssistStartColumn());
|
||||||
|
assertEquals(72, ps.GetEntryAssistEndColumn());
|
||||||
|
assertArrayEquals(new int[]{ 8, 16, 24, 32 }, ps.GetEntryAssistTabStops());
|
||||||
|
|
||||||
|
ps.setCursorPos(0, 0);
|
||||||
|
ps.WordTab(true);
|
||||||
|
assertEquals(8, ps.getCursorCol());
|
||||||
|
|
||||||
|
ps.WordTab(true);
|
||||||
|
assertEquals(16, ps.getCursorCol());
|
||||||
|
|
||||||
|
ps.WordTab(false);
|
||||||
|
assertEquals(8, ps.getCursorCol());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFieldManagementAccessors() {
|
||||||
|
screen.erase(false);
|
||||||
|
screen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||||
|
screen.setFieldAttribute(50, (byte) (FA_PRINTABLE | FA_PROTECT));
|
||||||
|
|
||||||
|
assertNotNull(ps.GetFieldList());
|
||||||
|
assertEquals(2, ps.GetFieldList().GetFieldCount());
|
||||||
|
|
||||||
|
ECLField f0 = ps.GetFirstField();
|
||||||
|
assertNotNull(f0);
|
||||||
|
assertEquals(0, f0.GetStart());
|
||||||
|
assertEquals(1, f0.GetDataStart());
|
||||||
|
assertEquals(49, f0.GetEnd());
|
||||||
|
assertEquals(49, f0.GetLength());
|
||||||
|
assertFalse(f0.IsProtected());
|
||||||
|
|
||||||
|
ECLField f1 = ps.GetNextField(f0);
|
||||||
|
assertNotNull(f1);
|
||||||
|
assertEquals(50, f1.GetStart());
|
||||||
|
assertTrue(f1.IsProtected());
|
||||||
|
|
||||||
|
assertEquals(f0.GetStart(), ps.GetPreviousField(f1).GetStart());
|
||||||
|
assertEquals(f0.GetStart(), ps.GetField(25).GetStart());
|
||||||
|
assertEquals(f1.GetStart(), ps.GetField(60).GetStart());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,304 @@
|
|||||||
|
package haus.nightmare.lib3270j.graphics;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.awt.Point;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verification test suite for Phase 3: Host Graphics (GOCA / 3179G / Programmed Symbols) (GDDM).
|
||||||
|
* Covers items 3.1 through 3.6 of the PhasedUpdates specification.
|
||||||
|
*/
|
||||||
|
public class GocaPhase3Test {
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
// ITEM 3.1: GOCA Order 0x23 / 0x27 - Viewing Window (Clipping Viewport)
|
||||||
|
// ====================================================================================================
|
||||||
|
@Test
|
||||||
|
@DisplayName("Item 3.1: Viewing Window (0x23 / 0x27) clipping bounds and raster isolation")
|
||||||
|
public void testItem3_1_ViewingWindowClipping() {
|
||||||
|
GraphicsPlane plane = new GraphicsPlane(400, 300);
|
||||||
|
GocaDecoder decoder = new GocaDecoder(plane);
|
||||||
|
|
||||||
|
assertFalse(plane.isViewingWindowActive());
|
||||||
|
|
||||||
|
// Define Viewing Window: x in [-100, 100], y in [-100, 100]
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
out.write(GocaConstants.G_GSVW_DEF); // 0x23
|
||||||
|
out.write(0x08); // 8-byte payload
|
||||||
|
out.write((byte) 0xFF); out.write((byte) 0x9C); // xMin = -100
|
||||||
|
out.write((byte) 0xFF); out.write((byte) 0x9C); // yMin = -100
|
||||||
|
out.write(0x00); out.write(100); // xMax = 100
|
||||||
|
out.write(0x00); out.write(100); // yMax = 100
|
||||||
|
|
||||||
|
byte[] vwOrder = out.toByteArray();
|
||||||
|
decoder.decodeGoca(vwOrder, 0, vwOrder.length);
|
||||||
|
|
||||||
|
assertTrue(plane.isViewingWindowActive());
|
||||||
|
assertEquals(-100, plane.getViewingWindowXMin());
|
||||||
|
assertEquals(-100, plane.getViewingWindowYMin());
|
||||||
|
assertEquals(100, plane.getViewingWindowXMax());
|
||||||
|
assertEquals(100, plane.getViewingWindowYMax());
|
||||||
|
|
||||||
|
// 1. Draw a line entirely outside viewing window: (-300, -150) to (-200, -150)
|
||||||
|
ByteArrayOutputStream lineOutside = new ByteArrayOutputStream();
|
||||||
|
lineOutside.write(GocaConstants.G_GSCOL); lineOutside.write(0x02); // Red
|
||||||
|
lineOutside.write(GocaConstants.G_GLINE); lineOutside.write(0x08);
|
||||||
|
lineOutside.write((byte) 0xFE); lineOutside.write((byte) 0xD4); // -300
|
||||||
|
lineOutside.write((byte) 0xFF); lineOutside.write((byte) 0x6A); // -150
|
||||||
|
lineOutside.write((byte) 0xFF); lineOutside.write((byte) 0x38); // -200
|
||||||
|
lineOutside.write((byte) 0xFF); lineOutside.write((byte) 0x6A); // -150
|
||||||
|
|
||||||
|
byte[] lineOutBytes = lineOutside.toByteArray();
|
||||||
|
decoder.decodeGoca(lineOutBytes, 0, lineOutBytes.length);
|
||||||
|
|
||||||
|
int pxClipped = plane.mapX(-300);
|
||||||
|
int pyClipped = plane.mapY(-150);
|
||||||
|
int pixelVal = plane.getRgbBuffer()[pyClipped * plane.getCanvasWidth() + pxClipped];
|
||||||
|
assertEquals(0, pixelVal, "Pixel outside viewing window must be clipped (0)");
|
||||||
|
|
||||||
|
// 2. Draw a line inside viewing window: (0, 0) to (50, 50)
|
||||||
|
ByteArrayOutputStream lineInside = new ByteArrayOutputStream();
|
||||||
|
lineInside.write(GocaConstants.G_GSCOL); lineInside.write(0x04); // Green
|
||||||
|
lineInside.write(GocaConstants.G_GLINE); lineInside.write(0x08);
|
||||||
|
lineInside.write(0x00); lineInside.write(0); lineInside.write(0x00); lineInside.write(0);
|
||||||
|
lineInside.write(0x00); lineInside.write(50); lineInside.write(0x00); lineInside.write(50);
|
||||||
|
|
||||||
|
byte[] lineInBytes = lineInside.toByteArray();
|
||||||
|
decoder.decodeGoca(lineInBytes, 0, lineInBytes.length);
|
||||||
|
|
||||||
|
int pxIn = plane.mapX(0);
|
||||||
|
int pyIn = plane.mapY(0);
|
||||||
|
int pixelInVal = plane.getRgbBuffer()[pyIn * plane.getCanvasWidth() + pxIn];
|
||||||
|
assertNotEquals(0, pixelInVal, "Pixel inside viewing window must be rendered");
|
||||||
|
|
||||||
|
// 3. Clear viewing window via zero-length payload
|
||||||
|
ByteArrayOutputStream clearVw = new ByteArrayOutputStream();
|
||||||
|
clearVw.write(GocaConstants.G_GSVW);
|
||||||
|
clearVw.write(0x00);
|
||||||
|
byte[] clearVwBytes = clearVw.toByteArray();
|
||||||
|
decoder.decodeGoca(clearVwBytes, 0, clearVwBytes.length);
|
||||||
|
|
||||||
|
assertFalse(plane.isViewingWindowActive());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
// ITEM 3.2: GOCA Order 0x22 - Segment Characteristics
|
||||||
|
// ====================================================================================================
|
||||||
|
@Test
|
||||||
|
@DisplayName("Item 3.2: Segment Characteristics (0x22) flags decoding")
|
||||||
|
public void testItem3_2_SegmentCharacteristics() {
|
||||||
|
GraphicsPlane plane = new GraphicsPlane(400, 300);
|
||||||
|
GocaDecoder decoder = new GocaDecoder(plane);
|
||||||
|
|
||||||
|
// Chained (0x80) | Dynamic (0x40) | Visible (0x00) -> 0xC0
|
||||||
|
decoder.processSegmentCharacteristics(new byte[]{(byte) 0xC0});
|
||||||
|
assertTrue(decoder.isSegChained());
|
||||||
|
assertTrue(decoder.isSegDynamic());
|
||||||
|
assertTrue(decoder.isSegVisible());
|
||||||
|
|
||||||
|
// Non-chained (0x00) | Static (0x00) | Invisible (0x20) -> 0x20
|
||||||
|
decoder.processSegmentCharacteristics(new byte[]{(byte) 0x20});
|
||||||
|
assertFalse(decoder.isSegChained());
|
||||||
|
assertFalse(decoder.isSegDynamic());
|
||||||
|
assertFalse(decoder.isSegVisible());
|
||||||
|
|
||||||
|
// Unchained (0x00) | Dynamic (0x40) | Visible (0x00) -> 0x40
|
||||||
|
decoder.processSegmentCharacteristics(new byte[]{(byte) 0x40});
|
||||||
|
assertFalse(decoder.isSegChained());
|
||||||
|
assertTrue(decoder.isSegDynamic());
|
||||||
|
assertTrue(decoder.isSegVisible());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
// ITEM 3.3: GOCA Order 0x11 - Fractional Line Width Calculation
|
||||||
|
// ====================================================================================================
|
||||||
|
@Test
|
||||||
|
@DisplayName("Item 3.3: Fractional Line Width (0x11) fixed-point ratio and plane synchronization")
|
||||||
|
public void testItem3_3_FractionalLineWidth() {
|
||||||
|
GraphicsPlane plane = new GraphicsPlane(400, 300);
|
||||||
|
GocaDecoder decoder = new GocaDecoder(plane);
|
||||||
|
|
||||||
|
// 1. Fixed point: integer = 3, fraction = 128/256 (0.5) -> 3.5
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
out.write(GocaConstants.G_GSFLW); // 0x11
|
||||||
|
out.write(0x03); // Integer part
|
||||||
|
out.write(0x80); // Fractional part (128 / 256 = 0.5)
|
||||||
|
|
||||||
|
byte[] flwBytes = out.toByteArray();
|
||||||
|
decoder.decodeGoca(flwBytes, 0, flwBytes.length);
|
||||||
|
|
||||||
|
assertEquals(3.5, decoder.getFractionalLineWidth(), 0.001);
|
||||||
|
assertEquals(3.5, plane.getFractionalLineWidth(), 0.001);
|
||||||
|
|
||||||
|
// 2. Single byte operand: integer = 2, frac = 0 -> 2.0
|
||||||
|
ByteArrayOutputStream out2 = new ByteArrayOutputStream();
|
||||||
|
out2.write(GocaConstants.G_GSFLW);
|
||||||
|
out2.write(0x02);
|
||||||
|
|
||||||
|
byte[] flwBytes2 = out2.toByteArray();
|
||||||
|
decoder.decodeGoca(flwBytes2, 0, flwBytes2.length);
|
||||||
|
|
||||||
|
assertEquals(2.0, decoder.getFractionalLineWidth(), 0.001);
|
||||||
|
assertEquals(2.0, plane.getFractionalLineWidth(), 0.001);
|
||||||
|
|
||||||
|
// 3. Reset defaults restores 1.0
|
||||||
|
decoder.resetDefaults();
|
||||||
|
assertEquals(1.0, decoder.getFractionalLineWidth(), 0.001);
|
||||||
|
assertEquals(1.0, plane.getFractionalLineWidth(), 0.001);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
// ITEM 3.4: GCS Character Baseline Angle and Shear Vector Transforms
|
||||||
|
// ====================================================================================================
|
||||||
|
@Test
|
||||||
|
@DisplayName("Item 3.4: Character Baseline Angle (GSCA 0x34) and Shear (GSCR 0x35) transforms")
|
||||||
|
public void testItem3_4_CharacterAngleAndShear() {
|
||||||
|
GraphicsPlane plane = new GraphicsPlane(400, 300);
|
||||||
|
GocaDecoder decoder = new GocaDecoder(plane);
|
||||||
|
|
||||||
|
// 1. GSCA 0x34: Character Angle vector (ax=0, ay=10) -> 90 degrees vertical
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
out.write(GocaConstants.G_GSCA);
|
||||||
|
out.write(0x04);
|
||||||
|
out.write(0x00); out.write(0); // ax = 0
|
||||||
|
out.write(0x00); out.write(10); // ay = 10
|
||||||
|
|
||||||
|
// GSCR 0x35: Character Shear vector (sx=10, sy=10) -> 45 degrees slant
|
||||||
|
out.write(GocaConstants.G_GSCR);
|
||||||
|
out.write(0x04);
|
||||||
|
out.write(0x00); out.write(10); // sx = 10
|
||||||
|
out.write(0x00); out.write(10); // sy = 10
|
||||||
|
|
||||||
|
byte[] stream = out.toByteArray();
|
||||||
|
decoder.decodeGoca(stream, 0, stream.length);
|
||||||
|
|
||||||
|
assertEquals(90.0, decoder.getCharAngle(), 0.01);
|
||||||
|
assertEquals(45.0, decoder.getCharShear(), 0.01);
|
||||||
|
|
||||||
|
// 2. Draw stroked vector text with active angle and shear
|
||||||
|
ByteArrayOutputStream textStream = new ByteArrayOutputStream();
|
||||||
|
textStream.write(GocaConstants.G_GSCS); textStream.write(0xF8); // Vector font
|
||||||
|
textStream.write(GocaConstants.G_GCHST); // Character String Absolute
|
||||||
|
textStream.write(0x05); // 4 bytes coord + 1 byte text 'A' (EBCDIC 0xC1)
|
||||||
|
textStream.write(0x00); textStream.write(50);
|
||||||
|
textStream.write(0x00); textStream.write(50);
|
||||||
|
textStream.write((byte) 0xC1); // EBCDIC 'A'
|
||||||
|
|
||||||
|
byte[] textBytes = textStream.toByteArray();
|
||||||
|
decoder.decodeGoca(textBytes, 0, textBytes.length);
|
||||||
|
|
||||||
|
assertTrue(plane.hasContent(), "Vector text with angle/shear must render to GraphicsPlane");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
// ITEM 3.5: Multi-Plane Programmed Symbols (RWS 4..7) & Multi-Color PS Cells
|
||||||
|
// ====================================================================================================
|
||||||
|
@Test
|
||||||
|
@DisplayName("Item 3.5: Multi-Plane Programmed Symbols (RWS 4..7) & Triple-Plane Composite Color Cells")
|
||||||
|
public void testItem3_5_ProgrammedSymbolsTriplePlane() {
|
||||||
|
ProgramSymbolManager psm = new ProgramSymbolManager(9, 16);
|
||||||
|
|
||||||
|
// 1. Single-plane set (RWS 2)
|
||||||
|
byte[] loadpsSingle = new byte[4 + 18];
|
||||||
|
loadpsSingle[0] = 0x01; // Format 1
|
||||||
|
loadpsSingle[1] = 0x41; // LCID 0x41
|
||||||
|
loadpsSingle[2] = 0x40; // Start codepoint
|
||||||
|
loadpsSingle[3] = 0x02; // RWS 2
|
||||||
|
for (int i = 0; i < 18; i++) loadpsSingle[4 + i] = (byte) 0x55;
|
||||||
|
psm.loadps(loadpsSingle);
|
||||||
|
|
||||||
|
ProgramSymbolSet singleSet = psm.getSymbolSet(0x41);
|
||||||
|
assertNotNull(singleSet);
|
||||||
|
assertFalse(singleSet.isTriplePlane());
|
||||||
|
|
||||||
|
// 2. Triple-plane set (RWS 4, LCID 0x42) loaded with all 3 planes simultaneously (54 bytes)
|
||||||
|
byte[] loadpsTriple = new byte[4 + 54];
|
||||||
|
loadpsTriple[0] = 0x01; // Format 1
|
||||||
|
loadpsTriple[1] = 0x42; // LCID 0x42
|
||||||
|
loadpsTriple[2] = 0x40; // Start codepoint
|
||||||
|
loadpsTriple[3] = 0x04; // RWS 4 (Triple Plane)
|
||||||
|
// Red slice: plane 1 (0x01)
|
||||||
|
for (int i = 0; i < 18; i++) loadpsTriple[4 + i] = (byte) 0xFF;
|
||||||
|
// Green slice: plane 2 (0x02)
|
||||||
|
for (int i = 0; i < 18; i++) loadpsTriple[4 + 18 + i] = (byte) 0xFF;
|
||||||
|
// Blue slice: plane 4 (0x04)
|
||||||
|
for (int i = 0; i < 18; i++) loadpsTriple[4 + 36 + i] = (byte) 0xFF;
|
||||||
|
|
||||||
|
psm.loadps(loadpsTriple);
|
||||||
|
|
||||||
|
ProgramSymbolSet tripleSet = psm.getSymbolSet(0x42);
|
||||||
|
assertNotNull(tripleSet);
|
||||||
|
assertTrue(tripleSet.isTriplePlane());
|
||||||
|
|
||||||
|
ProgramSymbolSet.SymbolSlot slot = psm.getSymbol(0x42, 0x40);
|
||||||
|
assertNotNull(slot);
|
||||||
|
assertTrue(slot.isTriplePlane());
|
||||||
|
|
||||||
|
// Verify multi-color RGB mapping: composite plane (1|2|4 = 7 -> White)
|
||||||
|
int[] rgb = slot.getRgbPixels(GocaConstants.GOCA_COLORS[0], 0);
|
||||||
|
assertNotNull(rgb);
|
||||||
|
assertEquals(9 * 16, rgb.length);
|
||||||
|
assertEquals(0xFFFFFFFF, rgb[0]); // White pixel (all 3 planes set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
// ITEM 3.6: Pick Correlation for Graphical Segment Selection
|
||||||
|
// ====================================================================================================
|
||||||
|
@Test
|
||||||
|
@DisplayName("Item 3.6: Pick Correlation & 56-Byte Graphic Input Structured Field generation")
|
||||||
|
public void testItem3_6_PickCorrelationAndGraphicInput() {
|
||||||
|
GraphicsPlane plane = new GraphicsPlane(400, 300);
|
||||||
|
GocaDecoder decoder = new GocaDecoder(plane);
|
||||||
|
|
||||||
|
// 1. Define Segment 100: drawing a line from (-50, -50) to (50, 50)
|
||||||
|
ByteArrayOutputStream segStream = new ByteArrayOutputStream();
|
||||||
|
segStream.write(GocaConstants.G_BEGSEGM);
|
||||||
|
segStream.write(0x0C);
|
||||||
|
segStream.write(0x00); segStream.write(0x00); segStream.write(0x00); segStream.write(100); // Seg ID 100
|
||||||
|
segStream.write(0x00); segStream.write(0x00);
|
||||||
|
segStream.write(0x00); segStream.write(0x00);
|
||||||
|
segStream.write(0x00); segStream.write(0x00); segStream.write(0x00); segStream.write(0x00);
|
||||||
|
segStream.write(GocaConstants.G_GSCOL); segStream.write(0x06); // Yellow
|
||||||
|
segStream.write(GocaConstants.G_GLINE); segStream.write(0x08);
|
||||||
|
segStream.write((byte) 0xFF); segStream.write((byte) 0xCE); // -50
|
||||||
|
segStream.write((byte) 0xFF); segStream.write((byte) 0xCE); // -50
|
||||||
|
segStream.write(0x00); segStream.write(50);
|
||||||
|
segStream.write(0x00); segStream.write(50);
|
||||||
|
segStream.write(GocaConstants.G_ENDSEGM); segStream.write(0x00);
|
||||||
|
|
||||||
|
byte[] segBytes = segStream.toByteArray();
|
||||||
|
decoder.decodeGoca(segBytes, 0, segBytes.length);
|
||||||
|
|
||||||
|
// Hit test inside segment bounds: (0, 0)
|
||||||
|
int pickedSeg = decoder.findPickedSegment(0, 0);
|
||||||
|
assertEquals(100, pickedSeg, "Segment 100 should be hit at (0, 0)");
|
||||||
|
|
||||||
|
// Hit test outside segment bounds: (200, 200)
|
||||||
|
int pickedMiss = decoder.findPickedSegment(200, 200);
|
||||||
|
assertEquals(0, pickedMiss, "Missed click should default to segId 0");
|
||||||
|
|
||||||
|
// 2. Build 56-byte Graphic Input Structured Field via GraphicInputBuilder
|
||||||
|
byte[] inputSF = GraphicInputBuilder.buildPickCorrelation(25, -25, 5);
|
||||||
|
assertEquals(56, inputSF.length);
|
||||||
|
// Header check: 0x0034 (52 byte payload length per IBM HOD)
|
||||||
|
assertEquals(0x00, inputSF[0]);
|
||||||
|
assertEquals(0x34, inputSF[1]);
|
||||||
|
assertEquals(0x0F, inputSF[2]);
|
||||||
|
assertEquals(0x0F, inputSF[3]);
|
||||||
|
|
||||||
|
// Coordinate checks: X = 25, Y = -25
|
||||||
|
int gX = ((inputSF[24] & 0xFF) << 8) | (inputSF[25] & 0xFF);
|
||||||
|
int gY = (short) (((inputSF[26] & 0xFF) << 8) | (inputSF[27] & 0xFF));
|
||||||
|
assertEquals(25, gX);
|
||||||
|
assertEquals(-25, gY);
|
||||||
|
|
||||||
|
// Mouse action flags: trigger class = 4, correlation class = 4, Button 1 = 0x01
|
||||||
|
assertEquals(0x04, inputSF[31]);
|
||||||
|
assertEquals(0x04, inputSF[33]);
|
||||||
|
assertEquals(0x01, inputSF[35]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
package haus.nightmare.lib3270j.screen;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import haus.nightmare.lib3270j.TerminalModel;
|
||||||
|
import haus.nightmare.lib3270j.charset.AbstractDBCSCodePage;
|
||||||
|
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||||
|
import haus.nightmare.lib3270j.ecl.ECLField;
|
||||||
|
import haus.nightmare.lib3270j.ecl.ECLFieldList;
|
||||||
|
import haus.nightmare.lib3270j.input.InputProcessor;
|
||||||
|
import static haus.nightmare.lib3270j.protocol.DS3270Constants.*;
|
||||||
|
|
||||||
|
public class ScreenBufferPhase4Test {
|
||||||
|
|
||||||
|
private ScreenBuffer screen;
|
||||||
|
private EbcdicTranslator translator;
|
||||||
|
private InputProcessor input;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup() {
|
||||||
|
translator = new EbcdicTranslator();
|
||||||
|
screen = new ScreenBuffer(TerminalModel.IBM_3278_2, translator);
|
||||||
|
input = new InputProcessor(screen, translator, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEntryAssistDOCModeAndWordWrap() {
|
||||||
|
screen.erase(false);
|
||||||
|
screen.setEntryAssistDOCmode(true);
|
||||||
|
screen.setEntryAssistWordWrap(true);
|
||||||
|
screen.setEntryAssistStartColumn(0);
|
||||||
|
screen.setEntryAssistEndColumn(20);
|
||||||
|
|
||||||
|
assertTrue(screen.isEntryAssistDOCmode());
|
||||||
|
assertTrue(screen.isEntryAssistWordWrap());
|
||||||
|
assertEquals(0, screen.getEntryAssistStartColumn());
|
||||||
|
assertEquals(20, screen.getEntryAssistEndColumn());
|
||||||
|
|
||||||
|
// Type "HELLO WORLD " near column 20
|
||||||
|
screen.setCursorPosition(0, 10);
|
||||||
|
String s1 = "HELLO ";
|
||||||
|
for (int i = 0; i < s1.length(); i++) {
|
||||||
|
input.typeCharacter(s1.charAt(i));
|
||||||
|
}
|
||||||
|
assertEquals(16, screen.getCursorCol());
|
||||||
|
assertEquals(0, screen.getCursorRow());
|
||||||
|
|
||||||
|
// Now type "TESTING" which starts at col 16 and crosses col 20
|
||||||
|
String s2 = "TESTING";
|
||||||
|
for (int i = 0; i < s2.length(); i++) {
|
||||||
|
input.typeCharacter(s2.charAt(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// "HELLO " should remain on row 0 (cols 10..15)
|
||||||
|
assertEquals("HELLO ", screen.getString(10, 6));
|
||||||
|
// "TESTING" should be soft-wrapped to row 1 (cols 0..6)
|
||||||
|
assertEquals("TESTING", screen.getString(80, 7));
|
||||||
|
assertEquals(1, screen.getCursorRow());
|
||||||
|
assertEquals(7, screen.getCursorCol());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEntryAssistWordWrapTypingSpaceAtEndMargin() {
|
||||||
|
screen.erase(false);
|
||||||
|
screen.setEntryAssistWordWrap(true);
|
||||||
|
screen.setEntryAssistStartColumn(5);
|
||||||
|
screen.setEntryAssistEndColumn(20);
|
||||||
|
|
||||||
|
screen.setCursorPosition(0, 20);
|
||||||
|
input.typeCharacter(' ');
|
||||||
|
|
||||||
|
// Cursor should wrap to next line at startCol 5
|
||||||
|
assertEquals(1, screen.getCursorRow());
|
||||||
|
assertEquals(5, screen.getCursorCol());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEntryAssistTabStopsAndNavigation() {
|
||||||
|
screen.erase(false);
|
||||||
|
screen.setEntryAssistTabStops(new int[]{ 10, 25, 50, 70 });
|
||||||
|
assertArrayEquals(new int[]{ 10, 25, 50, 70 }, screen.getEntryAssistTabStops());
|
||||||
|
|
||||||
|
screen.setCursorPosition(0, 0);
|
||||||
|
screen.processWordTab(true);
|
||||||
|
assertEquals(10, screen.getCursorCol());
|
||||||
|
|
||||||
|
screen.processWordTab(true);
|
||||||
|
assertEquals(25, screen.getCursorCol());
|
||||||
|
|
||||||
|
screen.processWordTab(true);
|
||||||
|
assertEquals(50, screen.getCursorCol());
|
||||||
|
|
||||||
|
screen.processWordTab(false);
|
||||||
|
assertEquals(25, screen.getCursorCol());
|
||||||
|
|
||||||
|
screen.processWordTab(false);
|
||||||
|
assertEquals(10, screen.getCursorCol());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProcessDeleteWord() {
|
||||||
|
screen.erase(false);
|
||||||
|
screen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||||
|
screen.setFieldAttribute(40, (byte) (FA_PRINTABLE | FA_PROTECT));
|
||||||
|
|
||||||
|
// Field contains "ALPHA BRAVO CHARLIE"
|
||||||
|
String text = "ALPHA BRAVO CHARLIE";
|
||||||
|
for (int i = 0; i < text.length(); i++) {
|
||||||
|
screen.setChar(0, 1 + i, text.charAt(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Place cursor at "BRAVO" (col 7)
|
||||||
|
screen.setCursorPosition(0, 7);
|
||||||
|
screen.processDeleteWord();
|
||||||
|
|
||||||
|
// "BRAVO " should be deleted, leaving "ALPHA CHARLIE"
|
||||||
|
assertEquals("ALPHA CHARLIE", screen.getString(1, 13));
|
||||||
|
assertTrue(screen.isModified(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDBCSSOSIPairBalancing() {
|
||||||
|
EbcdicTranslator dbcsTranslator = new EbcdicTranslator("930");
|
||||||
|
AbstractDBCSCodePage cp = (AbstractDBCSCodePage) dbcsTranslator.getCodePage();
|
||||||
|
cp.registerDbcsPair(0x4341, '\u6771');
|
||||||
|
|
||||||
|
ScreenBuffer dbcsScreen = new ScreenBuffer(TerminalModel.IBM_3279_2, dbcsTranslator);
|
||||||
|
dbcsScreen.erase(false);
|
||||||
|
dbcsScreen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||||
|
dbcsScreen.setFieldAttribute(20, (byte) (FA_PRINTABLE | FA_PROTECT));
|
||||||
|
|
||||||
|
// Insert DBCS character
|
||||||
|
boolean inserted = dbcsScreen.insertChar(1, '\u6771');
|
||||||
|
assertTrue(inserted);
|
||||||
|
assertEquals(ExtendedAttribute.CS_DBCS, dbcsScreen.getCell(1).cs);
|
||||||
|
assertEquals(ExtendedAttribute.DB_LEFT, dbcsScreen.getCell(1).db);
|
||||||
|
assertEquals(ExtendedAttribute.DB_RIGHT, dbcsScreen.getCell(2).db);
|
||||||
|
|
||||||
|
// Delete DBCS character at left half
|
||||||
|
boolean deleted = dbcsScreen.deleteChar(1);
|
||||||
|
assertTrue(deleted);
|
||||||
|
assertEquals(0, dbcsScreen.getCell(1).ec);
|
||||||
|
assertEquals(0, dbcsScreen.getCell(2).ec);
|
||||||
|
|
||||||
|
// Test balanceSOSI removes adjacent empty SO SI
|
||||||
|
dbcsScreen.setCell(3, 0x0E); // SO
|
||||||
|
dbcsScreen.setCell(4, 0x0F); // SI
|
||||||
|
dbcsScreen.balanceSOSI();
|
||||||
|
assertEquals(0, dbcsScreen.getCell(3).ec);
|
||||||
|
assertEquals(0, dbcsScreen.getCell(4).ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWrappedFieldListHandling() {
|
||||||
|
screen.erase(false);
|
||||||
|
// Field 1 at pos 100..499
|
||||||
|
screen.setFieldAttribute(100, (byte) FA_PRINTABLE);
|
||||||
|
// Field 2 at pos 500..99 (wrapping around end of buffer)
|
||||||
|
screen.setFieldAttribute(500, (byte) (FA_PRINTABLE | FA_PROTECT));
|
||||||
|
|
||||||
|
ECLFieldList list = screen.buildFieldList();
|
||||||
|
assertEquals(2, list.getFieldCount());
|
||||||
|
|
||||||
|
ECLField f1 = list.getFirstField();
|
||||||
|
assertNotNull(f1);
|
||||||
|
assertEquals(100, f1.getStart());
|
||||||
|
assertEquals(101, f1.getDataStart());
|
||||||
|
assertEquals(499, f1.getEnd());
|
||||||
|
assertEquals(399, f1.getLength());
|
||||||
|
assertFalse(f1.isWrapped());
|
||||||
|
assertFalse(f1.isProtected());
|
||||||
|
|
||||||
|
ECLField f2 = list.getNextField(f1);
|
||||||
|
assertNotNull(f2);
|
||||||
|
assertEquals(500, f2.getStart());
|
||||||
|
assertEquals(501, f2.getDataStart());
|
||||||
|
assertEquals(99, f2.getEnd());
|
||||||
|
assertEquals(1920 - 500 - 1 + 100, f2.getLength());
|
||||||
|
assertTrue(f2.isWrapped());
|
||||||
|
assertTrue(f2.isProtected());
|
||||||
|
|
||||||
|
// Test containment across wrap
|
||||||
|
assertTrue(f2.contains(500));
|
||||||
|
assertTrue(f2.contains(501));
|
||||||
|
assertTrue(f2.contains(1919));
|
||||||
|
assertTrue(f2.contains(0));
|
||||||
|
assertTrue(f2.contains(99));
|
||||||
|
assertFalse(f2.contains(100));
|
||||||
|
assertFalse(f2.contains(200));
|
||||||
|
|
||||||
|
// Test field lookup by position
|
||||||
|
assertEquals(f2.getStart(), list.findField(50).getStart());
|
||||||
|
assertEquals(f1.getStart(), list.findField(200).getStart());
|
||||||
|
assertEquals(f2.getStart(), list.findField(600).getStart());
|
||||||
|
|
||||||
|
// Test navigation
|
||||||
|
assertEquals(f2.getStart(), list.getPreviousField(f1).getStart());
|
||||||
|
assertNull(list.getNextField(f2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleFieldFullBufferWrap() {
|
||||||
|
screen.erase(false);
|
||||||
|
screen.setFieldAttribute(250, (byte) FA_PRINTABLE);
|
||||||
|
|
||||||
|
ECLFieldList list = screen.buildFieldList();
|
||||||
|
assertEquals(1, list.getFieldCount());
|
||||||
|
|
||||||
|
ECLField f = list.getFirstField();
|
||||||
|
assertNotNull(f);
|
||||||
|
assertEquals(250, f.getStart());
|
||||||
|
assertEquals(251, f.getDataStart());
|
||||||
|
assertEquals(249, f.getEnd());
|
||||||
|
assertEquals(1919, f.getLength());
|
||||||
|
assertTrue(f.isWrapped());
|
||||||
|
|
||||||
|
assertTrue(f.contains(0));
|
||||||
|
assertTrue(f.contains(250));
|
||||||
|
assertTrue(f.contains(1919));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -241,4 +241,51 @@ public class ProxyConnectionTest {
|
|||||||
connection.disconnect();
|
connection.disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSystemPropertiesProxyFallback() throws Exception {
|
||||||
|
try (ServerSocket proxyServer = new ServerSocket(0)) {
|
||||||
|
int proxyPort = proxyServer.getLocalPort();
|
||||||
|
CountDownLatch serverHandshakeDone = new CountDownLatch(1);
|
||||||
|
|
||||||
|
Thread serverThread = new Thread(() -> {
|
||||||
|
try (Socket clientSock = proxyServer.accept()) {
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(clientSock.getInputStream(), StandardCharsets.US_ASCII));
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if (line.isEmpty()) break;
|
||||||
|
}
|
||||||
|
OutputStream out = clientSock.getOutputStream();
|
||||||
|
out.write("HTTP/1.1 200 OK\r\n\r\n".getBytes(StandardCharsets.US_ASCII));
|
||||||
|
out.flush();
|
||||||
|
serverHandshakeDone.countDown();
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
});
|
||||||
|
serverThread.setDaemon(true);
|
||||||
|
serverThread.start();
|
||||||
|
|
||||||
|
String origHost = System.getProperty("http.proxyHost");
|
||||||
|
String origPort = System.getProperty("http.proxyPort");
|
||||||
|
try {
|
||||||
|
System.setProperty("http.proxyHost", "127.0.0.1");
|
||||||
|
System.setProperty("http.proxyPort", String.valueOf(proxyPort));
|
||||||
|
|
||||||
|
ConnectionConfig config = new ConnectionConfig("test.fallback.org", 23);
|
||||||
|
config.setConnectTimeoutMs(5000);
|
||||||
|
|
||||||
|
TelnetFSM fsm = new TelnetFSM(config, new haus.nightmare.lib3270j.screen.ScreenBuffer(TerminalModel.IBM_3279_4, new haus.nightmare.lib3270j.charset.EbcdicTranslator()), null);
|
||||||
|
TelnetConnection connection = new TelnetConnection(config, fsm);
|
||||||
|
|
||||||
|
connection.connect();
|
||||||
|
assertTrue(connection.isConnected());
|
||||||
|
assertTrue(serverHandshakeDone.await(3, TimeUnit.SECONDS));
|
||||||
|
connection.disconnect();
|
||||||
|
} finally {
|
||||||
|
if (origHost != null) System.setProperty("http.proxyHost", origHost);
|
||||||
|
else System.clearProperty("http.proxyHost");
|
||||||
|
if (origPort != null) System.setProperty("http.proxyPort", origPort);
|
||||||
|
else System.clearProperty("http.proxyPort");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user