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/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); 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/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/GUIHistory.java b/src/main/java/core/packetproxy/gui/GUIHistory.java index 20d77461..d97b4039 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; @@ -204,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() { @@ -222,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() { @@ -271,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() { @@ -410,6 +418,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.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); + } ((JComponent) table.getDefaultRenderer(Boolean.class)).setOpaque(true); table.getSelectionModel().addListSelectionListener(new javax.swing.event.ListSelectionListener() { diff --git a/src/main/java/core/packetproxy/gui/GUIMain.java b/src/main/java/core/packetproxy/gui/GUIMain.java index d0758e13..5945e572 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; @@ -117,6 +118,15 @@ private GUIMain(String title) { gui_log = GUILog.getInstance(); tabbedpane = new JTabbedPane(); + + // タブの高さを数値で強制指定 + 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()); tabbedpane.addTab(getPaneString(Panes.RESENDER), gui_resender.createPanel()); @@ -161,12 +171,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; + } } } @@ -178,6 +192,9 @@ private void setLookandFeel() throws Exception { // OptionPaneのロケール JOptionPane.setDefaultLocale(I18nString.getLocale()); + // スクロールバーの幅を太くする + UIManager.put("ScrollBar.width", 15); + setIconForWindows(); addShortcutForWindows(); addShortcutForMac(); @@ -290,7 +307,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 +315,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/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; } 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/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(); 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/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]); } 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..0eb79689 --- /dev/null +++ b/src/main/java/core/packetproxy/gui/NativeFileChooser.java @@ -0,0 +1,331 @@ +/* + * 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; + + /** + * 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 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", "Client Certificate file (*.jks)") + * @param extensions The file extensions without dots (e.g., "sqlite3", "json") + */ + public void addChoosableFileFilter(String description, String... extensions) { + fileFilters.add(new FilterEntry(description, extensions)); + } + + /** + * Set file filter using FileNameExtensionFilter for compatibility. + */ + public void setFileFilter(FileNameExtensionFilter filter) { + fileFilters.clear(); + fileFilters.add(new FilterEntry(filter.getDescription(), filter.getExtensions())); + } + + /** + * Add file filter using FileNameExtensionFilter for compatibility. + */ + public void addChoosableFileFilter(FileNameExtensionFilter filter) { + fileFilters.add(new FilterEntry(filter.getDescription(), 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); + } + } + + /** + * 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; + } + 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 (FilterEntry entry : fileFilters) { + for (String ext : entry.extensions) { + if (lowerName.endsWith("." + ext.toLowerCase())) { + return true; + } + } + } + return false; + }; + } + + private int showNativeOpenDialog(Component parent) { + try { + 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 && !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); + } + + dialog.setVisible(true); + + String file = dialog.getFile(); + String directory = dialog.getDirectory(); + + 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; + } + + return CANCEL_OPTION; + } catch (Exception e) { + return ERROR_OPTION; + } + } + + private int showNativeSaveDialog(Component parent) { + 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()); + } + + 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; + } catch (Exception e) { + return ERROR_OPTION; + } + } + + private int showSwingOpenDialog(Component parent) { + 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.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; + } + } + + private int showSwingSaveDialog(Component parent) { + try { + 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 (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; + } else if (result == JFileChooser.ERROR_OPTION) { + return ERROR_OPTION; + } + + return CANCEL_OPTION; + } catch (Exception e) { + return ERROR_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())); + } } }