This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git

commit 398add93ab20e86bf31a4693068031b8b104725f
Author: Andrus Adamchik <[email protected]>
AuthorDate: Sun May 24 09:28:46 2026 -0400

     CAY-2948 Modeler: use native FileDialog on Mac
    
    reverting to JFileChooser for Save on Mac
---
 .../modeler/platform/mac/MacFileChooser.java       | 83 ++++++++++++++++------
 1 file changed, 63 insertions(+), 20 deletions(-)

diff --git 
a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/platform/mac/MacFileChooser.java
 
b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/platform/mac/MacFileChooser.java
index 8c36723bd..d03eeb20e 100644
--- 
a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/platform/mac/MacFileChooser.java
+++ 
b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/platform/mac/MacFileChooser.java
@@ -22,9 +22,15 @@ package org.apache.cayenne.modeler.platform.mac;
 import org.apache.cayenne.modeler.pref.adapters.FileChooserPrefs;
 import org.apache.cayenne.modeler.toolkit.filechooser.CMFileChooser;
 
+import javax.swing.JButton;
+import javax.swing.JFileChooser;
+import javax.swing.SwingUtilities;
 import javax.swing.filechooser.FileFilter;
+import java.awt.Component;
+import java.awt.Container;
 import java.awt.FileDialog;
 import java.awt.Frame;
+import java.awt.event.HierarchyEvent;
 import java.io.File;
 import java.io.FilenameFilter;
 import java.util.function.Consumer;
@@ -36,8 +42,6 @@ import java.util.function.Consumer;
  */
 public class MacFileChooser implements CMFileChooser {
 
-    private static final String DIRS_DIALOG_PROPERTY = 
"apple.awt.fileDialogForDirectories";
-
     private final Frame parent;
     private final String title;
 
@@ -59,7 +63,7 @@ public class MacFileChooser implements CMFileChooser {
 
     @Override
     public File openDir(File initialDir) {
-        return showOpenDir(fd -> { if (initialDir != null) 
fd.setDirectory(initialDir.getAbsolutePath()); });
+        return showOpenDir(c -> { if (initialDir != null) 
c.setCurrentDirectory(initialDir); });
     }
 
     @Override
@@ -74,7 +78,7 @@ public class MacFileChooser implements CMFileChooser {
 
     @Override
     public File saveDir(File initialDir) {
-        return showOpenDir(fd -> { if (initialDir != null) 
fd.setDirectory(initialDir.getAbsolutePath()); });
+        return showOpenDir(c -> { if (initialDir != null) 
c.setCurrentDirectory(initialDir); });
     }
 
     private File showOpen(FilenameFilter fnFilter, Consumer<FileDialog> init) {
@@ -99,25 +103,64 @@ public class MacFileChooser implements CMFileChooser {
         return file != null ? new File(fd.getDirectory(), file) : null;
     }
 
-    private File showOpenDir(Consumer<FileDialog> init) {
-        // Toggle the property only for the duration of this modal dialog.
-        // FileDialog is EDT-blocking, so no other dialog can run concurrently.
-        System.setProperty(DIRS_DIALOG_PROPERTY, "true");
-        try {
-
-            // TODO: FileDialog.LOAD results in the "Save" button being called 
"Load". This is unfortunate, but the
-            //   alternative allows the user to override the file name
-            FileDialog fd = new FileDialog(parent, title != null ? title : "", 
FileDialog.LOAD);
-            init.accept(fd);
-            fd.setVisible(true);
-            String file = fd.getFile();
-            // getFile() returns the selected directory name; getDirectory() 
is its parent.
-            return file != null ? new File(fd.getDirectory(), file) : null;
-        } finally {
-            System.clearProperty(DIRS_DIALOG_PROPERTY);
+    private File showOpenDir(Consumer<JFileChooser> init) {
+
+        // JFileChooser instead of FileDialog: 
apple.awt.fileDialogForDirectories makes
+        // FileDialog pick directories, but the button is labeled "Load" with 
no way to
+        // override it. JFileChooser shows "Open" and lets us set a dialog 
title.
+
+        // FILES_AND_DIRECTORIES + directory filter instead of 
DIRECTORIES_ONLY: on macOS
+        // Aqua L&F the approval button stays disabled in DIRECTORIES_ONLY 
mode.
+        JFileChooser chooser = new JFileChooser();
+        chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
+        chooser.setFileFilter(DIRS_ONLY_FILTER);
+
+        // AquaFileChooserUI disables the approve button when the user 
navigates into a
+        // directory with nothing selected. Directly enabling buttons in the 
component tree
+        // (via invokeLater, after Aqua's own listener runs) keeps it always 
active so the
+        // user can confirm the current directory without selecting a child 
item.
+        // No state mutations — setSelectedFile(dir) makes Aqua navigate into 
that dir,
+        // firing DIRECTORY_CHANGED again and causing an infinite loop.
+        chooser.addPropertyChangeListener(e -> {
+            String prop = e.getPropertyName();
+            if (JFileChooser.DIRECTORY_CHANGED_PROPERTY.equals(prop)
+                    || 
JFileChooser.SELECTED_FILE_CHANGED_PROPERTY.equals(prop)) {
+                SwingUtilities.invokeLater(() -> enableButtons(chooser));
+            }
+        });
+
+        chooser.addHierarchyListener(e -> {
+            if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 && 
chooser.isShowing()) {
+                enableButtons(chooser);
+            }
+        });
+        
+        init.accept(chooser);
+        if (title != null) {
+            chooser.setDialogTitle(title);
+        }
+        if (chooser.showOpenDialog(parent) != JFileChooser.APPROVE_OPTION) {
+            return null;
+        }
+        File selected = chooser.getSelectedFile();
+        return selected != null ? selected : chooser.getCurrentDirectory();
+    }
+
+    private static void enableButtons(Container container) {
+        for (Component c : container.getComponents()) {
+            if (c instanceof JButton btn) {
+                btn.setEnabled(true);
+            } else if (c instanceof Container cont) {
+                enableButtons(cont);
+            }
         }
     }
 
+    private static final FileFilter DIRS_ONLY_FILTER = new FileFilter() {
+        @Override public boolean accept(File f) { return f.isDirectory(); }
+        @Override public String getDescription() { return "Directories"; }
+    };
+
     private static FilenameFilter toFilenameFilter(FileFilter filter) {
         return filter != null ? (dir, name) -> filter.accept(new File(dir, 
name)) : null;
     }

Reply via email to