From 00a040670d26a2be7e67e4fc69af7bddbb911cbd Mon Sep 17 00:00:00 2001 From: takatsugu-nakayama Date: Tue, 23 Dec 2025 14:02:24 +0900 Subject: [PATCH 01/15] feat: integrate FlatLaf for modern UI and add NativeFileChooser for file dialogs --- Makefile | 21 ++ build.gradle | 4 + .../core/packetproxy/gui/GUIFilterConfig.java | 13 +- .../java/core/packetproxy/gui/GUIMain.java | 22 +- .../java/core/packetproxy/gui/GUIMenu.java | 23 +- .../gui/GUIOptionClientCertificateDialog.java | 27 +- ...nImportCertificateAndPrivateKeyDialog.java | 72 +++-- .../gui/GUIProjectChooserDialog.java | 10 +- .../packetproxy/gui/NativeFileChooser.java | 285 ++++++++++++++++++ .../gui/WriteFileChooserWrapper.java | 71 ++--- .../packetproxy/ppcontextmenu/SampleItem.java | 24 +- 11 files changed, 438 insertions(+), 134 deletions(-) create mode 100644 Makefile create mode 100644 src/main/java/core/packetproxy/gui/NativeFileChooser.java diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..f9079f26 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ + +JAVA_PATH := /opt/homebrew/Cellar/openjdk@21/21.0.8/libexec/openjdk.jdk/Contents/Home + +.PHONY: build + + +build: + ./gradlew assemble + +rebuild: + ./gradlew clean + ./gradlew assemble + +clean: + ./gradlew clean + +run: + RESTORE_HISTORY=y ./gradlew run + +run-clean: + RESTORE_HISTORY=n ./gradlew run \ No newline at end of file diff --git a/build.gradle b/build.gradle index 2b113ddc..8c71d52a 100644 --- a/build.gradle +++ b/build.gradle @@ -37,6 +37,10 @@ dependencies { testImplementation 'org.mockito:mockito-core:4.7.0' testImplementation 'org.projectlombok:lombok:1.18.42' + // FlatLaf - Modern Look and Feel + implementation 'com.formdev:flatlaf:3.4.1' + implementation 'com.formdev:flatlaf-intellij-themes:3.4.1' + implementation 'com.google.guava:guava:24.1-jre' implementation 'commons-codec:commons-codec:1.6' implementation 'commons-io:commons-io:2.4' diff --git a/src/main/java/core/packetproxy/gui/GUIFilterConfig.java b/src/main/java/core/packetproxy/gui/GUIFilterConfig.java index 9d5cd8d5..8c004b08 100644 --- a/src/main/java/core/packetproxy/gui/GUIFilterConfig.java +++ b/src/main/java/core/packetproxy/gui/GUIFilterConfig.java @@ -25,15 +25,12 @@ import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JComponent; -import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; -import javax.swing.SwingUtilities; -import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; @@ -262,12 +259,12 @@ public void mouseClicked(MouseEvent e) { public void mouseClicked(MouseEvent e) { try { - JFileChooser filechooser = new JFileChooser(); + NativeFileChooser filechooser = new NativeFileChooser(); filechooser.setCurrentDirectory(new File(defaultDir)); - filechooser.setFileFilter(new FileNameExtensionFilter("*.json", "json")); - filechooser.setFileSelectionMode(JFileChooser.FILES_ONLY); - int selected = filechooser.showOpenDialog(SwingUtilities.getRoot(owner)); - if (selected == JFileChooser.APPROVE_OPTION) { + filechooser.addChoosableFileFilter("*.json", "json"); + filechooser.setAcceptAllFileFilterUsed(false); + int selected = filechooser.showOpenDialog(owner); + if (selected == NativeFileChooser.APPROVE_OPTION) { File file = filechooser.getSelectedFile(); byte[] jbytes = Utils.readfile(file.getAbsolutePath()); diff --git a/src/main/java/core/packetproxy/gui/GUIMain.java b/src/main/java/core/packetproxy/gui/GUIMain.java index d0758e13..480d54b3 100644 --- a/src/main/java/core/packetproxy/gui/GUIMain.java +++ b/src/main/java/core/packetproxy/gui/GUIMain.java @@ -26,6 +26,7 @@ import java.beans.PropertyChangeListener; import javax.swing.*; import javax.swing.UIManager.LookAndFeelInfo; +import com.formdev.flatlaf.FlatIntelliJLaf; import javax.swing.text.DefaultEditorKit; import javax.swing.text.JTextComponent; import javax.swing.text.Keymap; @@ -161,12 +162,16 @@ private void setLookandFeel() throws Exception { System.setProperty("swing.aatext", "true"); } - for (LookAndFeelInfo clInfo : UIManager.getInstalledLookAndFeels()) { - - if ("Nimbus".equals(clInfo.getName())) { - - UIManager.setLookAndFeel(clInfo.getClassName()); - break; + // FlatLaf Modern Light Theme (IntelliJ) + try { + FlatIntelliJLaf.setup(); + } catch (Exception e) { + // Fallback to Nimbus if FlatLaf fails + for (LookAndFeelInfo clInfo : UIManager.getInstalledLookAndFeels()) { + if ("Nimbus".equals(clInfo.getName())) { + UIManager.setLookAndFeel(clInfo.getClassName()); + break; + } } } @@ -290,7 +295,7 @@ private void enableFullScreenForMac(Window window) throws Exception { // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6939001 private void setInterceptHighLight() { JLabel label = new JLabel(tabbedpane.getTitleAt(1)); - label.setForeground(Color.ORANGE); + label.setForeground(new Color(255, 180, 0)); // Bright orange for dark theme tabbedpane.setTabComponentAt(1, label); tabbedpane.revalidate(); tabbedpane.repaint(); @@ -298,7 +303,8 @@ private void setInterceptHighLight() { private void setInterceptDownLight() { JLabel label = new JLabel(tabbedpane.getTitleAt(1)); - label.setForeground(Color.BLACK); + // Use default foreground color from Look and Feel + label.setForeground(UIManager.getColor("TabbedPane.foreground")); tabbedpane.setTabComponentAt(1, label); tabbedpane.revalidate(); tabbedpane.repaint(); diff --git a/src/main/java/core/packetproxy/gui/GUIMenu.java b/src/main/java/core/packetproxy/gui/GUIMenu.java index 1e32f628..7b475372 100644 --- a/src/main/java/core/packetproxy/gui/GUIMenu.java +++ b/src/main/java/core/packetproxy/gui/GUIMenu.java @@ -21,14 +21,11 @@ import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.io.File; -import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; -import javax.swing.SwingUtilities; -import javax.swing.filechooser.FileNameExtensionFilter; import packetproxy.common.ConfigIO; import packetproxy.common.I18nString; import packetproxy.common.RecentProjectsStore; @@ -131,12 +128,12 @@ public void onError() { public void actionPerformed(ActionEvent e) { try { - JFileChooser filechooser = new JFileChooser(); + NativeFileChooser filechooser = new NativeFileChooser(); filechooser.setCurrentDirectory(new File(defaultDir)); - filechooser.setFileFilter(new FileNameExtensionFilter("*.sqlite3", "sqlite3")); - filechooser.setFileSelectionMode(JFileChooser.FILES_ONLY); - int selected = filechooser.showOpenDialog(SwingUtilities.getRoot(self)); - if (selected == JFileChooser.APPROVE_OPTION) { + filechooser.addChoosableFileFilter("*.sqlite3", "sqlite3"); + filechooser.setAcceptAllFileFilterUsed(false); + int selected = filechooser.showOpenDialog(owner); + if (selected == NativeFileChooser.APPROVE_OPTION) { File file = filechooser.getSelectedFile(); Database.getInstance().Load(file.getAbsolutePath()); @@ -261,12 +258,12 @@ public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) { try { - JFileChooser filechooser = new JFileChooser(); + NativeFileChooser filechooser = new NativeFileChooser(); filechooser.setCurrentDirectory(new File(defaultDir)); - filechooser.setFileFilter(new FileNameExtensionFilter("*.json", "json")); - filechooser.setFileSelectionMode(JFileChooser.FILES_ONLY); - int selected = filechooser.showOpenDialog(SwingUtilities.getRoot(self)); - if (selected == JFileChooser.APPROVE_OPTION) { + filechooser.addChoosableFileFilter("*.json", "json"); + filechooser.setAcceptAllFileFilterUsed(false); + int selected = filechooser.showOpenDialog(owner); + if (selected == NativeFileChooser.APPROVE_OPTION) { File file = filechooser.getSelectedFile(); byte[] jbytes = Utils.readfile(file.getAbsolutePath()); diff --git a/src/main/java/core/packetproxy/gui/GUIOptionClientCertificateDialog.java b/src/main/java/core/packetproxy/gui/GUIOptionClientCertificateDialog.java index b9ad46d6..290798cc 100644 --- a/src/main/java/core/packetproxy/gui/GUIOptionClientCertificateDialog.java +++ b/src/main/java/core/packetproxy/gui/GUIOptionClientCertificateDialog.java @@ -29,14 +29,12 @@ import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JDialog; -import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JTextField; -import javax.swing.filechooser.FileNameExtensionFilter; import packetproxy.common.I18nString; import packetproxy.model.ClientCertificate; import packetproxy.model.ClientCertificates; @@ -54,7 +52,7 @@ public class GUIOptionClientCertificateDialog extends JDialog { private JComboBox certificateTypeCombo = new JComboBox(); private JTextField certificatePathField = new JTextField(); - private JFileChooser certFilePath = new JFileChooser(); + private NativeFileChooser certFilePath = new NativeFileChooser(); private JPasswordField storePasswordField = new JPasswordField(); private JPasswordField keyPasswordField = new JPasswordField(); private JComboBox serverCombo = new JComboBox(); @@ -119,22 +117,24 @@ private JComponent createCertificateTypeSetting() { if (e.getStateChange() == ItemEvent.SELECTED) { String t = (String) e.getItem(); - certFilePath.resetChoosableFileFilters(); + certFilePath = new NativeFileChooser(); + certFilePath.setAcceptAllFileFilterUsed(false); switch (ClientCertificate.Type.getTypeFromText(t)) { case JKS : certFilePath.addChoosableFileFilter( - new FileNameExtensionFilter(I18nString.get("Client Certificate file (*.jks)"), "jks")); + I18nString.get("Client Certificate file (*.jks)"), "jks"); break; case P12 : - certFilePath.addChoosableFileFilter(new FileNameExtensionFilter( - I18nString.get("Client Certificate file (*.p12, *.pfx)"), "p12", "pfx")); + certFilePath.addChoosableFileFilter( + I18nString.get("Client Certificate file (*.p12, *.pfx)"), "p12", "pfx"); break; default : } } }); certFilePath.addChoosableFileFilter( - new FileNameExtensionFilter(I18nString.get("Client Certificate file (*.p12, *.pfx)"), "p12", "pfx")); + I18nString.get("Client Certificate file (*.p12, *.pfx)"), "p12", "pfx"); + certFilePath.setAcceptAllFileFilterUsed(false); return label_and_object(I18nString.get("Type of certificate file:"), certificateTypeCombo); } @@ -147,12 +147,13 @@ private JComponent createCertificatePathSetting() { button.addActionListener(arg0 -> { try { - certFilePath.setAcceptAllFileFilterUsed(false); - certFilePath.showOpenDialog(panel); - File file = certFilePath.getSelectedFile(); - if (file != null) { + int selected = certFilePath.showOpenDialog(owner); + if (selected == NativeFileChooser.APPROVE_OPTION) { + File file = certFilePath.getSelectedFile(); + if (file != null) { - certificatePathField.setText(file.getPath()); + certificatePathField.setText(file.getPath()); + } } } catch (Exception e) { diff --git a/src/main/java/core/packetproxy/gui/GUIOptionImportCertificateAndPrivateKeyDialog.java b/src/main/java/core/packetproxy/gui/GUIOptionImportCertificateAndPrivateKeyDialog.java index 2b6cd3b0..d7bcc470 100644 --- a/src/main/java/core/packetproxy/gui/GUIOptionImportCertificateAndPrivateKeyDialog.java +++ b/src/main/java/core/packetproxy/gui/GUIOptionImportCertificateAndPrivateKeyDialog.java @@ -28,7 +28,6 @@ import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; -import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; @@ -36,7 +35,6 @@ import javax.swing.JPasswordField; import javax.swing.JRadioButton; import javax.swing.JTextField; -import javax.swing.filechooser.FileNameExtensionFilter; import packetproxy.common.I18nString; import packetproxy.model.CAs.PacketProxyCAPerUser; @@ -140,18 +138,20 @@ private JPanel createImportPEMPanel() { JTextField certificatePEMPathField = new JTextField(); certificatePanel.add(certificatePEMPathField); JButton certificateChoosebutton = new JButton(I18nString.get("choose...")); - JFileChooser certificatePEMChooser = new JFileChooser(); certificateChoosebutton.addActionListener(arg0 -> { try { + NativeFileChooser certificatePEMChooser = new NativeFileChooser(); certificatePEMChooser.addChoosableFileFilter( - new FileNameExtensionFilter(I18nString.get("Certificate file (*.crt, *.pem)"), "crt", "pem")); + I18nString.get("Certificate file (*.crt, *.pem)"), "crt", "pem"); certificatePEMChooser.setAcceptAllFileFilterUsed(false); - certificatePEMChooser.showOpenDialog(panel); - File file = certificatePEMChooser.getSelectedFile(); - if (file != null) { + int selected = certificatePEMChooser.showOpenDialog(panel); + if (selected == NativeFileChooser.APPROVE_OPTION) { + File file = certificatePEMChooser.getSelectedFile(); + if (file != null) { - certificatePEMPathField.setText(file.getPath()); + certificatePEMPathField.setText(file.getPath()); + } } } catch (Exception e) { @@ -166,18 +166,20 @@ private JPanel createImportPEMPanel() { JTextField privateKeyPEMPathField = new JTextField(); privateKeyPanel.add(privateKeyPEMPathField); JButton privateKeyChoosebutton = new JButton(I18nString.get("choose...")); - JFileChooser privateKeyPEMChooser = new JFileChooser(); privateKeyChoosebutton.addActionListener(arg0 -> { try { + NativeFileChooser privateKeyPEMChooser = new NativeFileChooser(); privateKeyPEMChooser.addChoosableFileFilter( - new FileNameExtensionFilter(I18nString.get("Private Key file (*.key, *.pem)"), "key", "pem")); + I18nString.get("Private Key file (*.key, *.pem)"), "key", "pem"); privateKeyPEMChooser.setAcceptAllFileFilterUsed(false); - privateKeyPEMChooser.showOpenDialog(panel); - File file = privateKeyPEMChooser.getSelectedFile(); - if (file != null) { + int selected = privateKeyPEMChooser.showOpenDialog(panel); + if (selected == NativeFileChooser.APPROVE_OPTION) { + File file = privateKeyPEMChooser.getSelectedFile(); + if (file != null) { - privateKeyPEMPathField.setText(file.getPath()); + privateKeyPEMPathField.setText(file.getPath()); + } } } catch (Exception e) { @@ -231,18 +233,20 @@ private JPanel createImportDERPanel() { JTextField certificateDERPathField = new JTextField(); certificatePanel.add(certificateDERPathField); JButton certificateChoosebutton = new JButton(I18nString.get("choose...")); - JFileChooser certificateDERChooser = new JFileChooser(); certificateChoosebutton.addActionListener(arg0 -> { try { + NativeFileChooser certificateDERChooser = new NativeFileChooser(); certificateDERChooser.addChoosableFileFilter( - new FileNameExtensionFilter(I18nString.get("Certificate file (*.crt, *.der)"), "crt", "der")); + I18nString.get("Certificate file (*.crt, *.der)"), "crt", "der"); certificateDERChooser.setAcceptAllFileFilterUsed(false); - certificateDERChooser.showOpenDialog(panel); - File file = certificateDERChooser.getSelectedFile(); - if (file != null) { + int selected = certificateDERChooser.showOpenDialog(panel); + if (selected == NativeFileChooser.APPROVE_OPTION) { + File file = certificateDERChooser.getSelectedFile(); + if (file != null) { - certificateDERPathField.setText(file.getPath()); + certificateDERPathField.setText(file.getPath()); + } } } catch (Exception e) { @@ -257,18 +261,20 @@ private JPanel createImportDERPanel() { JTextField privateKeyDERPathField = new JTextField(); privateKeyPanel.add(privateKeyDERPathField); JButton privateKeyChoosebutton = new JButton(I18nString.get("choose...")); - JFileChooser privateKeyDERChooser = new JFileChooser(); privateKeyChoosebutton.addActionListener(arg0 -> { try { + NativeFileChooser privateKeyDERChooser = new NativeFileChooser(); privateKeyDERChooser.addChoosableFileFilter( - new FileNameExtensionFilter(I18nString.get("Private Key file (*.key, *.der)"), "key", "der")); + I18nString.get("Private Key file (*.key, *.der)"), "key", "der"); privateKeyDERChooser.setAcceptAllFileFilterUsed(false); - privateKeyDERChooser.showOpenDialog(panel); - File file = privateKeyDERChooser.getSelectedFile(); - if (file != null) { + int selected = privateKeyDERChooser.showOpenDialog(panel); + if (selected == NativeFileChooser.APPROVE_OPTION) { + File file = privateKeyDERChooser.getSelectedFile(); + if (file != null) { - privateKeyDERPathField.setText(file.getPath()); + privateKeyDERPathField.setText(file.getPath()); + } } } catch (Exception e) { @@ -322,18 +328,20 @@ private JPanel createImportP12Panel() { JTextField p12PathField = new JTextField(); p12Panel.add(p12PathField); JButton p12Choosebutton = new JButton(I18nString.get("choose...")); - JFileChooser p12Chooser = new JFileChooser(); p12Choosebutton.addActionListener(arg0 -> { try { + NativeFileChooser p12Chooser = new NativeFileChooser(); p12Chooser.addChoosableFileFilter( - new FileNameExtensionFilter(I18nString.get("P12 file (*.p12, *.pfx)"), "p12", "pfx")); + I18nString.get("P12 file (*.p12, *.pfx)"), "p12", "pfx"); p12Chooser.setAcceptAllFileFilterUsed(false); - p12Chooser.showOpenDialog(panel); - File file = p12Chooser.getSelectedFile(); - if (file != null) { + int selected = p12Chooser.showOpenDialog(panel); + if (selected == NativeFileChooser.APPROVE_OPTION) { + File file = p12Chooser.getSelectedFile(); + if (file != null) { - p12PathField.setText(file.getPath()); + p12PathField.setText(file.getPath()); + } } } catch (Exception e) { diff --git a/src/main/java/core/packetproxy/gui/GUIProjectChooserDialog.java b/src/main/java/core/packetproxy/gui/GUIProjectChooserDialog.java index fa2fe556..be25f6ab 100644 --- a/src/main/java/core/packetproxy/gui/GUIProjectChooserDialog.java +++ b/src/main/java/core/packetproxy/gui/GUIProjectChooserDialog.java @@ -32,7 +32,6 @@ import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JDialog; -import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; @@ -43,7 +42,6 @@ import javax.swing.UIManager; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; -import javax.swing.filechooser.FileNameExtensionFilter; import packetproxy.common.I18nString; public class GUIProjectChooserDialog { @@ -375,11 +373,11 @@ private void updateButton() { } private boolean openByFileChooser(Component parent) throws Exception { - var filechooser = new JFileChooser(); - filechooser.setFileFilter(new FileNameExtensionFilter("*.sqlite3", "sqlite3")); - filechooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + var filechooser = new NativeFileChooser(); + filechooser.addChoosableFileFilter("*.sqlite3", "sqlite3"); + filechooser.setAcceptAllFileFilterUsed(false); var ok = filechooser.showOpenDialog(parent); - if (ok != JFileChooser.APPROVE_OPTION) { + if (ok != NativeFileChooser.APPROVE_OPTION) { return false; } var file = filechooser.getSelectedFile(); diff --git a/src/main/java/core/packetproxy/gui/NativeFileChooser.java b/src/main/java/core/packetproxy/gui/NativeFileChooser.java new file mode 100644 index 00000000..a5b1c065 --- /dev/null +++ b/src/main/java/core/packetproxy/gui/NativeFileChooser.java @@ -0,0 +1,285 @@ +/* + * Copyright 2019 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.gui; + +import java.awt.Component; +import java.awt.FileDialog; +import java.awt.Frame; +import java.io.File; +import java.io.FilenameFilter; +import java.util.ArrayList; +import java.util.List; +import javax.swing.JFileChooser; +import javax.swing.SwingUtilities; +import javax.swing.filechooser.FileNameExtensionFilter; +import packetproxy.util.PacketProxyUtility; + +/** + * A file chooser that uses the native file dialog on Mac (Finder) + * and falls back to JFileChooser on other platforms. + */ +public class NativeFileChooser { + + public static final int APPROVE_OPTION = JFileChooser.APPROVE_OPTION; + public static final int CANCEL_OPTION = JFileChooser.CANCEL_OPTION; + public static final int ERROR_OPTION = JFileChooser.ERROR_OPTION; + + private File selectedFile; + private File currentDirectory; + private String dialogTitle; + private List fileFilters = new ArrayList<>(); + private boolean acceptAllFileFilterUsed = true; + + public NativeFileChooser() { + this.currentDirectory = new File(System.getProperty("user.home")); + } + + public NativeFileChooser(String currentDirectoryPath) { + this.currentDirectory = new File(currentDirectoryPath); + } + + public void setCurrentDirectory(File dir) { + this.currentDirectory = dir; + } + + public void setDialogTitle(String title) { + this.dialogTitle = title; + } + + public void setAcceptAllFileFilterUsed(boolean b) { + this.acceptAllFileFilterUsed = b; + } + + /** + * Add a file filter with description and extensions. + * @param description The description (e.g., "*.sqlite3") + * @param extensions The file extensions without dots (e.g., "sqlite3", "json") + */ + public void addChoosableFileFilter(String description, String... extensions) { + fileFilters.add(extensions); + } + + /** + * Set file filter using FileNameExtensionFilter for compatibility. + */ + public void setFileFilter(FileNameExtensionFilter filter) { + fileFilters.clear(); + fileFilters.add(filter.getExtensions()); + } + + /** + * Add file filter using FileNameExtensionFilter for compatibility. + */ + public void addChoosableFileFilter(FileNameExtensionFilter filter) { + fileFilters.add(filter.getExtensions()); + } + + public File getSelectedFile() { + return selectedFile; + } + + public void setSelectedFile(File file) { + this.selectedFile = file; + } + + /** + * Show an open dialog. + * @param parent The parent component + * @return APPROVE_OPTION if a file was selected, CANCEL_OPTION otherwise + */ + public int showOpenDialog(Component parent) { + if (PacketProxyUtility.getInstance().isMac()) { + return showNativeOpenDialog(parent); + } else { + return showSwingOpenDialog(parent); + } + } + + /** + * Show a save dialog. + * @param parent The parent component + * @return APPROVE_OPTION if a file was selected, CANCEL_OPTION otherwise + */ + public int showSaveDialog(Component parent) { + if (PacketProxyUtility.getInstance().isMac()) { + return showNativeSaveDialog(parent); + } else { + return showSwingSaveDialog(parent); + } + } + + private Frame getFrame(Component parent) { + if (parent instanceof Frame) { + return (Frame) parent; + } + return (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent); + } + + private FilenameFilter createFilenameFilter() { + if (fileFilters.isEmpty()) { + return null; + } + return (dir, name) -> { + if (acceptAllFileFilterUsed) { + return true; + } + String lowerName = name.toLowerCase(); + for (String[] extensions : fileFilters) { + for (String ext : extensions) { + if (lowerName.endsWith("." + ext.toLowerCase())) { + return true; + } + } + } + return false; + }; + } + + private int showNativeOpenDialog(Component parent) { + Frame frame = getFrame(parent); + FileDialog dialog = new FileDialog(frame, dialogTitle != null ? dialogTitle : "Open", FileDialog.LOAD); + + if (currentDirectory != null) { + dialog.setDirectory(currentDirectory.getAbsolutePath()); + } + + FilenameFilter filter = createFilenameFilter(); + if (filter != null && !acceptAllFileFilterUsed) { + dialog.setFilenameFilter(filter); + } + + // On Mac, we can set allowed file types for better native integration + if (!fileFilters.isEmpty()) { + StringBuilder allowedExtensions = new StringBuilder(); + for (String[] extensions : fileFilters) { + for (String ext : extensions) { + if (allowedExtensions.length() > 0) { + allowedExtensions.append(";"); + } + allowedExtensions.append("*.").append(ext); + } + } + dialog.setFile(allowedExtensions.toString()); + } + + dialog.setVisible(true); + + String file = dialog.getFile(); + String directory = dialog.getDirectory(); + + if (file != null && directory != null) { + selectedFile = new File(directory, file); + return APPROVE_OPTION; + } + + return CANCEL_OPTION; + } + + private int showNativeSaveDialog(Component parent) { + Frame frame = getFrame(parent); + FileDialog dialog = new FileDialog(frame, dialogTitle != null ? dialogTitle : "Save", FileDialog.SAVE); + + if (currentDirectory != null) { + dialog.setDirectory(currentDirectory.getAbsolutePath()); + } + + if (selectedFile != null) { + dialog.setFile(selectedFile.getName()); + } + + dialog.setVisible(true); + + String file = dialog.getFile(); + String directory = dialog.getDirectory(); + + if (file != null && directory != null) { + selectedFile = new File(directory, file); + return APPROVE_OPTION; + } + + return CANCEL_OPTION; + } + + private int showSwingOpenDialog(Component parent) { + JFileChooser chooser = new JFileChooser(); + + if (currentDirectory != null) { + chooser.setCurrentDirectory(currentDirectory); + } + + if (dialogTitle != null) { + chooser.setDialogTitle(dialogTitle); + } + + chooser.setAcceptAllFileFilterUsed(acceptAllFileFilterUsed); + + for (String[] extensions : fileFilters) { + if (extensions.length > 0) { + StringBuilder desc = new StringBuilder("*."); + desc.append(String.join(", *.", extensions)); + chooser.addChoosableFileFilter(new FileNameExtensionFilter(desc.toString(), extensions)); + } + } + + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + + int result = chooser.showOpenDialog(parent); + + if (result == JFileChooser.APPROVE_OPTION) { + selectedFile = chooser.getSelectedFile(); + return APPROVE_OPTION; + } + + return CANCEL_OPTION; + } + + private int showSwingSaveDialog(Component parent) { + JFileChooser chooser = new JFileChooser(); + + if (currentDirectory != null) { + chooser.setCurrentDirectory(currentDirectory); + } + + if (dialogTitle != null) { + chooser.setDialogTitle(dialogTitle); + } + + if (selectedFile != null) { + chooser.setSelectedFile(selectedFile); + } + + chooser.setAcceptAllFileFilterUsed(acceptAllFileFilterUsed); + + for (String[] extensions : fileFilters) { + if (extensions.length > 0) { + StringBuilder desc = new StringBuilder("*."); + desc.append(String.join(", *.", extensions)); + chooser.addChoosableFileFilter(new FileNameExtensionFilter(desc.toString(), extensions)); + } + } + + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + + int result = chooser.showSaveDialog(parent); + + if (result == JFileChooser.APPROVE_OPTION) { + selectedFile = chooser.getSelectedFile(); + return APPROVE_OPTION; + } + + return CANCEL_OPTION; + } +} diff --git a/src/main/java/core/packetproxy/gui/WriteFileChooserWrapper.java b/src/main/java/core/packetproxy/gui/WriteFileChooserWrapper.java index 5f9588ea..f597bb8b 100644 --- a/src/main/java/core/packetproxy/gui/WriteFileChooserWrapper.java +++ b/src/main/java/core/packetproxy/gui/WriteFileChooserWrapper.java @@ -18,15 +18,15 @@ import java.io.File; import java.util.EventListener; import javax.swing.*; -import javax.swing.filechooser.FileNameExtensionFilter; public class WriteFileChooserWrapper { private static int EVENTLISTENER_IS_ALREADY_EXISTS = -1; private static int EVENTLISTENER_IS_ADDED = -1; - private JFileChooser fileChooser; + private NativeFileChooser fileChooser; private JFrame owner; private String fileExtension; + private String currentDirectory; protected FileChooserListener listener = null; public WriteFileChooserWrapper(JFrame owner, String fileExtension) { @@ -40,48 +40,16 @@ public WriteFileChooserWrapper(JFrame owner, String fileExtension, String curren private void setFileChooser(JFrame owner, String fileExtension, String currentDirectory) { this.owner = owner; this.fileExtension = fileExtension; + this.currentDirectory = currentDirectory; - fileChooser = new JFileChooser() { - - @Override - public void approveSelection() { - File f = getSelectedFile(); - File file; - if (f.getName().matches(".+\\." + fileExtension)) { - - file = f; - } else { - - file = new File(f.getAbsolutePath() + "." + fileExtension); - } - if (file.exists() && getDialogType() == SAVE_DIALOG) { - - int result = JOptionPane.showConfirmDialog(this, "ファイルが既に存在しますが上書きしますか?", "Existing file", - JOptionPane.YES_NO_CANCEL_OPTION); - switch (result) { - case JOptionPane.YES_OPTION : - super.approveSelection(); - return; - case JOptionPane.NO_OPTION : - return; - case JOptionPane.CLOSED_OPTION : - return; - case JOptionPane.CANCEL_OPTION : - cancelSelection(); - return; - } - } - super.approveSelection(); - } - }; - fileChooser.setCurrentDirectory(new File(currentDirectory)); - fileChooser.setFileFilter(new FileNameExtensionFilter("*." + fileExtension, fileExtension)); - fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + fileChooser = new NativeFileChooser(currentDirectory); + fileChooser.addChoosableFileFilter("*." + fileExtension, fileExtension); + fileChooser.setAcceptAllFileFilterUsed(false); } public void showSaveDialog() { - int selected = fileChooser.showSaveDialog(SwingUtilities.getRoot(owner)); - if (selected == JFileChooser.APPROVE_OPTION) { + int selected = fileChooser.showSaveDialog(owner); + if (selected == NativeFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFile(); if (null != listener) { @@ -94,16 +62,33 @@ public void showSaveDialog() { filePath = file.getAbsolutePath() + "." + fileExtension; } - listener.onApproved(new File(filePath), fileExtension); + + File finalFile = new File(filePath); + if (finalFile.exists()) { + int result = JOptionPane.showConfirmDialog(owner, "ファイルが既に存在しますが上書きしますか?", "Existing file", + JOptionPane.YES_NO_CANCEL_OPTION); + switch (result) { + case JOptionPane.YES_OPTION: + listener.onApproved(finalFile, fileExtension); + return; + case JOptionPane.NO_OPTION: + case JOptionPane.CLOSED_OPTION: + return; + case JOptionPane.CANCEL_OPTION: + listener.onCanceled(); + return; + } + } + listener.onApproved(finalFile, fileExtension); } - } else if (selected == JFileChooser.CANCEL_OPTION) { + } else if (selected == NativeFileChooser.CANCEL_OPTION) { if (null != listener) { listener.onCanceled(); } - } else if (selected == JFileChooser.ERROR_OPTION) { + } else if (selected == NativeFileChooser.ERROR_OPTION) { if (null != listener) { diff --git a/src/main/java/core/packetproxy/ppcontextmenu/SampleItem.java b/src/main/java/core/packetproxy/ppcontextmenu/SampleItem.java index 13ee8cfa..ab53d08e 100644 --- a/src/main/java/core/packetproxy/ppcontextmenu/SampleItem.java +++ b/src/main/java/core/packetproxy/ppcontextmenu/SampleItem.java @@ -16,12 +16,11 @@ package packetproxy.ppcontextmenu; import java.io.File; -import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JOptionPane; -import javax.swing.filechooser.FileNameExtensionFilter; import org.apache.commons.io.FileUtils; import packetproxy.gui.GUIPacket; +import packetproxy.gui.NativeFileChooser; public class SampleItem extends PPContextMenu { @@ -32,15 +31,18 @@ public String getLabelName() { @Override public void action() throws Exception { - JFileChooser saveFile = new JFileChooser("packet.dat"); + NativeFileChooser saveFile = new NativeFileChooser(); saveFile.setAcceptAllFileFilterUsed(false); - saveFile.addChoosableFileFilter(new FileNameExtensionFilter("データファイル (.dat)", "dat")); - saveFile.showSaveDialog((JFrame) this.dependentData.get("main_frame")); - File file = saveFile.getSelectedFile(); - GUIPacket gui_packet = (GUIPacket) this.dependentData.get("gui_packet"); - byte[] data = gui_packet.getPacket().getReceivedData(); - FileUtils.writeByteArrayToFile(file, data); - JOptionPane.showMessageDialog((JFrame) this.dependentData.get("main_frame"), - String.format("%sに保存しました!", file.getPath())); + saveFile.addChoosableFileFilter("データファイル (.dat)", "dat"); + JFrame mainFrame = (JFrame) this.dependentData.get("main_frame"); + int selected = saveFile.showSaveDialog(mainFrame); + if (selected == NativeFileChooser.APPROVE_OPTION) { + File file = saveFile.getSelectedFile(); + GUIPacket gui_packet = (GUIPacket) this.dependentData.get("gui_packet"); + byte[] data = gui_packet.getPacket().getReceivedData(); + FileUtils.writeByteArrayToFile(file, data); + JOptionPane.showMessageDialog(mainFrame, + String.format("%sに保存しました!", file.getPath())); + } } } From 11ed822ed1eb6b9398086e35ce0e15a62e76d29b Mon Sep 17 00:00:00 2001 From: takatsugu-nakayama Date: Thu, 25 Dec 2025 16:11:19 +0900 Subject: [PATCH 02/15] chore: remove Makefile as build process is now handled by Gradle --- Makefile | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 Makefile diff --git a/Makefile b/Makefile deleted file mode 100644 index f9079f26..00000000 --- a/Makefile +++ /dev/null @@ -1,21 +0,0 @@ - -JAVA_PATH := /opt/homebrew/Cellar/openjdk@21/21.0.8/libexec/openjdk.jdk/Contents/Home - -.PHONY: build - - -build: - ./gradlew assemble - -rebuild: - ./gradlew clean - ./gradlew assemble - -clean: - ./gradlew clean - -run: - RESTORE_HISTORY=y ./gradlew run - -run-clean: - RESTORE_HISTORY=n ./gradlew run \ No newline at end of file From c4a9634cfedc05224a77eb309f40b255db573225 Mon Sep 17 00:00:00 2001 From: takatsugu-nakayama Date: Thu, 25 Dec 2025 16:42:28 +0900 Subject: [PATCH 03/15] =?UTF-8?q?deskription=E3=81=8C=E7=84=A1=E8=A6=96?= =?UTF-8?q?=E3=81=95=E3=82=8C=E3=81=A6=E3=81=84=E3=81=9F=E3=81=AE=E3=81=A7?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../packetproxy/gui/NativeFileChooser.java | 59 +++++++++---------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/main/java/core/packetproxy/gui/NativeFileChooser.java b/src/main/java/core/packetproxy/gui/NativeFileChooser.java index a5b1c065..0fb91224 100644 --- a/src/main/java/core/packetproxy/gui/NativeFileChooser.java +++ b/src/main/java/core/packetproxy/gui/NativeFileChooser.java @@ -37,10 +37,23 @@ public class NativeFileChooser { public static final int CANCEL_OPTION = JFileChooser.CANCEL_OPTION; public static final int ERROR_OPTION = JFileChooser.ERROR_OPTION; + /** + * Internal class to store file filter description and extensions together. + */ + private static class FilterEntry { + final String description; + final String[] extensions; + + FilterEntry(String description, String[] extensions) { + this.description = description; + this.extensions = extensions; + } + } + private File selectedFile; private File currentDirectory; private String dialogTitle; - private List fileFilters = new ArrayList<>(); + private List fileFilters = new ArrayList<>(); private boolean acceptAllFileFilterUsed = true; public NativeFileChooser() { @@ -65,11 +78,11 @@ public void setAcceptAllFileFilterUsed(boolean b) { /** * Add a file filter with description and extensions. - * @param description The description (e.g., "*.sqlite3") + * @param description The description (e.g., "*.sqlite3", "Client Certificate file (*.jks)") * @param extensions The file extensions without dots (e.g., "sqlite3", "json") */ public void addChoosableFileFilter(String description, String... extensions) { - fileFilters.add(extensions); + fileFilters.add(new FilterEntry(description, extensions)); } /** @@ -77,14 +90,14 @@ public void addChoosableFileFilter(String description, String... extensions) { */ public void setFileFilter(FileNameExtensionFilter filter) { fileFilters.clear(); - fileFilters.add(filter.getExtensions()); + fileFilters.add(new FilterEntry(filter.getDescription(), filter.getExtensions())); } /** * Add file filter using FileNameExtensionFilter for compatibility. */ public void addChoosableFileFilter(FileNameExtensionFilter filter) { - fileFilters.add(filter.getExtensions()); + fileFilters.add(new FilterEntry(filter.getDescription(), filter.getExtensions())); } public File getSelectedFile() { @@ -137,8 +150,8 @@ private FilenameFilter createFilenameFilter() { return true; } String lowerName = name.toLowerCase(); - for (String[] extensions : fileFilters) { - for (String ext : extensions) { + for (FilterEntry entry : fileFilters) { + for (String ext : entry.extensions) { if (lowerName.endsWith("." + ext.toLowerCase())) { return true; } @@ -156,25 +169,13 @@ private int showNativeOpenDialog(Component parent) { dialog.setDirectory(currentDirectory.getAbsolutePath()); } + // Use FilenameFilter for filtering files by extension + // Note: setFile() should NOT be used for filtering as it sets the filename field, not the filter FilenameFilter filter = createFilenameFilter(); if (filter != null && !acceptAllFileFilterUsed) { dialog.setFilenameFilter(filter); } - // On Mac, we can set allowed file types for better native integration - if (!fileFilters.isEmpty()) { - StringBuilder allowedExtensions = new StringBuilder(); - for (String[] extensions : fileFilters) { - for (String ext : extensions) { - if (allowedExtensions.length() > 0) { - allowedExtensions.append(";"); - } - allowedExtensions.append("*.").append(ext); - } - } - dialog.setFile(allowedExtensions.toString()); - } - dialog.setVisible(true); String file = dialog.getFile(); @@ -226,11 +227,9 @@ private int showSwingOpenDialog(Component parent) { chooser.setAcceptAllFileFilterUsed(acceptAllFileFilterUsed); - for (String[] extensions : fileFilters) { - if (extensions.length > 0) { - StringBuilder desc = new StringBuilder("*."); - desc.append(String.join(", *.", extensions)); - chooser.addChoosableFileFilter(new FileNameExtensionFilter(desc.toString(), extensions)); + for (FilterEntry entry : fileFilters) { + if (entry.extensions.length > 0) { + chooser.addChoosableFileFilter(new FileNameExtensionFilter(entry.description, entry.extensions)); } } @@ -263,11 +262,9 @@ private int showSwingSaveDialog(Component parent) { chooser.setAcceptAllFileFilterUsed(acceptAllFileFilterUsed); - for (String[] extensions : fileFilters) { - if (extensions.length > 0) { - StringBuilder desc = new StringBuilder("*."); - desc.append(String.join(", *.", extensions)); - chooser.addChoosableFileFilter(new FileNameExtensionFilter(desc.toString(), extensions)); + for (FilterEntry entry : fileFilters) { + if (entry.extensions.length > 0) { + chooser.addChoosableFileFilter(new FileNameExtensionFilter(entry.description, entry.extensions)); } } From 240a535b0d99a5d2816cfad5c07f0f3dd02100fa Mon Sep 17 00:00:00 2001 From: takatsugu-nakayama Date: Thu, 25 Dec 2025 16:43:33 +0900 Subject: [PATCH 04/15] add null check --- src/main/java/core/packetproxy/gui/NativeFileChooser.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/core/packetproxy/gui/NativeFileChooser.java b/src/main/java/core/packetproxy/gui/NativeFileChooser.java index 0fb91224..bff5d5e2 100644 --- a/src/main/java/core/packetproxy/gui/NativeFileChooser.java +++ b/src/main/java/core/packetproxy/gui/NativeFileChooser.java @@ -134,7 +134,15 @@ public int showSaveDialog(Component parent) { } } + /** + * Get the Frame ancestor of the given component. + * @param parent The component to find the Frame ancestor for + * @return The Frame ancestor, or null if parent is null or no Frame ancestor exists + */ private Frame getFrame(Component parent) { + if (parent == null) { + return null; + } if (parent instanceof Frame) { return (Frame) parent; } From 22fbb9c9261eeb41c07690cea33e812a61caef54 Mon Sep 17 00:00:00 2001 From: takatsugu-nakayama Date: Thu, 25 Dec 2025 16:45:04 +0900 Subject: [PATCH 05/15] =?UTF-8?q?ERROR=5FOPTION=E3=82=92=E8=BF=94=E3=81=99?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../packetproxy/gui/NativeFileChooser.java | 200 ++++++++++-------- 1 file changed, 110 insertions(+), 90 deletions(-) diff --git a/src/main/java/core/packetproxy/gui/NativeFileChooser.java b/src/main/java/core/packetproxy/gui/NativeFileChooser.java index bff5d5e2..49d10ee4 100644 --- a/src/main/java/core/packetproxy/gui/NativeFileChooser.java +++ b/src/main/java/core/packetproxy/gui/NativeFileChooser.java @@ -170,121 +170,141 @@ private FilenameFilter createFilenameFilter() { } private int showNativeOpenDialog(Component parent) { - Frame frame = getFrame(parent); - FileDialog dialog = new FileDialog(frame, dialogTitle != null ? dialogTitle : "Open", FileDialog.LOAD); - - if (currentDirectory != null) { - dialog.setDirectory(currentDirectory.getAbsolutePath()); - } + try { + Frame frame = getFrame(parent); + FileDialog dialog = new FileDialog(frame, dialogTitle != null ? dialogTitle : "Open", FileDialog.LOAD); + + if (currentDirectory != null) { + dialog.setDirectory(currentDirectory.getAbsolutePath()); + } - // Use FilenameFilter for filtering files by extension - // Note: setFile() should NOT be used for filtering as it sets the filename field, not the filter - FilenameFilter filter = createFilenameFilter(); - if (filter != null && !acceptAllFileFilterUsed) { - dialog.setFilenameFilter(filter); - } + // Use FilenameFilter for filtering files by extension + // Note: setFile() should NOT be used for filtering as it sets the filename field, not the filter + FilenameFilter filter = createFilenameFilter(); + if (filter != null && !acceptAllFileFilterUsed) { + dialog.setFilenameFilter(filter); + } - dialog.setVisible(true); + dialog.setVisible(true); - String file = dialog.getFile(); - String directory = dialog.getDirectory(); - - if (file != null && directory != null) { - selectedFile = new File(directory, file); - return APPROVE_OPTION; + String file = dialog.getFile(); + String directory = dialog.getDirectory(); + + if (file != null && directory != null) { + selectedFile = new File(directory, file); + return APPROVE_OPTION; + } + + return CANCEL_OPTION; + } catch (Exception e) { + return ERROR_OPTION; } - - return CANCEL_OPTION; } private int showNativeSaveDialog(Component parent) { - Frame frame = getFrame(parent); - FileDialog dialog = new FileDialog(frame, dialogTitle != null ? dialogTitle : "Save", FileDialog.SAVE); - - if (currentDirectory != null) { - dialog.setDirectory(currentDirectory.getAbsolutePath()); - } + try { + Frame frame = getFrame(parent); + FileDialog dialog = new FileDialog(frame, dialogTitle != null ? dialogTitle : "Save", FileDialog.SAVE); + + if (currentDirectory != null) { + dialog.setDirectory(currentDirectory.getAbsolutePath()); + } - if (selectedFile != null) { - dialog.setFile(selectedFile.getName()); - } + if (selectedFile != null) { + dialog.setFile(selectedFile.getName()); + } - dialog.setVisible(true); + dialog.setVisible(true); - String file = dialog.getFile(); - String directory = dialog.getDirectory(); - - if (file != null && directory != null) { - selectedFile = new File(directory, file); - return APPROVE_OPTION; + String file = dialog.getFile(); + String directory = dialog.getDirectory(); + + if (file != null && directory != null) { + selectedFile = new File(directory, file); + return APPROVE_OPTION; + } + + return CANCEL_OPTION; + } catch (Exception e) { + return ERROR_OPTION; } - - return CANCEL_OPTION; } private int showSwingOpenDialog(Component parent) { - JFileChooser chooser = new JFileChooser(); - - if (currentDirectory != null) { - chooser.setCurrentDirectory(currentDirectory); - } - - if (dialogTitle != null) { - chooser.setDialogTitle(dialogTitle); - } + try { + JFileChooser chooser = new JFileChooser(); + + if (currentDirectory != null) { + chooser.setCurrentDirectory(currentDirectory); + } + + if (dialogTitle != null) { + chooser.setDialogTitle(dialogTitle); + } - chooser.setAcceptAllFileFilterUsed(acceptAllFileFilterUsed); - - for (FilterEntry entry : fileFilters) { - if (entry.extensions.length > 0) { - chooser.addChoosableFileFilter(new FileNameExtensionFilter(entry.description, entry.extensions)); + chooser.setAcceptAllFileFilterUsed(acceptAllFileFilterUsed); + + for (FilterEntry entry : fileFilters) { + if (entry.extensions.length > 0) { + chooser.addChoosableFileFilter(new FileNameExtensionFilter(entry.description, entry.extensions)); + } } - } - chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); - - int result = chooser.showOpenDialog(parent); - - if (result == JFileChooser.APPROVE_OPTION) { - selectedFile = chooser.getSelectedFile(); - return APPROVE_OPTION; + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + + int result = chooser.showOpenDialog(parent); + + if (result == JFileChooser.APPROVE_OPTION) { + selectedFile = chooser.getSelectedFile(); + return APPROVE_OPTION; + } else if (result == JFileChooser.ERROR_OPTION) { + return ERROR_OPTION; + } + + return CANCEL_OPTION; + } catch (Exception e) { + return ERROR_OPTION; } - - return CANCEL_OPTION; } private int showSwingSaveDialog(Component parent) { - JFileChooser chooser = new JFileChooser(); - - if (currentDirectory != null) { - chooser.setCurrentDirectory(currentDirectory); - } - - if (dialogTitle != null) { - chooser.setDialogTitle(dialogTitle); - } + try { + JFileChooser chooser = new JFileChooser(); + + if (currentDirectory != null) { + chooser.setCurrentDirectory(currentDirectory); + } + + if (dialogTitle != null) { + chooser.setDialogTitle(dialogTitle); + } - if (selectedFile != null) { - chooser.setSelectedFile(selectedFile); - } + if (selectedFile != null) { + chooser.setSelectedFile(selectedFile); + } - chooser.setAcceptAllFileFilterUsed(acceptAllFileFilterUsed); - - for (FilterEntry entry : fileFilters) { - if (entry.extensions.length > 0) { - chooser.addChoosableFileFilter(new FileNameExtensionFilter(entry.description, entry.extensions)); + chooser.setAcceptAllFileFilterUsed(acceptAllFileFilterUsed); + + for (FilterEntry entry : fileFilters) { + if (entry.extensions.length > 0) { + chooser.addChoosableFileFilter(new FileNameExtensionFilter(entry.description, entry.extensions)); + } } - } - chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); - - int result = chooser.showSaveDialog(parent); - - if (result == JFileChooser.APPROVE_OPTION) { - selectedFile = chooser.getSelectedFile(); - return APPROVE_OPTION; + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + + int result = chooser.showSaveDialog(parent); + + if (result == JFileChooser.APPROVE_OPTION) { + selectedFile = chooser.getSelectedFile(); + return APPROVE_OPTION; + } else if (result == JFileChooser.ERROR_OPTION) { + return ERROR_OPTION; + } + + return CANCEL_OPTION; + } catch (Exception e) { + return ERROR_OPTION; } - - return CANCEL_OPTION; } } From 7b30fd23062f22ff6c0ccd5c7370c360ed4f3fcb Mon Sep 17 00:00:00 2001 From: takatsugu-nakayama Date: Thu, 25 Dec 2025 17:01:17 +0900 Subject: [PATCH 06/15] =?UTF-8?q?json=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB?= =?UTF-8?q?=E3=81=AE=E8=A8=AD=E5=AE=9A=E3=82=92=E8=AA=AD=E3=81=BF=E8=BE=BC?= =?UTF-8?q?=E3=82=81=E3=81=A6=E3=81=84=E3=81=AA=E3=81=8B=E3=81=A3=E3=81=9F?= =?UTF-8?q?=E3=81=AE=E3=81=A7=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../packetproxy/gui/NativeFileChooser.java | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/main/java/core/packetproxy/gui/NativeFileChooser.java b/src/main/java/core/packetproxy/gui/NativeFileChooser.java index 49d10ee4..0eb79689 100644 --- a/src/main/java/core/packetproxy/gui/NativeFileChooser.java +++ b/src/main/java/core/packetproxy/gui/NativeFileChooser.java @@ -178,10 +178,15 @@ private int showNativeOpenDialog(Component parent) { dialog.setDirectory(currentDirectory.getAbsolutePath()); } - // Use FilenameFilter for filtering files by extension - // Note: setFile() should NOT be used for filtering as it sets the filename field, not the filter FilenameFilter filter = createFilenameFilter(); - if (filter != null && !acceptAllFileFilterUsed) { + if (filter != null && !acceptAllFileFilterUsed && !fileFilters.isEmpty()) { + FilterEntry firstFilter = fileFilters.get(0); + if (firstFilter.extensions.length > 0) { + // Build pattern like "*.json" or "*.sqlite3" + String pattern = "*." + firstFilter.extensions[0]; + dialog.setFile(pattern); + } + // Also set FilenameFilter as a fallback dialog.setFilenameFilter(filter); } @@ -192,6 +197,22 @@ private int showNativeOpenDialog(Component parent) { if (file != null && directory != null) { selectedFile = new File(directory, file); + if (filter != null && !acceptAllFileFilterUsed) { + String fileName = selectedFile.getName().toLowerCase(); + boolean matches = false; + for (FilterEntry entry : fileFilters) { + for (String ext : entry.extensions) { + if (fileName.endsWith("." + ext.toLowerCase())) { + matches = true; + break; + } + } + if (matches) break; + } + if (!matches) { + return CANCEL_OPTION; + } + } return APPROVE_OPTION; } From f26f38ccac0608f4ea4d2570febf7ee0de9708cd Mon Sep 17 00:00:00 2001 From: takatsugu-nakayama Date: Fri, 26 Dec 2025 13:58:56 +0900 Subject: [PATCH 07/15] =?UTF-8?q?History=E3=81=AE=E3=83=86=E3=83=BC?= =?UTF-8?q?=E3=83=96=E3=83=AB=E3=82=92=E5=B7=A6=E5=AF=84=E3=81=9B=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=80=81=E3=83=9C=E3=83=BC=E3=83=80=E3=83=BC=E3=81=AE?= =?UTF-8?q?=E7=B7=9A=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/core/packetproxy/gui/GUIHistory.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/core/packetproxy/gui/GUIHistory.java b/src/main/java/core/packetproxy/gui/GUIHistory.java index 20d77461..f1227fda 100644 --- a/src/main/java/core/packetproxy/gui/GUIHistory.java +++ b/src/main/java/core/packetproxy/gui/GUIHistory.java @@ -47,6 +47,7 @@ import java.util.Objects; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; @@ -61,11 +62,13 @@ import javax.swing.JTable; import javax.swing.JToggleButton; import javax.swing.KeyStroke; +import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.event.ListSelectionEvent; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; +import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableRowSorter; @@ -410,6 +413,23 @@ public Component prepareRenderer(TableCellRenderer tcr, int row, int column) { table.getColumn(columnNames[i]).setPreferredWidth(columnWidth[i]); } + // Set header alignment to left with border and padding + DefaultTableCellRenderer headerRenderer = new DefaultTableCellRenderer() { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createMatteBorder(0, 0, 1, 1, Color.GRAY), + BorderFactory.createEmptyBorder(2, 5, 2, 5) // top, left, bottom, right padding + )); + return c; + } + }; + headerRenderer.setHorizontalAlignment(SwingConstants.LEFT); + for (int i = 0; i < columnNames.length; i++) { + table.getColumnModel().getColumn(i).setHeaderRenderer(headerRenderer); + } ((JComponent) table.getDefaultRenderer(Boolean.class)).setOpaque(true); table.getSelectionModel().addListSelectionListener(new javax.swing.event.ListSelectionListener() { From eff9bd6f930e4ddb32b08d67dd448e1377ea5142 Mon Sep 17 00:00:00 2001 From: takatsugu-nakayama Date: Fri, 26 Dec 2025 14:31:17 +0900 Subject: [PATCH 08/15] =?UTF-8?q?Option=E3=81=AE=E3=82=AB=E3=83=86?= =?UTF-8?q?=E3=82=B4=E3=83=AA=E9=96=93=E3=81=AE=E6=B0=B4=E5=B9=B3=E7=B7=9A?= =?UTF-8?q?=E3=82=92=E8=A1=A8=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/core/packetproxy/gui/GUIOption.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/core/packetproxy/gui/GUIOption.java b/src/main/java/core/packetproxy/gui/GUIOption.java index 729f64bb..5054476f 100644 --- a/src/main/java/core/packetproxy/gui/GUIOption.java +++ b/src/main/java/core/packetproxy/gui/GUIOption.java @@ -79,7 +79,11 @@ private JComponent createElement(String title, String description) throws Except private JComponent createSeparator() { JSeparator line = new JSeparator(); - line.setMaximumSize(new Dimension(Short.MAX_VALUE, line.getMinimumSize().height)); + line.setForeground(Color.LIGHT_GRAY); + line.setBackground(Color.LIGHT_GRAY); + line.setOpaque(true); + line.setPreferredSize(new Dimension(0, 1)); + line.setMaximumSize(new Dimension(Integer.MAX_VALUE, 1)); return line; } From 6b8d326d70bc82a3cbfb6efdaa43889aaa0375e7 Mon Sep 17 00:00:00 2001 From: takatsugu-nakayama Date: Fri, 26 Dec 2025 15:29:11 +0900 Subject: [PATCH 09/15] =?UTF-8?q?=E3=83=86=E3=83=BC=E3=83=96=E3=83=AB?= =?UTF-8?q?=E3=83=98=E3=83=83=E3=83=80=E3=82=92=E5=B7=A6=E5=AF=84=E3=81=9B?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=80=81=E3=82=AB=E3=83=A9=E3=83=A0=E5=A2=83?= =?UTF-8?q?=E7=95=8C=E3=81=AB=E7=B7=9A=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../packetproxy/gui/GUIBulkSenderTable.java | 20 ++++++++++++++++ .../java/core/packetproxy/gui/GUIHistory.java | 2 +- .../gui/GUIOptionComponentBase.java | 24 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/main/java/core/packetproxy/gui/GUIBulkSenderTable.java b/src/main/java/core/packetproxy/gui/GUIBulkSenderTable.java index a32cb11b..7fbb6e2f 100644 --- a/src/main/java/core/packetproxy/gui/GUIBulkSenderTable.java +++ b/src/main/java/core/packetproxy/gui/GUIBulkSenderTable.java @@ -34,9 +34,12 @@ import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; +import javax.swing.BorderFactory; import javax.swing.JTable; import javax.swing.KeyStroke; +import javax.swing.SwingConstants; import javax.swing.event.ListSelectionEvent; +import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; import packetproxy.common.Utils; import packetproxy.model.OneShotPacket; @@ -131,6 +134,23 @@ public Component prepareRenderer(TableCellRenderer tcr, int row, int column) { table.getColumn(columnNames[i]).setPreferredWidth(columnWidth[i]); } + // Set header style with left alignment, border, and padding + DefaultTableCellRenderer headerRenderer = new DefaultTableCellRenderer() { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createMatteBorder(0, 0, 1, 1, Color.LIGHT_GRAY), + BorderFactory.createEmptyBorder(2, 5, 2, 5) + )); + return c; + } + }; + headerRenderer.setHorizontalAlignment(SwingConstants.LEFT); + for (int i = 0; i < columnNames.length; i++) { + table.getColumnModel().getColumn(i).setHeaderRenderer(headerRenderer); + } table.addKeyListener(new KeyAdapter() { diff --git a/src/main/java/core/packetproxy/gui/GUIHistory.java b/src/main/java/core/packetproxy/gui/GUIHistory.java index f1227fda..359f8fc0 100644 --- a/src/main/java/core/packetproxy/gui/GUIHistory.java +++ b/src/main/java/core/packetproxy/gui/GUIHistory.java @@ -420,7 +420,7 @@ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createMatteBorder(0, 0, 1, 1, Color.GRAY), + BorderFactory.createMatteBorder(0, 0, 1, 1, Color.LIGHT_GRAY), BorderFactory.createEmptyBorder(2, 5, 2, 5) // top, left, bottom, right padding )); return c; diff --git a/src/main/java/core/packetproxy/gui/GUIOptionComponentBase.java b/src/main/java/core/packetproxy/gui/GUIOptionComponentBase.java index b66478df..78e210f1 100644 --- a/src/main/java/core/packetproxy/gui/GUIOptionComponentBase.java +++ b/src/main/java/core/packetproxy/gui/GUIOptionComponentBase.java @@ -18,6 +18,7 @@ import java.awt.Color; import java.awt.Component; import java.awt.Dimension; +import javax.swing.BorderFactory; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.beans.PropertyChangeEvent; @@ -30,8 +31,10 @@ import javax.swing.JPanel; import javax.swing.JTable; import javax.swing.RowFilter; +import javax.swing.SwingConstants; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; +import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableRowSorter; import packetproxy.common.FontManager; import packetproxy.common.I18nString; @@ -73,6 +76,7 @@ public boolean isCellEditable(int row, int column) { table.getColumn(menu[i]).setPreferredWidth(menuWidth[i]); } + setHeaderStyle(table, menu.length); ((JComponent) table.getDefaultRenderer(Boolean.class)).setOpaque(true); table.addMouseListener(tableAction); table.setRowHeight(FontManager.getInstance().getUIFontHeight(table)); @@ -146,6 +150,7 @@ void handleUpdate() { table.getColumn(menu[i]).setPreferredWidth(menuWidth[i]); } + setHeaderStyle(table, menu.length); ((JComponent) table.getDefaultRenderer(Boolean.class)).setOpaque(true); table.addMouseListener(tableAction); table.setRowHeight(FontManager.getInstance().getUIFontHeight(table)); @@ -174,6 +179,25 @@ void handleUpdate() { return panel; } + private void setHeaderStyle(JTable table, int columnCount) { + DefaultTableCellRenderer headerRenderer = new DefaultTableCellRenderer() { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createMatteBorder(0, 0, 1, 1, Color.LIGHT_GRAY), + BorderFactory.createEmptyBorder(2, 5, 2, 5) + )); + return c; + } + }; + headerRenderer.setHorizontalAlignment(SwingConstants.LEFT); + for (int i = 0; i < columnCount; i++) { + table.getColumnModel().getColumn(i).setHeaderRenderer(headerRenderer); + } + } + private JPanel createTableButton(ActionListener addAction, ActionListener editAction, ActionListener removeAction) { JPanel panel = new JPanel(); From 9e8ae7d547ea9dc57f05393a39ba6612cd3a3f56 Mon Sep 17 00:00:00 2001 From: takatsugu-nakayama Date: Fri, 26 Dec 2025 16:14:37 +0900 Subject: [PATCH 10/15] =?UTF-8?q?=E4=B8=8A=E9=83=A8=E3=82=BF=E3=83=96?= =?UTF-8?q?=E4=B8=8A=E4=B8=8B=E3=81=AE=E3=83=91=E3=83=83=E3=83=87=E3=82=A3?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=82=92=E5=89=8A=E6=B8=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/core/packetproxy/gui/GUIMain.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/core/packetproxy/gui/GUIMain.java b/src/main/java/core/packetproxy/gui/GUIMain.java index 480d54b3..578125c8 100644 --- a/src/main/java/core/packetproxy/gui/GUIMain.java +++ b/src/main/java/core/packetproxy/gui/GUIMain.java @@ -118,6 +118,8 @@ private GUIMain(String title) { gui_log = GUILog.getInstance(); tabbedpane = new JTabbedPane(); + UIManager.put("TabbedPane.tabInsets", new Insets(1, 10, 1, 10)); + SwingUtilities.updateComponentTreeUI(tabbedpane); tabbedpane.addTab(getPaneString(Panes.HISTORY), gui_history.createPanel()); tabbedpane.addTab(getPaneString(Panes.INTERCEPT), gui_intercept.createPanel()); tabbedpane.addTab(getPaneString(Panes.RESENDER), gui_resender.createPanel()); From 36cee5a79f018797a994e821a798c0b90eb04025 Mon Sep 17 00:00:00 2001 From: takatsugu-nakayama Date: Fri, 26 Dec 2025 16:15:12 +0900 Subject: [PATCH 11/15] =?UTF-8?q?VulCheck=20Helper=E3=81=AE=E3=83=86?= =?UTF-8?q?=E3=83=BC=E3=83=96=E3=83=AB=E3=83=98=E3=83=83=E3=83=80=E3=82=92?= =?UTF-8?q?=E5=B7=A6=E5=AF=84=E3=81=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../packetproxy/gui/GUIVulCheckRecvTable.java | 17 ++++++++++++++++- .../packetproxy/gui/GUIVulCheckSendTable.java | 17 ++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/main/java/core/packetproxy/gui/GUIVulCheckRecvTable.java b/src/main/java/core/packetproxy/gui/GUIVulCheckRecvTable.java index 9f2c5a9d..b14b5950 100644 --- a/src/main/java/core/packetproxy/gui/GUIVulCheckRecvTable.java +++ b/src/main/java/core/packetproxy/gui/GUIVulCheckRecvTable.java @@ -23,6 +23,7 @@ import java.util.function.Consumer; import javax.swing.*; import javax.swing.event.ListSelectionEvent; +import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; import packetproxy.model.OneShotPacket; import packetproxy.model.OptionTableModel; @@ -83,8 +84,22 @@ public Component prepareRenderer(TableCellRenderer tcr, int row, int column) { ((JComponent) table.getDefaultRenderer(Boolean.class)).setOpaque(true); table.setAutoCreateRowSorter(true); + // Set header alignment to left with border and padding + DefaultTableCellRenderer headerRenderer = new DefaultTableCellRenderer() { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createMatteBorder(0, 0, 1, 1, Color.LIGHT_GRAY), + BorderFactory.createEmptyBorder(2, 5, 2, 5) // top, left, bottom, right padding + )); + return c; + } + }; + headerRenderer.setHorizontalAlignment(SwingConstants.LEFT); for (int i = 0; i < columnNames.length; i++) { - + table.getColumnModel().getColumn(i).setHeaderRenderer(headerRenderer); table.getColumn(columnNames[i]).setPreferredWidth(columnWidth[i]); } diff --git a/src/main/java/core/packetproxy/gui/GUIVulCheckSendTable.java b/src/main/java/core/packetproxy/gui/GUIVulCheckSendTable.java index 55577ca1..5c747df1 100644 --- a/src/main/java/core/packetproxy/gui/GUIVulCheckSendTable.java +++ b/src/main/java/core/packetproxy/gui/GUIVulCheckSendTable.java @@ -26,6 +26,7 @@ import java.util.function.Function; import javax.swing.*; import javax.swing.event.ListSelectionEvent; +import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; import packetproxy.model.OneShotPacket; import packetproxy.model.OptionTableModel; @@ -91,8 +92,22 @@ public Component prepareRenderer(TableCellRenderer tcr, int row, int column) { ((JComponent) table.getDefaultRenderer(Boolean.class)).setOpaque(true); table.setAutoCreateRowSorter(true); + // Set header alignment to left with border and padding + DefaultTableCellRenderer headerRenderer = new DefaultTableCellRenderer() { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createMatteBorder(0, 0, 1, 1, Color.LIGHT_GRAY), + BorderFactory.createEmptyBorder(2, 5, 2, 5) // top, left, bottom, right padding + )); + return c; + } + }; + headerRenderer.setHorizontalAlignment(SwingConstants.LEFT); for (int i = 0; i < columnNames.length; i++) { - + table.getColumnModel().getColumn(i).setHeaderRenderer(headerRenderer); table.getColumn(columnNames[i]).setPreferredWidth(columnWidth[i]); } From c4fb187f513353dfffefb88d27254a0a67342812 Mon Sep 17 00:00:00 2001 From: takatsugu-nakayama Date: Fri, 26 Dec 2025 16:31:11 +0900 Subject: [PATCH 12/15] =?UTF-8?q?=E3=82=BF=E3=83=96=E3=81=AE=E4=B8=8A?= =?UTF-8?q?=E4=B8=8B=E3=81=AE=E4=BD=99=E7=99=BD=E3=82=92=E5=89=8A=E6=B8=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/core/packetproxy/gui/GUIMain.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/core/packetproxy/gui/GUIMain.java b/src/main/java/core/packetproxy/gui/GUIMain.java index 578125c8..a39699ab 100644 --- a/src/main/java/core/packetproxy/gui/GUIMain.java +++ b/src/main/java/core/packetproxy/gui/GUIMain.java @@ -118,7 +118,14 @@ private GUIMain(String title) { gui_log = GUILog.getInstance(); tabbedpane = new JTabbedPane(); - UIManager.put("TabbedPane.tabInsets", new Insets(1, 10, 1, 10)); + + // タブの高さを数値で強制指定 + UIManager.put("TabbedPane.tabHeight", 22); + // フォーカスが当たった時の枠線の太さを0にする + UIManager.put("TabbedPane.focusWidth", 0); + UIManager.put("TabbedPane.innerBorderInsets", new Insets(0, 0, 0, 0)); + UIManager.put("TabbedPane.tabInsets", new Insets(0, 10, 0, 10)); + SwingUtilities.updateComponentTreeUI(tabbedpane); tabbedpane.addTab(getPaneString(Panes.HISTORY), gui_history.createPanel()); tabbedpane.addTab(getPaneString(Panes.INTERCEPT), gui_intercept.createPanel()); From 9385adb442579a42e9e8a01b5563fe5e41570829 Mon Sep 17 00:00:00 2001 From: takatsugu-nakayama Date: Fri, 26 Dec 2025 16:38:35 +0900 Subject: [PATCH 13/15] =?UTF-8?q?filter=E5=8F=B3=E3=81=AE=E3=83=9C?= =?UTF-8?q?=E3=82=BF=E3=83=B3=E5=B9=85=E3=82=92=E8=AA=BF=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/core/packetproxy/gui/GUIHistory.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/core/packetproxy/gui/GUIHistory.java b/src/main/java/core/packetproxy/gui/GUIHistory.java index 359f8fc0..d97b4039 100644 --- a/src/main/java/core/packetproxy/gui/GUIHistory.java +++ b/src/main/java/core/packetproxy/gui/GUIHistory.java @@ -207,7 +207,10 @@ public void focusGained(FocusEvent e) { }); JButton filterConfigAdd = new JButton(new ImageIcon(getClass().getResource("/gui/plus.png"))); - filterConfigAdd.setMaximumSize(new Dimension(15, gui_filter.getMaximumSize().height)); + int buttonWidth = 35; // ボタンの横幅を広げる + filterConfigAdd.setPreferredSize(new Dimension(buttonWidth, gui_filter.getMaximumSize().height)); + filterConfigAdd.setMaximumSize(new Dimension(buttonWidth, gui_filter.getMaximumSize().height)); + filterConfigAdd.setMinimumSize(new Dimension(buttonWidth, gui_filter.getMaximumSize().height)); filterConfigAdd.setBackground(filterConfigAdd.getBackground()); filterConfigAdd.addActionListener(new ActionListener() { @@ -225,8 +228,9 @@ public void actionPerformed(ActionEvent e) { }); JToggleButton filterDropDown = new JToggleButton(new ImageIcon(getClass().getResource("/gui/arrow.png"))); - filterDropDown.setMaximumSize( - new Dimension(filterConfigAdd.getMaximumSize().width, gui_filter.getMaximumSize().height)); + filterDropDown.setPreferredSize(new Dimension(buttonWidth, gui_filter.getMaximumSize().height)); + filterDropDown.setMaximumSize(new Dimension(buttonWidth, gui_filter.getMaximumSize().height)); + filterDropDown.setMinimumSize(new Dimension(buttonWidth, gui_filter.getMaximumSize().height)); filterDropDown.setBackground(filterDropDown.getBackground()); filterDropDown.addMouseListener(new MouseAdapter() { @@ -274,8 +278,9 @@ public void mouseReleased(MouseEvent arg0) { ImageIcon icon = new ImageIcon(getClass().getResource("/gui/config.png")); JButton filterConfig = new JButton(icon); - filterConfig.setMaximumSize( - new Dimension(filterConfigAdd.getMaximumSize().width, gui_filter.getMaximumSize().height)); + filterConfig.setPreferredSize(new Dimension(buttonWidth, gui_filter.getMaximumSize().height)); + filterConfig.setMaximumSize(new Dimension(buttonWidth, gui_filter.getMaximumSize().height)); + filterConfig.setMinimumSize(new Dimension(buttonWidth, gui_filter.getMaximumSize().height)); filterConfig.setBackground(filterConfig.getBackground()); filterConfig.addActionListener(new ActionListener() { From fbc8a1d344d77fde1a1dfb1bb99742b93c28a04c Mon Sep 17 00:00:00 2001 From: takatsugu-nakayama Date: Fri, 26 Dec 2025 16:42:43 +0900 Subject: [PATCH 14/15] =?UTF-8?q?=E3=82=B9=E3=82=AF=E3=83=AD=E3=83=BC?= =?UTF-8?q?=E3=83=AB=E3=83=90=E3=83=BC=E3=81=8C=E7=B4=B0=E3=81=99=E3=81=8E?= =?UTF-8?q?=E3=81=9F=E3=81=AE=E3=81=A7=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/core/packetproxy/gui/GUIMain.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/core/packetproxy/gui/GUIMain.java b/src/main/java/core/packetproxy/gui/GUIMain.java index a39699ab..5945e572 100644 --- a/src/main/java/core/packetproxy/gui/GUIMain.java +++ b/src/main/java/core/packetproxy/gui/GUIMain.java @@ -192,6 +192,9 @@ private void setLookandFeel() throws Exception { // OptionPaneのロケール JOptionPane.setDefaultLocale(I18nString.getLocale()); + // スクロールバーの幅を太くする + UIManager.put("ScrollBar.width", 15); + setIconForWindows(); addShortcutForWindows(); addShortcutForMac(); From 509ad326e312a27296ab31f8092c43b9dafda204 Mon Sep 17 00:00:00 2001 From: takatsugu-nakayama Date: Fri, 26 Dec 2025 16:49:40 +0900 Subject: [PATCH 15/15] =?UTF-8?q?Resender=E5=86=85=E3=81=AE=E3=82=BF?= =?UTF-8?q?=E3=83=96=E3=81=AE=E4=BD=99=E7=99=BD=E3=82=92=E8=AA=BF=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/core/packetproxy/gui/CloseButtonTabbedPane.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/core/packetproxy/gui/CloseButtonTabbedPane.java b/src/main/java/core/packetproxy/gui/CloseButtonTabbedPane.java index a326d638..b0a2c58e 100644 --- a/src/main/java/core/packetproxy/gui/CloseButtonTabbedPane.java +++ b/src/main/java/core/packetproxy/gui/CloseButtonTabbedPane.java @@ -17,14 +17,17 @@ import java.awt.Component; import java.awt.Dimension; +import java.awt.Insets; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTabbedPane; +import javax.swing.UIManager; public class CloseButtonTabbedPane extends JTabbedPane { @@ -36,6 +39,7 @@ public CloseButtonTabbedPane() { super(); icon = new ImageIcon(getClass().getResource("/gui/close.png")); icon_mouseovered = new ImageIcon(getClass().getResource("/gui/close_mouseovered.png")); + UIManager.put("TabbedPane.tabInsets", new Insets(0, 7, 0, 7)); } @Override @@ -43,6 +47,7 @@ public void addTab(String title, Component content) { JPanel main_panel = new JPanel(); main_panel.setLayout(new BoxLayout(main_panel, BoxLayout.X_AXIS)); main_panel.setOpaque(false); + main_panel.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); JLabel label = new JLabel(title); JButton button = new JButton(icon); button.setPreferredSize(new Dimension(icon.getIconWidth() + 1, icon.getIconHeight() + 1)); @@ -66,6 +71,8 @@ public void mouseExited(MouseEvent e) { } }); main_panel.add(label); + // 数字とバツボタンの間に余白を追加 + main_panel.add(Box.createHorizontalStrut(7)); main_panel.add(button); super.addTab(null, content); setTabComponentAt(getTabCount() - 1, main_panel);