From 9453d55abd83230f4947bcd16df950da89f996fd Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Sat, 4 Jan 2025 12:33:18 +0100 Subject: [PATCH] System File Chooser: fixes for Windows --- .../flatlaf/ui/FlatNativeWindowsLibrary.java | 6 +- .../src/main/cpp/WinFileChooser.cpp | 24 +++- .../FlatSystemFileChooserWindowsTest.java | 110 +++++++++++++----- .../FlatSystemFileChooserWindowsTest.jfd | 94 ++++++++++----- 4 files changed, 171 insertions(+), 63 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeWindowsLibrary.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeWindowsLibrary.java index 991a9878..3e7c7b0c 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeWindowsLibrary.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeWindowsLibrary.java @@ -201,10 +201,10 @@ public class FlatNativeWindowsLibrary * the file dialog. It is highly recommended to invoke it from a new thread * to avoid blocking the AWT event dispatching thread. * - * @param owner the owner of the file dialog + * @param owner the owner of the file dialog; or {@code null} * @param open if {@code true}, shows the open dialog; if {@code false}, shows the save dialog * @param title text displayed in dialog title; or {@code null} - * @param okButtonLabel text displayed in default button; or {@code null} + * @param okButtonLabel text displayed in default button; or {@code null}. Use '&' for mnemonics (e.g. "&Choose") * @param fileNameLabel text displayed in front of the filename text field; or {@code null} * @param fileName user-editable filename currently shown in the filename field; or {@code null} * @param folder current directory shown in the dialog; or {@code null} @@ -219,7 +219,7 @@ public class FlatNativeWindowsLibrary * @param optionsClear options to clear; see {@code FOS_*} constants * @param fileTypeIndex the file type that appears as selected (zero-based) * @param fileTypes file types that the dialog can open or save. - * Pairs of strings are required. + * Pairs of strings are required for each filter. * First string is the display name of the filter shown in the combobox (e.g. "Text Files"). * Second string is the filter pattern (e.g. "*.txt", "*.exe;*.dll" or "*.*"). * @return file path(s) that the user selected; an empty array if canceled; diff --git a/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/WinFileChooser.cpp b/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/WinFileChooser.cpp index b51b0d7f..03bc2b5b 100644 --- a/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/WinFileChooser.cpp +++ b/flatlaf-natives/flatlaf-natives-windows/src/main/cpp/WinFileChooser.cpp @@ -23,6 +23,7 @@ /** * @author Karl Tauber + * @since 3.6 */ // see FlatWndProc.cpp @@ -79,7 +80,7 @@ public: //---- class FilterSpec ------------------------------------------------------- class FilterSpec { - JNIEnv* env; + JNIEnv* env = NULL; jstring* jnames = NULL; jstring* jspecs = NULL; @@ -89,6 +90,9 @@ public: public: FilterSpec( JNIEnv* _env, jobjectArray fileTypes ) { + if( fileTypes == NULL ) + return; + env = _env; count = env->GetArrayLength( fileTypes ) / 2; if( count <= 0 ) @@ -165,7 +169,13 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibr if( !coInitializer.initialized ) return NULL; - HWND hwndOwner = getWindowHandle( env, owner ); + // handle limitations (without this, some Win32 method fails and this method returns NULL) + if( (optionsSet & FOS_PICKFOLDERS) != 0 ) { + if( open ) + fileTypes = NULL; // no filter allowed for picking folders + else + optionsSet &= ~FOS_PICKFOLDERS; // not allowed for save dialog + } // convert Java strings to C strings AutoReleaseString ctitle( env, title ); @@ -185,6 +195,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibr NULL, CLSCTX_INPROC_SERVER, open ? IID_IFileOpenDialog : IID_IFileSaveDialog, reinterpret_cast( &dialog ) ) ); + // set title, etc. if( ctitle != NULL ) CHECK_HRESULT( dialog->SetTitle( ctitle ) ); if( cokButtonLabel != NULL ) @@ -202,23 +213,26 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibr if( cdefaultExtension != NULL ) CHECK_HRESULT( dialog->SetDefaultExtension( cdefaultExtension ) ); + // set options FILEOPENDIALOGOPTIONS existingOptions; CHECK_HRESULT( dialog->GetOptions( &existingOptions ) ); CHECK_HRESULT( dialog->SetOptions ( (existingOptions & ~optionsClear) | optionsSet ) ); - if( specs.count > 0 ) { + // initialize filter + if( specs.count > 0 && (optionsSet & FOS_PICKFOLDERS) == 0 ) { CHECK_HRESULT( dialog->SetFileTypes( specs.count, specs.specs ) ); if( fileTypeIndex > 0 ) CHECK_HRESULT( dialog->SetFileTypeIndex( min( fileTypeIndex + 1, specs.count ) ) ); } // show dialog + HWND hwndOwner = (owner != NULL) ? getWindowHandle( env, owner ) : NULL; HRESULT hr = dialog->Show( hwndOwner ); if( hr == HRESULT_FROM_WIN32(ERROR_CANCELLED) ) return newJavaStringArray( env, 0 ); CHECK_HRESULT( hr ); - // convert URLs to Java string array + // convert shell items to Java string array if( open ) { AutoReleasePtr shellItems; DWORD count; @@ -235,7 +249,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_formdev_flatlaf_ui_FlatNativeWindowsLibr jstring jpath = newJavaString( env, path ); CoTaskMemFree( path ); - env->SetObjectArrayElement( array, 0, jpath ); + env->SetObjectArrayElement( array, i, jpath ); env->DeleteLocalRef( jpath ); } return array; diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatSystemFileChooserWindowsTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatSystemFileChooserWindowsTest.java index ab1f0865..31a55aea 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatSystemFileChooserWindowsTest.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatSystemFileChooserWindowsTest.java @@ -17,10 +17,12 @@ package com.formdev.flatlaf.testing; import static com.formdev.flatlaf.ui.FlatNativeWindowsLibrary.*; +import java.awt.Dialog; import java.awt.EventQueue; import java.awt.SecondaryLoop; import java.awt.Toolkit; import java.awt.Window; +import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowFocusListener; import java.awt.event.WindowListener; @@ -75,7 +77,26 @@ public class FlatSystemFileChooserWindowsTest } private void openOrSave( boolean open, boolean direct ) { - Window owner = SwingUtilities.windowForComponent( this ); + Window frame = SwingUtilities.windowForComponent( this ); + if( ownerFrameRadioButton.isSelected() ) + openOrSave( open, direct, frame ); + else if( ownerDialogRadioButton.isSelected() ) { + JDialog dialog = new JDialog( frame, "Dummy Modal Dialog", Dialog.DEFAULT_MODALITY_TYPE ); + dialog.setDefaultCloseOperation( JDialog.DISPOSE_ON_CLOSE ); + dialog.addWindowListener( new WindowAdapter() { + @Override + public void windowOpened( WindowEvent e ) { + openOrSave( open, direct, dialog ); + } + } ); + dialog.setSize( 1200, 1000 ); + dialog.setLocationRelativeTo( this ); + dialog.setVisible( true ); + } else + openOrSave( open, direct, null ); + } + + private void openOrSave( boolean open, boolean direct, Window owner ) { String title = n( titleField.getText() ); String okButtonLabel = n( okButtonLabelField.getText() ); String fileNameLabel = n( fileNameLabelField.getText() ); @@ -215,6 +236,11 @@ public class FlatSystemFileChooserWindowsTest private void initComponents() { // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents + ownerLabel = new JLabel(); + ownerFrameRadioButton = new JRadioButton(); + ownerDialogRadioButton = new JRadioButton(); + ownerNullRadioButton = new JRadioButton(); + ownerSpacer = new JPanel(null); titleLabel = new JLabel(); titleField = new JTextField(); panel1 = new JPanel(); @@ -285,12 +311,31 @@ public class FlatSystemFileChooserWindowsTest "[]" + "[]" + "[]" + + "[]" + "[grow,fill]")); + //---- ownerLabel ---- + ownerLabel.setText("owner"); + add(ownerLabel, "cell 0 0"); + + //---- ownerFrameRadioButton ---- + ownerFrameRadioButton.setText("JFrame"); + ownerFrameRadioButton.setSelected(true); + add(ownerFrameRadioButton, "cell 1 0"); + + //---- ownerDialogRadioButton ---- + ownerDialogRadioButton.setText("JDialog"); + add(ownerDialogRadioButton, "cell 1 0"); + + //---- ownerNullRadioButton ---- + ownerNullRadioButton.setText("null"); + add(ownerNullRadioButton, "cell 1 0"); + add(ownerSpacer, "cell 1 0,growx"); + //---- titleLabel ---- titleLabel.setText("title"); - add(titleLabel, "cell 0 0"); - add(titleField, "cell 1 0"); + add(titleLabel, "cell 0 1"); + add(titleField, "cell 1 1"); //======== panel1 ======== { @@ -402,46 +447,46 @@ public class FlatSystemFileChooserWindowsTest hidePinnedPlacesCheckBox.setText("hidePinnedPlaces"); panel1.add(hidePinnedPlacesCheckBox, "cell 1 7"); } - add(panel1, "cell 2 0 1 10,aligny top,growy 0"); + add(panel1, "cell 2 1 1 10,aligny top,growy 0"); //---- okButtonLabelLabel ---- okButtonLabelLabel.setText("okButtonLabel"); - add(okButtonLabelLabel, "cell 0 1"); - add(okButtonLabelField, "cell 1 1"); + add(okButtonLabelLabel, "cell 0 2"); + add(okButtonLabelField, "cell 1 2"); //---- fileNameLabelLabel ---- fileNameLabelLabel.setText("fileNameLabel"); - add(fileNameLabelLabel, "cell 0 2"); - add(fileNameLabelField, "cell 1 2"); + add(fileNameLabelLabel, "cell 0 3"); + add(fileNameLabelField, "cell 1 3"); //---- fileNameLabel ---- fileNameLabel.setText("fileName"); - add(fileNameLabel, "cell 0 3"); - add(fileNameField, "cell 1 3"); + add(fileNameLabel, "cell 0 4"); + add(fileNameField, "cell 1 4"); //---- folderLabel ---- folderLabel.setText("folder"); - add(folderLabel, "cell 0 4"); - add(folderField, "cell 1 4"); + add(folderLabel, "cell 0 5"); + add(folderField, "cell 1 5"); //---- saveAsItemLabel ---- saveAsItemLabel.setText("saveAsItem"); - add(saveAsItemLabel, "cell 0 5"); - add(saveAsItemField, "cell 1 5"); + add(saveAsItemLabel, "cell 0 6"); + add(saveAsItemField, "cell 1 6"); //---- defaultFolderLabel ---- defaultFolderLabel.setText("defaultFolder"); - add(defaultFolderLabel, "cell 0 6"); - add(defaultFolderField, "cell 1 6"); + add(defaultFolderLabel, "cell 0 7"); + add(defaultFolderField, "cell 1 7"); //---- defaultExtensionLabel ---- defaultExtensionLabel.setText("defaultExtension"); - add(defaultExtensionLabel, "cell 0 7"); - add(defaultExtensionField, "cell 1 7"); + add(defaultExtensionLabel, "cell 0 8"); + add(defaultExtensionField, "cell 1 8"); //---- fileTypesLabel ---- fileTypesLabel.setText("fileTypes"); - add(fileTypesLabel, "cell 0 8"); + add(fileTypesLabel, "cell 0 9"); //---- fileTypesField ---- fileTypesField.setEditable(true); @@ -451,11 +496,11 @@ public class FlatSystemFileChooserWindowsTest "Text Files,*.txt,PDF Files,*.pdf,All Files,*.*", "Text and PDF Files,*.txt;*.pdf" })); - add(fileTypesField, "cell 1 8"); + add(fileTypesField, "cell 1 9"); //---- fileTypeIndexLabel ---- fileTypeIndexLabel.setText("fileTypeIndex"); - add(fileTypeIndexLabel, "cell 0 9"); + add(fileTypeIndexLabel, "cell 0 10"); //---- fileTypeIndexSlider ---- fileTypeIndexSlider.setMaximum(10); @@ -463,27 +508,27 @@ public class FlatSystemFileChooserWindowsTest fileTypeIndexSlider.setValue(0); fileTypeIndexSlider.setPaintLabels(true); fileTypeIndexSlider.setSnapToTicks(true); - add(fileTypeIndexSlider, "cell 1 9"); + add(fileTypeIndexSlider, "cell 1 10"); //---- openButton ---- openButton.setText("Open..."); openButton.addActionListener(e -> open()); - add(openButton, "cell 0 10 3 1"); + add(openButton, "cell 0 11 3 1"); //---- saveButton ---- saveButton.setText("Save..."); saveButton.addActionListener(e -> save()); - add(saveButton, "cell 0 10 3 1"); + add(saveButton, "cell 0 11 3 1"); //---- openDirectButton ---- openDirectButton.setText("Open (no-thread)..."); openDirectButton.addActionListener(e -> openDirect()); - add(openDirectButton, "cell 0 10 3 1"); + add(openDirectButton, "cell 0 11 3 1"); //---- saveDirectButton ---- saveDirectButton.setText("Save (no-thread)..."); saveDirectButton.addActionListener(e -> saveDirect()); - add(saveDirectButton, "cell 0 10 3 1"); + add(saveDirectButton, "cell 0 11 3 1"); //======== filesScrollPane ======== { @@ -492,11 +537,22 @@ public class FlatSystemFileChooserWindowsTest filesField.setRows(8); filesScrollPane.setViewportView(filesField); } - add(filesScrollPane, "cell 0 11 3 1,growx"); + add(filesScrollPane, "cell 0 12 3 1,growx"); + + //---- ownerButtonGroup ---- + ButtonGroup ownerButtonGroup = new ButtonGroup(); + ownerButtonGroup.add(ownerFrameRadioButton); + ownerButtonGroup.add(ownerDialogRadioButton); + ownerButtonGroup.add(ownerNullRadioButton); // JFormDesigner - End of component initialization //GEN-END:initComponents } // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables + private JLabel ownerLabel; + private JRadioButton ownerFrameRadioButton; + private JRadioButton ownerDialogRadioButton; + private JRadioButton ownerNullRadioButton; + private JPanel ownerSpacer; private JLabel titleLabel; private JTextField titleField; private JPanel panel1; diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatSystemFileChooserWindowsTest.jfd b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatSystemFileChooserWindowsTest.jfd index f26815de..66590a21 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatSystemFileChooserWindowsTest.jfd +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatSystemFileChooserWindowsTest.jfd @@ -6,19 +6,52 @@ new FormModel { add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { "$layoutConstraints": "ltr,insets dialog,hidemode 3" "$columnConstraints": "[left][grow,fill][fill]" - "$rowConstraints": "[][][][][][][][][][][][grow,fill]" + "$rowConstraints": "[][][][][][][][][][][][][grow,fill]" } ) { name: "this" + add( new FormComponent( "javax.swing.JLabel" ) { + name: "ownerLabel" + "text": "owner" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 0" + } ) + add( new FormComponent( "javax.swing.JRadioButton" ) { + name: "ownerFrameRadioButton" + "text": "JFrame" + "selected": true + "$buttonGroup": new FormReference( "ownerButtonGroup" ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 0" + } ) + add( new FormComponent( "javax.swing.JRadioButton" ) { + name: "ownerDialogRadioButton" + "text": "JDialog" + "$buttonGroup": new FormReference( "ownerButtonGroup" ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 0" + } ) + add( new FormComponent( "javax.swing.JRadioButton" ) { + name: "ownerNullRadioButton" + "text": "null" + "$buttonGroup": new FormReference( "ownerButtonGroup" ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 0" + } ) + add( new FormComponent( "com.jformdesigner.designer.wrapper.HSpacer" ) { + name: "ownerSpacer" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 0,growx" + } ) add( new FormComponent( "javax.swing.JLabel" ) { name: "titleLabel" "text": "title" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 0" + "value": "cell 0 1" } ) add( new FormComponent( "javax.swing.JTextField" ) { name: "titleField" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 0" + "value": "cell 1 1" } ) add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { "$layoutConstraints": "insets 2,hidemode 3" @@ -165,90 +198,90 @@ new FormModel { "value": "cell 1 7" } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 2 0 1 10,aligny top,growy 0" + "value": "cell 2 1 1 10,aligny top,growy 0" } ) add( new FormComponent( "javax.swing.JLabel" ) { name: "okButtonLabelLabel" "text": "okButtonLabel" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 1" + "value": "cell 0 2" } ) add( new FormComponent( "javax.swing.JTextField" ) { name: "okButtonLabelField" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 1" + "value": "cell 1 2" } ) add( new FormComponent( "javax.swing.JLabel" ) { name: "fileNameLabelLabel" "text": "fileNameLabel" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 2" + "value": "cell 0 3" } ) add( new FormComponent( "javax.swing.JTextField" ) { name: "fileNameLabelField" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 2" + "value": "cell 1 3" } ) add( new FormComponent( "javax.swing.JLabel" ) { name: "fileNameLabel" "text": "fileName" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 3" + "value": "cell 0 4" } ) add( new FormComponent( "javax.swing.JTextField" ) { name: "fileNameField" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 3" + "value": "cell 1 4" } ) add( new FormComponent( "javax.swing.JLabel" ) { name: "folderLabel" "text": "folder" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 4" + "value": "cell 0 5" } ) add( new FormComponent( "javax.swing.JTextField" ) { name: "folderField" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 4" + "value": "cell 1 5" } ) add( new FormComponent( "javax.swing.JLabel" ) { name: "saveAsItemLabel" "text": "saveAsItem" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 5" + "value": "cell 0 6" } ) add( new FormComponent( "javax.swing.JTextField" ) { name: "saveAsItemField" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 5" + "value": "cell 1 6" } ) add( new FormComponent( "javax.swing.JLabel" ) { name: "defaultFolderLabel" "text": "defaultFolder" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 6" + "value": "cell 0 7" } ) add( new FormComponent( "javax.swing.JTextField" ) { name: "defaultFolderField" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 6" + "value": "cell 1 7" } ) add( new FormComponent( "javax.swing.JLabel" ) { name: "defaultExtensionLabel" "text": "defaultExtension" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 7" + "value": "cell 0 8" } ) add( new FormComponent( "javax.swing.JTextField" ) { name: "defaultExtensionField" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 7" + "value": "cell 1 8" } ) add( new FormComponent( "javax.swing.JLabel" ) { name: "fileTypesLabel" "text": "fileTypes" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 8" + "value": "cell 0 9" } ) add( new FormComponent( "javax.swing.JComboBox" ) { name: "fileTypesField" @@ -261,13 +294,13 @@ new FormModel { addElement( "Text and PDF Files,*.txt;*.pdf" ) } }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 8" + "value": "cell 1 9" } ) add( new FormComponent( "javax.swing.JLabel" ) { name: "fileTypeIndexLabel" "text": "fileTypeIndex" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 9" + "value": "cell 0 10" } ) add( new FormComponent( "javax.swing.JSlider" ) { name: "fileTypeIndexSlider" @@ -277,35 +310,35 @@ new FormModel { "paintLabels": true "snapToTicks": true }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 9" + "value": "cell 1 10" } ) add( new FormComponent( "javax.swing.JButton" ) { name: "openButton" "text": "Open..." addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "open", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 10 3 1" + "value": "cell 0 11 3 1" } ) add( new FormComponent( "javax.swing.JButton" ) { name: "saveButton" "text": "Save..." addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "save", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 10 3 1" + "value": "cell 0 11 3 1" } ) add( new FormComponent( "javax.swing.JButton" ) { name: "openDirectButton" "text": "Open (no-thread)..." addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "openDirect", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 10 3 1" + "value": "cell 0 11 3 1" } ) add( new FormComponent( "javax.swing.JButton" ) { name: "saveDirectButton" "text": "Save (no-thread)..." addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "saveDirect", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 10 3 1" + "value": "cell 0 11 3 1" } ) add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) { name: "filesScrollPane" @@ -314,11 +347,16 @@ new FormModel { "rows": 8 } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 11 3 1,growx" + "value": "cell 0 12 3 1,growx" } ) }, new FormLayoutConstraints( null ) { "location": new java.awt.Point( 0, 0 ) - "size": new java.awt.Dimension( 690, 630 ) + "size": new java.awt.Dimension( 845, 630 ) + } ) + add( new FormNonVisual( "javax.swing.ButtonGroup" ) { + name: "ownerButtonGroup" + }, new FormLayoutConstraints( null ) { + "location": new java.awt.Point( 0, 640 ) } ) } }