|
16 | 16 |
|
17 | 17 | package com.formdev.flatlaf.util; |
18 | 18 |
|
| 19 | +import java.awt.AWTEvent; |
19 | 20 | import java.awt.Component; |
20 | 21 | import java.awt.Dimension; |
21 | 22 | import java.awt.EventQueue; |
|
24 | 25 | import java.awt.SecondaryLoop; |
25 | 26 | import java.awt.Toolkit; |
26 | 27 | import java.awt.Window; |
| 28 | +import java.awt.event.ActionEvent; |
| 29 | +import java.awt.event.KeyEvent; |
| 30 | +import java.awt.event.MouseEvent; |
27 | 31 | import java.io.File; |
28 | 32 | import java.lang.reflect.InvocationTargetException; |
29 | 33 | import java.lang.reflect.Method; |
@@ -773,7 +777,8 @@ private int showDialogImpl( Component parent ) { |
773 | 777 | } |
774 | 778 |
|
775 | 779 | private FileChooserProvider getProvider() { |
776 | | - if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_SYSTEM_FILE_CHOOSER, true ) ) |
| 780 | + if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_SYSTEM_FILE_CHOOSER, true ) || |
| 781 | + SystemInfo.isProjector || SystemInfo.isWebswing || SystemInfo.isWinPE ) |
777 | 782 | return new SwingFileChooserProvider(); |
778 | 783 |
|
779 | 784 | if( SystemInfo.isWindows_10_orLater && FlatNativeWindowsLibrary.isLoaded() ) |
@@ -810,14 +815,25 @@ public File[] showDialog( Window owner, SystemFileChooser fc ) { |
810 | 815 | } |
811 | 816 |
|
812 | 817 | AtomicReference<String[]> filenamesRef = new AtomicReference<>(); |
| 818 | + InputBlockingEventQueue queue = InputBlockingEventQueue.install(); |
813 | 819 |
|
814 | | - // create secondary event look and invoke system file dialog on a new thread |
815 | | - SecondaryLoop secondaryLoop = Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop(); |
816 | | - new Thread( () -> { |
817 | | - filenamesRef.set( showSystemDialog( owner, fc ) ); |
818 | | - secondaryLoop.exit(); |
819 | | - }, "FlatLaf SystemFileChooser" ).start(); |
820 | | - secondaryLoop.enter(); |
| 820 | + try { |
| 821 | + // create secondary event look and invoke system file dialog on a new thread |
| 822 | + SecondaryLoop secondaryLoop = Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop(); |
| 823 | + new Thread( () -> { |
| 824 | + try { |
| 825 | + filenamesRef.set( showSystemDialog( owner, fc ) ); |
| 826 | + } finally { |
| 827 | + secondaryLoop.exit(); |
| 828 | + } |
| 829 | + }, "FlatLaf SystemFileChooser" ).start(); |
| 830 | + |
| 831 | + // enter secondary loop, which waits until system file dialog is closed |
| 832 | + secondaryLoop.enter(); |
| 833 | + } finally { |
| 834 | + if( queue != null ) |
| 835 | + queue.uninstall(); |
| 836 | + } |
821 | 837 |
|
822 | 838 | // dispose dummy window to allow AWT to auto-shutdown |
823 | 839 | if( dummyWindow != null ) |
@@ -870,6 +886,72 @@ private static boolean hasDisplayableWindow( Window owner ) { |
870 | 886 | } |
871 | 887 | return false; |
872 | 888 | } |
| 889 | + |
| 890 | + //---- class InputBlockingEventQueue ---------------------------------- |
| 891 | + |
| 892 | + /** |
| 893 | + * Used while system file dialog is displayed to block some Swing events. |
| 894 | + * This is similar to what Swing does when showing modal dialogs. |
| 895 | + * See class {@code java.awt.ModalEventFilter}. |
| 896 | + * <p> |
| 897 | + * Without this, it would be possible for the user to e.g. press some |
| 898 | + * shortcut keys during creation of system file dialog, which is done |
| 899 | + * on another thread, and open additional dialogs. |
| 900 | + * E.g. press Ctrl+O twice, could open two file dialogs. |
| 901 | + */ |
| 902 | + private static class InputBlockingEventQueue |
| 903 | + extends EventQueue |
| 904 | + { |
| 905 | + private static boolean problemLogged; |
| 906 | + |
| 907 | + static InputBlockingEventQueue install() { |
| 908 | + InputBlockingEventQueue queue = new InputBlockingEventQueue(); |
| 909 | + |
| 910 | + try { |
| 911 | + Toolkit toolkit = Toolkit.getDefaultToolkit(); |
| 912 | + toolkit.getSystemEventQueue().push( queue ); |
| 913 | + |
| 914 | + // check whether push() worked |
| 915 | + // (e.g. SWTSwing uses own event queue that does not support push()) |
| 916 | + if( toolkit.getSystemEventQueue() != queue ) { |
| 917 | + logEventQueuePushProblem( null ); |
| 918 | + return null; |
| 919 | + } |
| 920 | + |
| 921 | + return queue; |
| 922 | + } catch( RuntimeException ex ) { |
| 923 | + // catch runtime exception from EventQueue.push() |
| 924 | + logEventQueuePushProblem( ex ); |
| 925 | + return null; |
| 926 | + } |
| 927 | + } |
| 928 | + |
| 929 | + private static void logEventQueuePushProblem( Throwable t ) { |
| 930 | + if( problemLogged ) |
| 931 | + return; |
| 932 | + problemLogged = true; |
| 933 | + |
| 934 | + LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to push input-blocking event queue.", t ); |
| 935 | + } |
| 936 | + |
| 937 | + void uninstall() { |
| 938 | + super.pop(); |
| 939 | + } |
| 940 | + |
| 941 | + @Override |
| 942 | + protected void dispatchEvent( AWTEvent event ) { |
| 943 | + int eventID = event.getID(); |
| 944 | + if( (eventID >= MouseEvent.MOUSE_FIRST && eventID <= MouseEvent.MOUSE_LAST) || |
| 945 | + (eventID >= KeyEvent.KEY_FIRST && eventID <= KeyEvent.KEY_LAST) || |
| 946 | + (eventID >= ActionEvent.ACTION_FIRST && eventID <= ActionEvent.ACTION_LAST) ) |
| 947 | + { |
| 948 | +// System.out.println( "BLOCKED " + event ); |
| 949 | + return; |
| 950 | + } |
| 951 | + |
| 952 | + super.dispatchEvent( event ); |
| 953 | + } |
| 954 | + } |
873 | 955 | } |
874 | 956 |
|
875 | 957 | //---- class WindowsFileChooserProvider ----------------------------------- |
|
0 commit comments