From 6efa9d5c76329d020d4b73b21cc09593f99573b2 Mon Sep 17 00:00:00 2001 From: Rudi Date: Mon, 20 Apr 2026 20:32:50 -0400 Subject: [PATCH] All current bugs fixed --- build/j3270/org/pubvm/j3270/J3270App$2.class | Bin 878 -> 878 bytes build/j3270/org/pubvm/j3270/J3270App.class | Bin 15945 -> 15928 bytes .../config/Settings$StartupBehavior.class | Bin 1286 -> 1286 bytes .../org/pubvm/j3270/config/Settings.class | Bin 7046 -> 9784 bytes .../org/pubvm/j3270/ui/SettingsDialog$1.class | Bin 1599 -> 1599 bytes .../org/pubvm/j3270/ui/SettingsDialog$2.class | Bin 1802 -> 1802 bytes .../org/pubvm/j3270/ui/SettingsDialog$3.class | Bin 665 -> 665 bytes .../org/pubvm/j3270/ui/SettingsDialog$4.class | Bin 1355 -> 1355 bytes .../org/pubvm/j3270/ui/SettingsDialog.class | Bin 16457 -> 21254 bytes .../org/pubvm/j3270/ui/TerminalPanel$1.class | Bin 1362 -> 2347 bytes .../org/pubvm/j3270/ui/TerminalPanel$2.class | Bin 781 -> 689 bytes .../org/pubvm/j3270/ui/TerminalPanel.class | Bin 16529 -> 22584 bytes .../main/java/org/pubvm/j3270/J3270App.java | 17 +- .../java/org/pubvm/j3270/config/Settings.java | 90 ++++ .../org/pubvm/j3270/ui/SettingsDialog.java | 205 ++++++++- .../org/pubvm/j3270/ui/TerminalPanel.java | 389 +++++++++++++++++- run.sh | 7 +- 17 files changed, 678 insertions(+), 30 deletions(-) diff --git a/build/j3270/org/pubvm/j3270/J3270App$2.class b/build/j3270/org/pubvm/j3270/J3270App$2.class index c31a9edac41c030e5d1bf65c035f2c181fb3c2e6..de1d926f0384bf396de6a87bc5f6cd4d37c90499 100644 GIT binary patch delta 13 UcmaFI_Kt0X5;Nn{$;!;$03>?^XaE2J delta 13 UcmaFI_Kt0X5;Nnn$;!;$03?6}X#fBK diff --git a/build/j3270/org/pubvm/j3270/J3270App.class b/build/j3270/org/pubvm/j3270/J3270App.class index daefe95ec216b46164261a87c6887d92477a52cf..3f7ee53c4e2a4fe6272ceccfa9733fa3741f3454 100644 GIT binary patch delta 790 zcmZuvYeE&?skbY2psc_S%vFw#)TAnU8p z)NUwA7fKd$5kd5;en=Ok1P5kzqaq`^7*zI7>sRr@a~_`eJU5T4V%8;ZM%mt+m5!fQis|7BKxq&PfEvb5q-W(p5@8)(l zcdqhABV5dx`AzK;OiG=?hQdeKx6t67@ZVzrCZIClqoPYl_xURNe{*Ih#E~&dV`8ge zDb!3y5Z57u8HnO~m}rKXnb^h+DBwoexJgMd#rteU0JEJ{CL0|&keDkHcOj5@2xUHG zTCt1;*ug>+vKZwezLz%Cu@r4A!&!knEJr^paEFz+$0`i58Y8U1DEDEUcD&+#ykRXq z^8lt;M->lJ%R>xgJ#}p095ykM%{2P*Fimtj^Otwi(E<&Rhy{*{Ia(3PV~FN)#PI}H z@)Xi|606yU47S6<4&<>DB|IaR>OvXMp_b?2;04sP8;$HiFE7HyUfkp*+~#H6S(yaok;Hd;NRxop2 zQqfGpW}{28wPRkafYS*1Q)LD+v%RO4rcXN`zQUgrTKX=u^aJ7eDRlG;M&hrAj7mhZ zW%dI>c?y3#L;xNMMLots3@i1?=|O{Hpdpbwh+sTX29rH;ESy-So^A N(4rE0rzd5P^czdDx{v?> delta 792 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 delta 37 tcmeC;>*Cv>!N$b!b+Q&)oe;wh1{H>%3>pl-7_1n6GdMB)oxF=J007Z}3#R}8 diff --git a/build/j3270/org/pubvm/j3270/ui/SettingsDialog$3.class b/build/j3270/org/pubvm/j3270/ui/SettingsDialog$3.class index f85b833fa85f79be66c3875b23171992ed54c470..f5efe874ac53b801f14bb4ebb7805c43e4899c71 100644 GIT binary patch delta 19 bcmbQqI+JySArs@C$wo|8jQc0&GU)&SJR=3n delta 19 acmbQqI+JySArqtgWFsakMy1KQOgaEEQv{L# diff --git a/build/j3270/org/pubvm/j3270/ui/SettingsDialog$4.class b/build/j3270/org/pubvm/j3270/ui/SettingsDialog$4.class index 748008323a7086064212b2647af71964dee9a927..3d9b553e552c820eede85ecf02c72b614e3373ff 100644 GIT binary patch delta 51 zcmV-30L=f(3d;(xY6Jo9lWPQkA@KkO0rCJI0rLPK0rUVQ0rdbV0rmhj0rvnv0r&t% J0s4~+1wX#T5H0`! delta 51 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 literal 16457 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 VIPRG4&Qf<--;N_41&%`U|6f|>e5?Qf diff --git a/build/j3270/org/pubvm/j3270/ui/TerminalPanel$1.class b/build/j3270/org/pubvm/j3270/ui/TerminalPanel$1.class index 27b7e2d6c81e85f47d559452f4b3298a2567a5d9..c0bbf6061a580ad937c213e3d7aeb034bf522b73 100644 GIT binary patch literal 2347 zcmcIkS#uOs6#i~zX);`rArK8q05RZXfq{Slg+#)Tz-T7K3?u;s=}h0ubm-|0y(Cdo zC@r;0T&qOIrNjlx?Lo@PkfPKlh zQr{QBGSn)Fa4ZLBSU0Iot8sP4jf-hvy783dIigonr(9u^#3q6uW9a0PVWW?&p^wWpOi~jJQ@D~&_zDE5Wy5-)`jxUbitwrJMu#=+`yBMP6Vpy1(u#ZkmIKmzC z8=I6Wk8*S^rg9>Jy?9K);~e{>e9e73$Pgs=W3mz596fk~p*AB7V^r7N90Q{;d(q2r zP`Z(-C#32jjy|axk*Y_8d@~Uo#t{Wcjwhw6|7fdSS)@b;g}fy!!--%3DFuTZM=``u z=jUpu^gdIn%ppKC$PkJpldY7^0ERizQZbNMXJypm93wKM^fzqHI5NR8juSG$m5wl~ zjkl#(N}u6)b`dEhBc-P~o|Dr0s%@XCl`U0{@&Cxmb{dDk1c9PEhio;QrP(3?{Q=2w z=yC}twwO|N(+BG;hk-n`ms*x26NXB`9c-fAyo}Z~i$>*kCMrn(%W;nQHc^}Dtf4c~);0RKz1! zaJhQ#NJ+AY9X+LUcUO>1_cUuIjr~Sfi0%g(gJnj-=n4lKLmfr*2eo-THIJveLV?CG zF^*LkVQJhLp2Mh5gywLv!Z}spoRLIu4jFkCk(E`~_*D;L716H{YOkRluhX)B15J37 zHpnG(<1HH3w=sx!a2oHz!h5)g_v!ZmKEY*tjSuk?KEfP6#&5WSTlf^yG0Nd?N?D3tnEz^SVk zE2;SXE8xsk94;eq8{K89g3-3>y(868qDSRB$(87nXNs7VWD#cRxU1(|QA_#!j0moy z0l#233e@8{+I&TtlpB=$O`3yWE4{pf*)VnQd9ta&3ph__kk(x-Ui3M2c!|yk)hs8A G{{8`u^h5pt delta 621 zcmX|;%TE(g6oAGQlx%;Z7I2G5!<&1MWf!0^O^9|W_NV!iCr!BNdIx)pZGuirQ2$2BKRnrqU= z{Yt4^cWL94<}_!*sx_1>FywGfGc2E8jP>TGmYzLzDR5qM!L)Lu)#y^tV3#zbjD@f4 zY|9nRRkU0$`!%mpt%qN1KO9uq9d{K?({*p*$)s1C@#X_RoQ-=cmTV}=I2IG)Tn>B)kfSz{oRvLnu})0tkQJ#B)Kb=p#PgO8Nc za}gI!O~hq0xz(7wi-h=(*v?~l;hb#Plfmb?$HFMf6j{aR1!ZnZS{(I90-IZn`rF(Q UwdIc`m}-z_rbS(Ow%L@Ne^n`N)c^nh diff --git a/build/j3270/org/pubvm/j3270/ui/TerminalPanel$2.class b/build/j3270/org/pubvm/j3270/ui/TerminalPanel$2.class index 2f3341d3f6b823148822e823e03dbb7361b81878..c26fa2c1e00e46b0164e61e62672cdc1778672e3 100644 GIT binary patch literal 689 zcmah`O=}cE5PiKn*^imYX0q}7D{+aiS-}bM;2}YfHLJoV1QT+|t#`LcI+^KSx_ege zQ#g>BE$^Kr+mUw{>G(Z zB9u$JIv;CA?AeHqoiHT6C6#nf8OrUo!#WyRs3M86h$h3ztZ@vH5mvqxFA8o(4D2W* zg)1?x`r#2TobE~2)5L=+PcGJQw8M zQfl~jpVw0xWTDmW|8M7Q{|h$#jQM@Xhi5x{d?_97>%t60Px?8{e-7}-tC5{;HO#b? z>iCZfhT5*M?xnW0TKq~WVY(T&R#>diiEDIimL&ej#7gu zp3{rr2E_^?@D(>H&Jlf=P~sMDQ}nG{guZpDa{+dN`S%FUJ^hF!uTh$6G*Bk~jV69a i6F+c=US+Td0q#zV?qQiQpi9-stZ!@JK4EyNPV_gpah=lu literal 781 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 lC-A literal 16529 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 diff --git a/j3270/src/main/java/org/pubvm/j3270/J3270App.java b/j3270/src/main/java/org/pubvm/j3270/J3270App.java index 618c6ce..84f255a 100644 --- a/j3270/src/main/java/org/pubvm/j3270/J3270App.java +++ b/j3270/src/main/java/org/pubvm/j3270/J3270App.java @@ -109,7 +109,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate viewMenu.add(createMenuItem("Font Size -", KeyEvent.VK_MINUS, () -> adjustFontSize(-2))); viewMenu.add(createMenuItem("Reset Font", KeyEvent.VK_0, () -> { terminalPanel.setFontSize(16); - pack(); + terminalPanel.guardedPack(); })); menuBar.add(viewMenu); @@ -200,8 +200,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate setTitle("j3270 — " + config.getHost() + ":" + config.getPort()); // Resize window to match model - terminalPanel.revalidate(); - pack(); + terminalPanel.guardedPack(); new Thread(() -> { try { @@ -233,9 +232,11 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate private void adjustFontSize(int delta) { int current = terminalPanel.getFontSize(); - int newSize = Math.max(8, Math.min(36, current + delta)); + int newSize = Math.max(8, Math.min(72, current + delta)); terminalPanel.setFontSize(newSize); - pack(); + // Pack after font change — setFontSize sets the resize guard + // to prevent the componentResized from re-triggering autoFitFont + terminalPanel.guardedPack(); } // ========== ConnectionListener ========== @@ -249,8 +250,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate // Auto-resize and focus on first full session if (newState.isFullSession() && !oldState.isFullSession()) { - terminalPanel.revalidate(); - pack(); + terminalPanel.guardedPack(); terminalPanel.requestFocusInWindow(); } }); @@ -293,8 +293,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate @Override public void onScreenSizeChanged(int rows, int cols) { SwingUtilities.invokeLater(() -> { - terminalPanel.revalidate(); - pack(); + terminalPanel.guardedPack(); }); } diff --git a/j3270/src/main/java/org/pubvm/j3270/config/Settings.java b/j3270/src/main/java/org/pubvm/j3270/config/Settings.java index 4bf43bd..28b7cac 100644 --- a/j3270/src/main/java/org/pubvm/j3270/config/Settings.java +++ b/j3270/src/main/java/org/pubvm/j3270/config/Settings.java @@ -3,6 +3,7 @@ package org.pubvm.j3270.config; import java.util.prefs.Preferences; import java.awt.Color; import java.io.*; +import java.io.PrintWriter; import java.util.logging.Logger; import java.util.logging.Level; @@ -171,6 +172,16 @@ public class Settings { log.info("Configuration loaded successfully."); } + // ========== Block select mode ========== + + public static boolean getBlockSelectMode() { + return prefs.getBoolean("blockSelectMode", false); + } + + public static void setBlockSelectMode(boolean block) { + prefs.putBoolean("blockSelectMode", block); + } + private static void applyConfigEntry(String section, String key, String value) { switch (section) { case "appearance": @@ -189,6 +200,7 @@ public class Settings { break; case "autoConnectHost": setAutoConnectHost(value); break; case "autoConnectPort": setAutoConnectPort(Integer.parseInt(value)); break; + case "blockSelectMode": setBlockSelectMode(Boolean.parseBoolean(value)); break; default: log.warning("Unknown behavior key: " + key); } @@ -217,4 +229,82 @@ public class Settings { break; } } + + // ========== Export to INI ========== + + /** + * Export the current configuration to an INI-style file. + * Writes all settings including appearance, behavior, colors, and keybindings. + */ + public static void exportToIniFile(String path) throws IOException { + try (PrintWriter w = new PrintWriter(new BufferedWriter(new FileWriter(path)))) { + w.println("; j3270 Configuration File"); + w.println("; Exported on " + new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date())); + w.println("; Load with: java -jar j3270.jar -c " + new File(path).getName()); + w.println(); + + // [appearance] + w.println("[appearance]"); + w.println("fontFamily = " + getFontFamily()); + w.println("fontSize = " + getFontSize()); + w.println(); + + // [behavior] + w.println("[behavior]"); + w.println("startupBehavior = " + getStartupBehavior().name()); + String acHost = getAutoConnectHost(); + if (acHost != null && !acHost.isEmpty()) { + w.println("autoConnectHost = " + acHost); + w.println("autoConnectPort = " + getAutoConnectPort()); + } + w.println("blockSelectMode = " + getBlockSelectMode()); + w.println(); + + // [colors] + w.println("[colors]"); + // Host colors 0-15 + for (int i = 0; i < 16; i++) { + String hex = prefs.get("color_" + i, null); + if (hex != null) { + w.println("color_" + i + " = " + hex); + } + } + // Mono colors + String[] monoKeys = {"NORMAL", "INTENSIFY", "PROTECTED", "PROTECTED_HIGH", "BACKGROUND"}; + for (String mk : monoKeys) { + String hex = prefs.get("mono_" + mk, null); + if (hex != null) { + w.println("mono_" + mk + " = " + hex); + } + } + w.println(); + + // [keybindings] + w.println("[keybindings]"); + // Navigation keys + String[] navActions = {"ENTER", "TAB", "shift TAB", "UP", "DOWN", "LEFT", "RIGHT", + "HOME", "END", "PAGE_UP", "PAGE_DOWN", "ESCAPE", "INSERT", "DELETE", "BACK_SPACE", "CLEAR"}; + for (String act : navActions) { + String val = prefs.get("key_" + act, null); + if (val != null) { + w.println(act + " = " + val); + } + } + // PF keys + for (int i = 1; i <= 24; i++) { + String val = prefs.get("key_PF" + i, null); + if (val != null) { + w.println("PF" + i + " = " + val); + } + } + // PA keys + for (int i = 1; i <= 3; i++) { + String val = prefs.get("key_PA" + i, null); + if (val != null) { + w.println("PA" + i + " = " + val); + } + } + } + log.info("Configuration exported to: " + path); + } } diff --git a/j3270/src/main/java/org/pubvm/j3270/ui/SettingsDialog.java b/j3270/src/main/java/org/pubvm/j3270/ui/SettingsDialog.java index 00be7ae..7ef2eee 100644 --- a/j3270/src/main/java/org/pubvm/j3270/ui/SettingsDialog.java +++ b/j3270/src/main/java/org/pubvm/j3270/ui/SettingsDialog.java @@ -10,12 +10,24 @@ import java.awt.event.*; import java.util.HashMap; import java.util.Map; import javax.swing.table.DefaultTableModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.JTableHeader; import javax.swing.border.LineBorder; +import javax.swing.border.TitledBorder; public class SettingsDialog extends JDialog { private final J3270App parentApp; + // Dark theme colors + private static final Color DARK_BG = new Color(43, 43, 43); + private static final Color DARK_BG_LIGHTER = new Color(55, 55, 55); + private static final Color DARK_FG = new Color(224, 224, 224); + private static final Color DARK_BORDER = new Color(70, 70, 70); + private static final Color DARK_SELECTION = new Color(75, 110, 175); + private static final Color DARK_BUTTON_BG = new Color(60, 63, 65); + private static final Color DARK_FIELD_BG = new Color(50, 50, 50); + // Appearance tab private JComboBox fontBox; private JSpinner fontSizeSpinner; @@ -25,6 +37,7 @@ public class SettingsDialog extends JDialog { private JPanel autoConnectPanel; private JTextField hostField; private JTextField portField; + private JCheckBox blockSelectCheck; // Advanced tab state tracking private final Color[] tempHostColors = new Color[16]; @@ -49,10 +62,15 @@ public class SettingsDialog extends JDialog { tabbedPane.addTab("Advanced", createAdvancedPanel()); JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + JButton btnExport = new JButton("Export Config..."); JButton btnOk = new JButton("OK"); JButton btnApply = new JButton("Apply"); JButton btnCancel = new JButton("Cancel"); + btnExport.addActionListener((ActionEvent e) -> { + exportConfig(); + }); + btnOk.addActionListener((ActionEvent e) -> { boolean success = applySettings(); if (success) { @@ -68,14 +86,145 @@ public class SettingsDialog extends JDialog { dispose(); }); + buttonPanel.add(btnExport); + buttonPanel.add(Box.createHorizontalStrut(20)); buttonPanel.add(btnApply); buttonPanel.add(btnCancel); buttonPanel.add(btnOk); getContentPane().add(tabbedPane, BorderLayout.CENTER); getContentPane().add(buttonPanel, BorderLayout.SOUTH); + + // Apply dark theme to all components for cross-platform readability + applyDarkTheme(getContentPane()); + applyDarkTheme(tabbedPane); + applyDarkTheme(buttonPanel); + getContentPane().setBackground(DARK_BG); } + // ========== Dark Theme Utility ========== + + /** + * Recursively apply dark theme to a component and all its children. + * Ensures the Settings dialog is readable on Windows, Linux, and macOS. + */ + private void applyDarkTheme(Component comp) { + if (comp instanceof JTabbedPane) { + JTabbedPane tp = (JTabbedPane) comp; + tp.setBackground(DARK_BG); + tp.setForeground(DARK_FG); + for (int i = 0; i < tp.getTabCount(); i++) { + applyDarkTheme(tp.getComponentAt(i)); + } + return; + } + + if (comp instanceof JTable) { + JTable table = (JTable) comp; + table.setBackground(DARK_FIELD_BG); + table.setForeground(DARK_FG); + table.setSelectionBackground(DARK_SELECTION); + table.setSelectionForeground(Color.WHITE); + table.setGridColor(DARK_BORDER); + JTableHeader header = table.getTableHeader(); + if (header != null) { + header.setBackground(DARK_BG_LIGHTER); + header.setForeground(DARK_FG); + DefaultTableCellRenderer headerRenderer = new DefaultTableCellRenderer(); + headerRenderer.setBackground(DARK_BG_LIGHTER); + headerRenderer.setForeground(DARK_FG); + header.setDefaultRenderer(headerRenderer); + } + return; + } + + if (comp instanceof JScrollPane) { + JScrollPane sp = (JScrollPane) comp; + sp.setBackground(DARK_BG); + sp.getViewport().setBackground(DARK_FIELD_BG); + applyDarkTheme(sp.getViewport().getView()); + return; + } + + if (comp instanceof JButton) { + JButton btn = (JButton) comp; + btn.setBackground(DARK_BUTTON_BG); + btn.setForeground(DARK_FG); + btn.setFocusPainted(false); + btn.setBorder(BorderFactory.createCompoundBorder( + new LineBorder(DARK_BORDER, 1), + BorderFactory.createEmptyBorder(3, 10, 3, 10))); + btn.setOpaque(true); + return; + } + + if (comp instanceof JComboBox) { + JComboBox cb = (JComboBox) comp; + cb.setBackground(DARK_FIELD_BG); + cb.setForeground(DARK_FG); + return; + } + + if (comp instanceof JSpinner) { + JSpinner sp = (JSpinner) comp; + sp.setBackground(DARK_FIELD_BG); + sp.setForeground(DARK_FG); + JComponent editor = sp.getEditor(); + if (editor instanceof JSpinner.DefaultEditor) { + JTextField tf = ((JSpinner.DefaultEditor) editor).getTextField(); + tf.setBackground(DARK_FIELD_BG); + tf.setForeground(DARK_FG); + tf.setCaretColor(DARK_FG); + } + return; + } + + if (comp instanceof JTextField) { + JTextField tf = (JTextField) comp; + tf.setBackground(DARK_FIELD_BG); + tf.setForeground(DARK_FG); + tf.setCaretColor(DARK_FG); + return; + } + + if (comp instanceof JCheckBox) { + JCheckBox cb = (JCheckBox) comp; + cb.setBackground(DARK_BG); + cb.setForeground(DARK_FG); + return; + } + + if (comp instanceof JLabel) { + comp.setForeground(DARK_FG); + return; + } + + // Generic panel / container + // Skip color swatch panels — their background IS the color + if (comp instanceof JPanel && "colorSwatch".equals(comp.getName())) { + return; + } + comp.setBackground(DARK_BG); + comp.setForeground(DARK_FG); + + if (comp instanceof JPanel) { + JPanel panel = (JPanel) comp; + // Style titled borders + if (panel.getBorder() instanceof TitledBorder) { + TitledBorder tb = (TitledBorder) panel.getBorder(); + tb.setTitleColor(DARK_FG); + } + } + + if (comp instanceof Container) { + for (Component child : ((Container) comp).getComponents()) { + applyDarkTheme(child); + } + } + } + + // ========== Tab Panels ========== + private JPanel createAppearancePanel() { JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); @@ -174,8 +323,14 @@ public class SettingsDialog extends JDialog { }); autoConnectPanel.setVisible(Settings.getStartupBehavior() == Settings.StartupBehavior.AUTO_CONNECT); - // Placeholder for potentially more behavior options below + // Block select mode checkbox gbc.gridy = 2; + gbc.gridwidth = 2; + blockSelectCheck = new JCheckBox("Block selection mode (rectangular select)", Settings.getBlockSelectMode()); + panel.add(blockSelectCheck, gbc); + + // Placeholder for potentially more behavior options below + gbc.gridy = 3; gbc.weighty = 1.0; panel.add(Box.createGlue(), gbc); @@ -204,6 +359,7 @@ public class SettingsDialog extends JDialog { Color init = org.pubvm.j3270.config.Settings.getColorOverride(i, TerminalPanel.DEFAULT_HOST_COLORS[i]); tempHostColors[i] = init; JPanel cb = new JPanel(); + cb.setName("colorSwatch"); cb.setBackground(init); cb.setPreferredSize(new Dimension(30, 30)); cb.setBorder(new LineBorder(Color.DARK_GRAY)); @@ -238,6 +394,7 @@ public class SettingsDialog extends JDialog { JPanel p = new JPanel(new BorderLayout()); p.add(new JLabel(mk, SwingConstants.CENTER), BorderLayout.NORTH); JPanel cb = new JPanel(); + cb.setName("colorSwatch"); cb.setBackground(init); cb.setPreferredSize(new Dimension(40, 40)); cb.setBorder(new LineBorder(Color.DARK_GRAY)); @@ -331,6 +488,8 @@ public class SettingsDialog extends JDialog { captureDialog.setSize(320, 100); captureDialog.setLocationRelativeTo(this); JLabel lbl = new JLabel("Press any key combination now...", SwingConstants.CENTER); + lbl.setForeground(DARK_FG); + captureDialog.getContentPane().setBackground(DARK_BG); captureDialog.add(lbl); captureDialog.addKeyListener(new KeyAdapter() { @Override @@ -442,6 +601,8 @@ public class SettingsDialog extends JDialog { return action; // nav keys default to their own name } + // ========== Apply Settings ========== + private boolean applySettings() { try { // Apply Appearance @@ -464,6 +625,9 @@ public class SettingsDialog extends JDialog { } } + // Block select mode + Settings.setBlockSelectMode(blockSelectCheck.isSelected()); + // Propagate visual changes to the app // Save Colors for (int i=0; i<16; i++) { @@ -487,4 +651,43 @@ public class SettingsDialog extends JDialog { return false; } } + + // ========== Export Config ========== + + private void exportConfig() { + JFileChooser fc = new JFileChooser(); + fc.setDialogTitle("Export Configuration"); + fc.setSelectedFile(new java.io.File("j3270.ini")); + fc.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("INI Files (*.ini)", "ini")); + + int result = fc.showSaveDialog(this); + if (result == JFileChooser.APPROVE_OPTION) { + java.io.File file = fc.getSelectedFile(); + // Ensure .ini extension + if (!file.getName().toLowerCase().endsWith(".ini")) { + file = new java.io.File(file.getAbsolutePath() + ".ini"); + } + + // Confirm overwrite + if (file.exists()) { + int confirm = JOptionPane.showConfirmDialog(this, + "File already exists. Overwrite?", "Confirm Overwrite", + JOptionPane.YES_NO_OPTION); + if (confirm != JOptionPane.YES_OPTION) return; + } + + try { + // Apply current dialog state first so the export reflects latest changes + applySettings(); + Settings.exportToIniFile(file.getAbsolutePath()); + JOptionPane.showMessageDialog(this, + "Configuration exported to:\n" + file.getAbsolutePath(), + "Export Successful", JOptionPane.INFORMATION_MESSAGE); + } catch (Exception ex) { + JOptionPane.showMessageDialog(this, + "Export failed: " + ex.getMessage(), + "Export Error", JOptionPane.ERROR_MESSAGE); + } + } + } } diff --git a/j3270/src/main/java/org/pubvm/j3270/ui/TerminalPanel.java b/j3270/src/main/java/org/pubvm/j3270/ui/TerminalPanel.java index 3163aab..81394bf 100644 --- a/j3270/src/main/java/org/pubvm/j3270/ui/TerminalPanel.java +++ b/j3270/src/main/java/org/pubvm/j3270/ui/TerminalPanel.java @@ -6,6 +6,7 @@ import org.lib3270j.screen.ScreenBuffer; import javax.swing.*; import java.awt.*; +import java.awt.datatransfer.*; import java.awt.event.*; import static org.lib3270j.protocol.DS3270Constants.*; @@ -44,6 +45,39 @@ public class TerminalPanel extends JPanel { // Background private Color bgColor; + // ========== Selection / Copy-Paste state ========== + private boolean blockSelectMode = false; + private int selectionStartRow = -1, selectionStartCol = -1; + private int selectionEndRow = -1, selectionEndCol = -1; + private boolean isDragging = false; + private static final Color SELECTION_COLOR = new Color(75, 110, 175, 100); + + // ========== Resize guard ========== + private boolean resizeGuard = false; + + /** + * Compute the horizontal render offset to center the grid within the panel. + * Any leftover pixels (from integer font sizing) are split evenly. + */ + private int getRenderOffsetX() { + int termCols = 80; + if (client != null) termCols = client.getScreenBuffer().getCols(); + int gridW = termCols * cellWidth; + int extra = getWidth() - gridW; + return Math.max(padding, extra / 2); + } + + /** + * Compute the vertical render offset to center the grid within the panel. + */ + private int getRenderOffsetY() { + int termRows = 24; + if (client != null) termRows = client.getScreenBuffer().getRows(); + int gridH = termRows * cellHeight; + int extra = getHeight() - gridH; + return Math.max(padding, extra / 2); + } + // Default Host color mapping public static final Color[] DEFAULT_HOST_COLORS = { new Color(0, 0, 0), // 0: Neutral Black @@ -87,22 +121,313 @@ public class TerminalPanel extends JPanel { setupFont(); setupCursorBlink(); - // Handle mouse clicks to position cursor and grab focus - addMouseListener(new MouseAdapter() { + blockSelectMode = org.pubvm.j3270.config.Settings.getBlockSelectMode(); + + // Handle mouse clicks to position cursor, selection, and grab focus + MouseAdapter mouseHandler = new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { requestFocusInWindow(); + + // Right-click context menu + if (SwingUtilities.isRightMouseButton(e)) { + showContextMenu(e); + return; + } + if (client != null && client.getConnectionState().isFullSession()) { - int col = (e.getX() - padding) / cellWidth; - int row = (e.getY() - padding) / cellHeight; ScreenBuffer sb = client.getScreenBuffer(); - if (row >= 0 && row < sb.getRows() && col >= 0 && col < sb.getCols()) { - sb.setCursorAddress(row * sb.getCols() + col); - repaint(); + int ox = getRenderOffsetX(); + int oy = getRenderOffsetY(); + int col = (e.getX() - ox) / cellWidth; + int row = (e.getY() - oy) / cellHeight; + col = Math.max(0, Math.min(col, sb.getCols() - 1)); + row = Math.max(0, Math.min(row, sb.getRows() - 1)); + + // Start selection + selectionStartRow = row; + selectionStartCol = col; + selectionEndRow = row; + selectionEndCol = col; + isDragging = true; + repaint(); + } + } + + @Override + public void mouseDragged(MouseEvent e) { + if (isDragging && client != null && client.getConnectionState().isFullSession()) { + ScreenBuffer sb = client.getScreenBuffer(); + int ox = getRenderOffsetX(); + int oy = getRenderOffsetY(); + int col = (e.getX() - ox) / cellWidth; + int row = (e.getY() - oy) / cellHeight; + col = Math.max(0, Math.min(col, sb.getCols() - 1)); + row = Math.max(0, Math.min(row, sb.getRows() - 1)); + + selectionEndRow = row; + selectionEndCol = col; + repaint(); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (isDragging) { + isDragging = false; + // If start == end, treat as a click (position cursor, clear selection) + if (selectionStartRow == selectionEndRow && selectionStartCol == selectionEndCol) { + if (client != null && client.getConnectionState().isFullSession()) { + ScreenBuffer sb = client.getScreenBuffer(); + sb.setCursorAddress(selectionStartRow * sb.getCols() + selectionStartCol); + clearSelection(); + } + } + repaint(); + } + } + }; + addMouseListener(mouseHandler); + addMouseMotionListener(mouseHandler); + + // Handle resize: auto-fit font to window size + addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + if (resizeGuard) return; + autoFitFont(); + } + }); + } + + // ========== Selection / Copy-Paste ========== + + private void clearSelection() { + selectionStartRow = -1; + selectionStartCol = -1; + selectionEndRow = -1; + selectionEndCol = -1; + } + + private boolean hasSelection() { + return selectionStartRow >= 0 && selectionEndRow >= 0 && + !(selectionStartRow == selectionEndRow && selectionStartCol == selectionEndCol); + } + + /** + * Get the selected text from the screen buffer. + * In block mode: rectangular selection with newlines between rows. + * In line mode: stream selection, flowing left-to-right, top-to-bottom. + */ + public String getSelectedText() { + if (!hasSelection() || client == null) return ""; + ScreenBuffer sb = client.getScreenBuffer(); + int cols = sb.getCols(); + + int r1 = Math.min(selectionStartRow, selectionEndRow); + int r2 = Math.max(selectionStartRow, selectionEndRow); + int c1, c2; + + if (blockSelectMode) { + // Block mode: rectangular selection + c1 = Math.min(selectionStartCol, selectionEndCol); + c2 = Math.max(selectionStartCol, selectionEndCol); + + StringBuilder result = new StringBuilder(); + for (int r = r1; r <= r2; r++) { + if (r > r1) result.append('\n'); + for (int c = c1; c <= c2; c++) { + int baddr = r * cols + c; + ExtendedAttribute ea = sb.getCell(baddr); + char ch = ea.ucs4; + if (ea.isFieldAttribute() || ch <= 0x20 || ch == 0xFF) { + result.append(' '); + } else { + result.append(ch); } } } + return result.toString(); + } else { + // Line/stream mode: flow from start to end + int startAddr, endAddr; + if (selectionStartRow < selectionEndRow || + (selectionStartRow == selectionEndRow && selectionStartCol <= selectionEndCol)) { + startAddr = selectionStartRow * cols + selectionStartCol; + endAddr = selectionEndRow * cols + selectionEndCol; + } else { + startAddr = selectionEndRow * cols + selectionEndCol; + endAddr = selectionStartRow * cols + selectionStartCol; + } + + StringBuilder result = new StringBuilder(); + int prevRow = startAddr / cols; + for (int addr = startAddr; addr <= endAddr; addr++) { + int curRow = addr / cols; + if (curRow != prevRow) { + result.append('\n'); + prevRow = curRow; + } + ExtendedAttribute ea = sb.getCell(addr); + char ch = ea.ucs4; + if (ea.isFieldAttribute() || ch <= 0x20 || ch == 0xFF) { + result.append(' '); + } else { + result.append(ch); + } + } + return result.toString(); + } + } + + private void copySelection() { + String text = getSelectedText(); + if (!text.isEmpty()) { + StringSelection ss = new StringSelection(text); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, null); + } + } + + private void pasteClipboard() { + if (client == null || !client.getConnectionState().isFullSession()) return; + try { + String text = (String) Toolkit.getDefaultToolkit().getSystemClipboard() + .getData(DataFlavor.stringFlavor); + if (text != null) { + for (char ch : text.toCharArray()) { + if (ch == '\n' || ch == '\r') { + // Skip newlines in paste — user may paste multi-line text + // but 3270 fields don't wrap the same way + continue; + } + if (ch >= 0x20 && ch != 0x7F) { + client.typeCharacter(ch); + } + } + refreshScreen(); + } + } catch (Exception ex) { + // Clipboard not available or wrong type — silently ignore + } + } + + private void showContextMenu(MouseEvent e) { + JPopupMenu popup = new JPopupMenu(); + + JMenuItem copyItem = new JMenuItem("Copy"); + copyItem.setEnabled(hasSelection()); + copyItem.addActionListener(ev -> copySelection()); + popup.add(copyItem); + + JMenuItem pasteItem = new JMenuItem("Paste"); + pasteItem.setEnabled(client != null && client.getConnectionState().isFullSession()); + pasteItem.addActionListener(ev -> pasteClipboard()); + popup.add(pasteItem); + + popup.addSeparator(); + + JCheckBoxMenuItem blockModeItem = new JCheckBoxMenuItem("Block Select Mode", blockSelectMode); + blockModeItem.addActionListener(ev -> { + blockSelectMode = blockModeItem.isSelected(); + org.pubvm.j3270.config.Settings.setBlockSelectMode(blockSelectMode); }); + popup.add(blockModeItem); + + popup.show(this, e.getX(), e.getY()); + } + + private boolean isCellSelected(int row, int col) { + if (!hasSelection()) return false; + + int r1 = Math.min(selectionStartRow, selectionEndRow); + int r2 = Math.max(selectionStartRow, selectionEndRow); + + if (blockSelectMode) { + int c1 = Math.min(selectionStartCol, selectionEndCol); + int c2 = Math.max(selectionStartCol, selectionEndCol); + return row >= r1 && row <= r2 && col >= c1 && col <= c2; + } else { + // Stream mode + int cols = 80; + if (client != null) cols = client.getScreenBuffer().getCols(); + int addr = row * cols + col; + int startAddr, endAddr; + if (selectionStartRow < selectionEndRow || + (selectionStartRow == selectionEndRow && selectionStartCol <= selectionEndCol)) { + startAddr = selectionStartRow * cols + selectionStartCol; + endAddr = selectionEndRow * cols + selectionEndCol; + } else { + startAddr = selectionEndRow * cols + selectionEndCol; + endAddr = selectionStartRow * cols + selectionStartCol; + } + return addr >= startAddr && addr <= endAddr; + } + } + + // ========== Font auto-resize ========== + + /** + * Auto-fit the font size to fill the current panel dimensions + * while respecting the terminal model's character grid. + * Any leftover pixels are handled by centering the grid + * (see getRenderOffsetX/Y). + */ + private void autoFitFont() { + int panelW = getWidth(); + int panelH = getHeight(); + if (panelW <= 0 || panelH <= 0) return; + + int termCols = 80; + int termRows = 24; + if (client != null) { + ScreenBuffer sb = client.getScreenBuffer(); + termCols = sb.getCols(); + termRows = sb.getRows(); + } + + // Use minimal padding for the fit calculation + int availW = panelW - 2 * padding; + int availH = panelH - 2 * padding; + if (availW <= 0 || availH <= 0) return; + + // Find the largest font size where the grid fits + int bestSize = 8; + for (int testSize = 8; testSize <= 72; testSize++) { + Font testFont = new Font(terminalFont.getFamily(), Font.PLAIN, testSize); + FontMetrics fm = getFontMetrics(testFont); + int testCellW = fm.charWidth('M'); + int testCellH = fm.getHeight(); + + if (testCellW * termCols <= availW && testCellH * termRows <= availH) { + bestSize = testSize; + } else { + break; + } + } + + if (bestSize != currentFontSize) { + currentFontSize = bestSize; + org.pubvm.j3270.config.Settings.setFontSize(bestSize); + terminalFont = new Font(terminalFont.getFamily(), Font.PLAIN, bestSize); + updateCellSize(); + } + // No window snap — any leftover pixels are centered via getRenderOffsetX/Y + repaint(); + } + + /** + * Called by J3270App when it needs to pack the frame (screen size changed, + * connection established, etc.). Sets the resize guard to prevent + * autoFitFont from firing during the pack. + */ + public void guardedPack() { + resizeGuard = true; + revalidate(); + Container top = getTopLevelAncestor(); + if (top instanceof java.awt.Window) { + ((java.awt.Window) top).pack(); + } + SwingUtilities.invokeLater(() -> resizeGuard = false); } /** @@ -167,6 +492,11 @@ public class TerminalPanel extends JPanel { // Clear bindKeyToMap(im, "CLEAR", org.pubvm.j3270.config.Settings.getKeyBinding("CLEAR", "alt C")); + // Copy/Paste bindings — Cmd+C / Cmd+V (macOS) or Ctrl+C / Ctrl+V (others) + int shortcutMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx(); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, shortcutMask), "j3270-COPY"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, shortcutMask), "j3270-PASTE"); + // Create actions for all bound keys am.put("j3270-ENTER", createAction(this::handleEnter)); am.put("j3270-ESCAPE", createAction(this::handleReset)); @@ -185,6 +515,10 @@ public class TerminalPanel extends JPanel { am.put("j3270-INSERT", createAction(this::handleInsert)); am.put("j3270-CLEAR", createAction(this::handleClear)); + // Copy/Paste actions + am.put("j3270-COPY", createAction(this::copySelection)); + am.put("j3270-PASTE", createAction(this::pasteClipboard)); + for (int i = 1; i <= 24; i++) { final int pf = i; am.put("j3270-PF" + i, createAction(() -> handlePF(pf))); @@ -365,6 +699,7 @@ public class TerminalPanel extends JPanel { setupColors(); setupKeyBindings(); setupFont(); + blockSelectMode = org.pubvm.j3270.config.Settings.getBlockSelectMode(); revalidate(); repaint(); } @@ -374,6 +709,7 @@ public class TerminalPanel extends JPanel { org.pubvm.j3270.config.Settings.setFontSize(size); terminalFont = terminalFont.deriveFont((float) size); updateCellSize(); + resizeGuard = true; revalidate(); repaint(); } @@ -406,10 +742,11 @@ public class TerminalPanel extends JPanel { public Dimension getPreferredSize() { if (client != null) { ScreenBuffer sb = client.getScreenBuffer(); - // Always size to the MAXIMUM (alternate) screen dimensions, - // matching how x3270 works — the window shows the full model size - int displayCols = sb.getMaxCols(); - int displayRows = sb.getMaxRows(); + // Use the CURRENT screen dimensions (not max) to eliminate extra space. + // When the host switches to alternate screen, onScreenSizeChanged fires + // and the frame re-packs. + int displayCols = sb.getCols(); + int displayRows = sb.getRows(); return new Dimension(displayCols * cellWidth + padding * 2, displayRows * cellHeight + padding * 2); } @@ -427,11 +764,14 @@ public class TerminalPanel extends JPanel { g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); - // Manually clear the entire graphics bounds to ensure no ghosting on - // resize/clip + // Clear entire panel with background color g2.setColor(bgColor); g2.fillRect(0, 0, getWidth(), getHeight()); + // Compute centered offsets for the grid + int ox = getRenderOffsetX(); + int oy = getRenderOffsetY(); + ScreenBuffer sb = client.getScreenBuffer(); int rows = sb.getRows(); int cols = sb.getCols(); @@ -446,8 +786,8 @@ public class TerminalPanel extends JPanel { int baddr = row * cols + col; ExtendedAttribute ea = sb.getCell(baddr); - int x = padding + col * cellWidth; - int y = padding + row * cellHeight; + int x = ox + col * cellWidth; + int y = oy + row * cellHeight; // Determine colors and attributes Color fgColor; @@ -462,6 +802,11 @@ public class TerminalPanel extends JPanel { // Field attributes display as blanks g2.setColor(this.bgColor); g2.fillRect(x, y, cellWidth, cellHeight); + // Selection highlight on field attribute cells too + if (isCellSelected(row, col)) { + g2.setColor(SELECTION_COLOR); + g2.fillRect(x, y, cellWidth, cellHeight); + } continue; } @@ -493,6 +838,10 @@ public class TerminalPanel extends JPanel { if (faIsZero(currentFA & 0xFF)) { g2.setColor(this.bgColor); g2.fillRect(x, y, cellWidth, cellHeight); + if (isCellSelected(row, col)) { + g2.setColor(SELECTION_COLOR); + g2.fillRect(x, y, cellWidth, cellHeight); + } continue; } @@ -522,6 +871,12 @@ public class TerminalPanel extends JPanel { g2.drawLine(x, y + cellHeight - fontDescent, x + cellWidth - 1, y + cellHeight - fontDescent); } + + // Draw selection highlight + if (isCellSelected(row, col)) { + g2.setColor(SELECTION_COLOR); + g2.fillRect(x, y, cellWidth, cellHeight); + } } } @@ -530,8 +885,8 @@ public class TerminalPanel extends JPanel { int curAddr = sb.getCursorAddress(); int curRow = curAddr / cols; int curCol = curAddr % cols; - int cx = padding + curCol * cellWidth; - int cy = padding + curRow * cellHeight; + int cx = ox + curCol * cellWidth; + int cy = oy + curRow * cellHeight; g2.setColor(new Color(255, 255, 255, 180)); g2.setXORMode(bgColor); diff --git a/run.sh b/run.sh index e877d9f..36a7539 100755 --- a/run.sh +++ b/run.sh @@ -39,6 +39,7 @@ echo "Executable JAR created at: $BUILD_DIR/j3270.jar" # --------------------------------------------------------- # Run the newly packaged JAR # --------------------------------------------------------- -exec java -Dapple.laf.useScreenMenuBar=true \ - -Dapple.awt.application.name=j3270 \ - -jar "$BUILD_DIR/j3270.jar" "$@" \ No newline at end of file +#exec java -Dapple.laf.useScreenMenuBar=true \ +# -Dapple.awt.application.name=j3270 \ +# -jar "$BUILD_DIR/j3270.jar" "$@" +nohup java -jar build/j3270.jar &