001/******************************************************************************* 002 003 File: FolderDialog.java 004 Author: Steve Roy <steve@sillybit.com> 005 006 Part of MRJ Adapter, a unified API for easy integration of Mac OS specific 007 functionality within your cross-platform Java application. 008 009 This library is open source and can be modified and/or distributed under 010 the terms of the Artistic License. 011 <http://mrjadapter.dev.java.net/license.html> 012 013 Change History: 014 03/06/03 Created this file - Steve 015 03/25/03 Moved to the net.roydesign.ui package, modified to use the 016 apple.awt.fileDialogForDirectories property with MRJ 4, added 017 the getInitialMode() method, removed getFolder() because 018 it's redundant with getDirectory(), removed the filename filter 019 which was irrelevant - Steve 020 12/16/03 Fixed getDirectory() to check if super.getFile() is null before 021 trying to build a path with it - Steve 022 01/01/23 Copied out of defunct MRJAdapter into my private library and replaced 023 references to "MRJAdapter.mrjVersion" with MacOSUtilities.isMacOS(). 024 03/22/25 Fixed an error with setVisible() throwing a NullPointerException on Windows. 025 026*******************************************************************************/ 027 028package jahuwaldt.swing; 029 030import java.awt.FileDialog; 031import java.awt.Frame; 032import java.io.File; 033import static java.util.Objects.isNull; 034import static java.util.Objects.nonNull; 035import java.util.Properties; 036 037/** 038 * A folder dialog is a modal file dialog to specially select a folder on 039 * disk. This class takes advantage of a little know trick in Apple's VMs to 040 * show a real folder dialog, with a Choose button and all. However, there is 041 * no such thing on other platforms, where this class employs the usual 042 * kludge which is to show a Save dialog. If you would rather use the Swing 043 * JFileChooser, go right ahead. 044 * 045 * Based on: MRJ Adapter 1.2, Modified by Joseph A. Huwaldt 046 * @version March 22, 2025 047 */ 048public class FolderDialog extends FileDialog { 049 050 /** 051 * Whether the <code>setMode()</code> method should check calls or not. 052 */ 053 private boolean modeCheckingEnabled = false; 054 055 /** 056 * Construct a folder dialog with the given parent frame. 057 * 058 * @param parent the parent frame 059 */ 060 public FolderDialog(Frame parent) { 061 this(parent, ""); 062 } 063 064 /** 065 * Construct a folder dialog with the given parent frame and title. 066 * 067 * @param parent the parent frame 068 * @param title the title of the dialog 069 */ 070 @SuppressWarnings("OverridableMethodCallInConstructor") 071 public FolderDialog(Frame parent, String title) { 072 super(parent, title, getInitialMode()); 073 if (!MacOSUtilities.isMacOS()) 074 setFile("-"); 075 modeCheckingEnabled = true; 076 } 077 078 /** 079 * Get the file of this file dialog, which in the case of this class, is always an 080 * empty string ("") unless the user has canceled where the return value will be 081 * <code>null</code>. 082 * 083 * @return an empty string if a directory was selected, or <code>null</code> 084 */ 085 @Override 086 public String getFile() { 087 return nonNull(super.getFile()) ? "" : null; 088 } 089 090 /** 091 * Get the directory of this file dialog. 092 * 093 * @return the directory of the dialog, or null 094 */ 095 @Override 096 public String getDirectory() { 097 String path = super.getDirectory(); 098 if (isNull(path)) 099 return null; 100 if (MacOSUtilities.isMacOS() && nonNull(super.getFile())) 101 return new File(path, super.getFile()).getPath(); 102 return path; 103 } 104 105 /** 106 * Set the mode of the dialog. This method is overriden because it doesn't make sense 107 * in the context of an application dialog to allow selection of the mode. It will 108 * throw an error if you try to call it. 109 * 110 * @param mode the mode 111 */ 112 @Override 113 public void setMode(int mode) { 114 if (modeCheckingEnabled) 115 throw new Error("can't set mode"); 116 super.setMode(mode); 117 } 118 119 /** 120 * Shows or hides this Dialog depending on the value of parameter 'visible'. Since the 121 * dialog is modal, this method will not return until either the user dismisses the 122 * dialog or you make it invisible yourself via <code>setVisible(false)</code> or 123 * <code>dispose()</code>. 124 */ 125 @Override 126 public void setVisible(boolean visible) { 127 // Set the system property required by MacOS. 128 String prop = "apple.awt.fileDialogForDirectories"; 129 Object oldValue = null; 130 Properties props = System.getProperties(); 131 if (MacOSUtilities.isMacOS()) { 132 oldValue = props.get(prop); 133 props.put(prop, "true"); 134 } 135 136 // Do the usual thing 137 super.setVisible(visible); 138 139 // Reset the system property. 140 if (MacOSUtilities.isMacOS()) { 141 if (isNull(oldValue)) 142 props.remove(prop); 143 else 144 props.put(prop, oldValue); 145 } 146 } 147 148 /** 149 * Perform the preparatory setup for the folder dialog and return the value to use for 150 * the mode of the dialog. This method is called by the constructor. 151 * 152 * @return the mode value to use 153 */ 154 private static int getInitialMode() { 155 if (MacOSUtilities.isMacOS()) 156 return LOAD; 157 return SAVE; 158 } 159}