JAL-4214 Close modal internal frame if JRootPane activity detected. Problem with...
[jalview.git] / src / jalview / gui / JvOptionPane.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import java.awt.AWTEvent;
24 import java.awt.ActiveEvent;
25 import java.awt.Component;
26 import java.awt.Container;
27 import java.awt.Dialog.ModalityType;
28 import java.awt.EventQueue;
29 import java.awt.HeadlessException;
30 import java.awt.MenuComponent;
31 import java.awt.Toolkit;
32 import java.awt.Window;
33 import java.awt.event.ActionEvent;
34 import java.awt.event.ActionListener;
35 import java.awt.event.MouseAdapter;
36 import java.awt.event.MouseMotionAdapter;
37 import java.beans.PropertyChangeEvent;
38 import java.beans.PropertyChangeListener;
39 import java.beans.PropertyVetoException;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.HashMap;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.concurrent.ExecutorService;
46 import java.util.concurrent.Executors;
47
48 import javax.swing.Icon;
49 import javax.swing.JButton;
50 import javax.swing.JDialog;
51 import javax.swing.JFrame;
52 import javax.swing.JInternalFrame;
53 import javax.swing.JLayeredPane;
54 import javax.swing.JOptionPane;
55 import javax.swing.JPanel;
56 import javax.swing.SwingUtilities;
57 import javax.swing.UIManager;
58 import javax.swing.event.InternalFrameEvent;
59 import javax.swing.event.InternalFrameListener;
60
61 import jalview.bin.Console;
62 import jalview.util.ChannelProperties;
63 import jalview.util.MessageManager;
64 import jalview.util.Platform;
65 import jalview.util.dialogrunner.DialogRunnerI;
66
67 public class JvOptionPane extends JOptionPane
68         implements DialogRunnerI, PropertyChangeListener
69 {
70   private static final long serialVersionUID = -3019167117756785229L;
71
72   private static Object mockResponse = JvOptionPane.CANCEL_OPTION;
73
74   private static boolean interactiveMode = true;
75
76   public static final Runnable NULLCALLABLE = () -> {
77   };
78
79   private Component parentComponent;
80
81   private ExecutorService executor = Executors.newCachedThreadPool();
82
83   private JDialog dialog = null;
84
85   private Map<Object, Runnable> callbacks = new HashMap<>();
86
87   /*
88    * JalviewJS reports user choice in the dialog as the selected option (text);
89    * this list allows conversion to index (int)
90    */
91   List<Object> ourOptions;
92
93   public JvOptionPane(final Component parent)
94   {
95     this.parentComponent = Platform.isJS() ? this : parent;
96     this.setIcon(null);
97   }
98
99   public static int showConfirmDialog(Component parentComponent,
100           Object message) throws HeadlessException
101   {
102     // only called by test
103     return isInteractiveMode()
104             ? JOptionPane.showConfirmDialog(parentComponent, message)
105             : (int) getMockResponse();
106   }
107
108   /**
109    * Message, title, optionType
110    * 
111    * @param parentComponent
112    * @param message
113    * @param title
114    * @param optionType
115    * @return
116    * @throws HeadlessException
117    */
118   public static int showConfirmDialog(Component parentComponent,
119           Object message, String title, int optionType)
120           throws HeadlessException
121   {
122     if (!isInteractiveMode())
123     {
124       return (int) getMockResponse();
125     }
126     switch (optionType)
127     {
128     case JvOptionPane.YES_NO_CANCEL_OPTION:
129       // FeatureRenderer amendFeatures ?? TODO ??
130       // Chimera close
131       // PromptUserConfig
132       // $FALL-THROUGH$
133     default:
134     case JvOptionPane.YES_NO_OPTION:
135       // PromptUserConfig usage stats
136       // for now treated as "OK CANCEL"
137       // $FALL-THROUGH$
138     case JvOptionPane.OK_CANCEL_OPTION:
139       // will fall back to simple HTML
140       return JOptionPane.showConfirmDialog(parentComponent, message, title,
141               optionType);
142     }
143   }
144
145   /**
146    * Adds a message type. Fallback is to just add it in the beginning.
147    * 
148    * @param parentComponent
149    * @param message
150    * @param title
151    * @param optionType
152    * @param messageType
153    * @return
154    * @throws HeadlessException
155    */
156   public static int showConfirmDialog(Component parentComponent,
157           Object message, String title, int optionType, int messageType)
158           throws HeadlessException
159   {
160     // JalviewServicesChanged
161     // PromptUserConfig raiseDialog
162     return isInteractiveMode()
163             ? JOptionPane.showConfirmDialog(parentComponent, message, title,
164                     optionType, messageType)
165             : (int) getMockResponse();
166   }
167
168   /**
169    * Adds an icon
170    * 
171    * @param parentComponent
172    * @param message
173    * @param title
174    * @param optionType
175    * @param messageType
176    * @param icon
177    * @return
178    * @throws HeadlessException
179    */
180   public static int showConfirmDialog(Component parentComponent,
181           Object message, String title, int optionType, int messageType,
182           Icon icon) throws HeadlessException
183   {
184     // JvOptionPaneTest only
185     return isInteractiveMode()
186             ? JOptionPane.showConfirmDialog(parentComponent, message, title,
187                     optionType, messageType, icon)
188             : (int) getMockResponse();
189   }
190
191   /**
192    * Internal version "OK"
193    * 
194    * @param parentComponent
195    * @param message
196    * @return
197    */
198   public static int showInternalConfirmDialog(Component parentComponent,
199           Object message)
200   {
201     // JvOptionPaneTest only;
202     return isInteractiveMode()
203             ? JOptionPane.showInternalConfirmDialog(parentComponent,
204                     message)
205             : (int) getMockResponse();
206   }
207
208   /**
209    * Internal version -- changed to standard version for now
210    * 
211    * @param parentComponent
212    * @param message
213    * @param title
214    * @param optionType
215    * @return
216    */
217   public static int showInternalConfirmDialog(Component parentComponent,
218           String message, String title, int optionType)
219   {
220     if (!isInteractiveMode())
221     {
222       return (int) getMockResponse();
223     }
224     switch (optionType)
225     {
226     case JvOptionPane.YES_NO_CANCEL_OPTION:
227       // ColourMenuHelper.addMenuItmers.offerRemoval TODO
228     case JvOptionPane.YES_NO_OPTION:
229       // UserDefinedColoursSave -- relevant? TODO
230       // $FALL-THROUGH$
231     default:
232     case JvOptionPane.OK_CANCEL_OPTION:
233
234       // EditNameDialog --- uses panel for messsage TODO
235
236       // Desktop.inputURLMenuItem
237       // WsPreferenses
238       return JOptionPane.showConfirmDialog(parentComponent, message, title,
239               optionType);
240     }
241   }
242
243   /**
244    * 
245    * @param parentComponent
246    * @param message
247    * @param title
248    * @param optionType
249    * @param messageType
250    * @return
251    */
252   public static int showInternalConfirmDialog(Component parentComponent,
253           Object message, String title, int optionType, int messageType)
254   {
255     if (!isInteractiveMode())
256     {
257       return (int) getMockResponse();
258     }
259     switch (optionType)
260     {
261     case JvOptionPane.YES_NO_CANCEL_OPTION:
262     case JvOptionPane.YES_NO_OPTION:
263       // UserQuestionanaireCheck
264       // VamsasApplication
265       // $FALL-THROUGH$
266     default:
267     case JvOptionPane.OK_CANCEL_OPTION:
268       // will fall back to simple HTML
269       return JOptionPane.showConfirmDialog(parentComponent, message, title,
270               optionType, messageType);
271     }
272   }
273
274   /**
275    * adds icon; no longer internal
276    * 
277    * @param parentComponent
278    * @param message
279    * @param title
280    * @param optionType
281    * @param messageType
282    * @param icon
283    * @return
284    */
285   public static int showInternalConfirmDialog(Component parentComponent,
286           Object message, String title, int optionType, int messageType,
287           Icon icon)
288   {
289     if (!isInteractiveMode())
290     {
291       return (int) getMockResponse();
292     }
293     switch (optionType)
294     {
295     case JvOptionPane.YES_NO_CANCEL_OPTION:
296     case JvOptionPane.YES_NO_OPTION:
297       //$FALL-THROUGH$
298     default:
299     case JvOptionPane.OK_CANCEL_OPTION:
300       // Preferences editLink/newLink
301       return JOptionPane.showConfirmDialog(parentComponent, message, title,
302               optionType, messageType, icon);
303     }
304
305   }
306
307   /**
308    * custom options full-featured
309    * 
310    * @param parentComponent
311    * @param message
312    * @param title
313    * @param optionType
314    * @param messageType
315    * @param icon
316    * @param options
317    * @param initialValue
318    * @return
319    * @throws HeadlessException
320    */
321   public static int showOptionDialog(Component parentComponent,
322           String message, String title, int optionType, int messageType,
323           Icon icon, Object[] options, Object initialValue)
324           throws HeadlessException
325   {
326     if (!isInteractiveMode())
327     {
328       return (int) getMockResponse();
329     }
330     // two uses:
331     //
332     // TODO
333     //
334     // 1) AlignViewport for openLinkedAlignment
335     //
336     // Show a dialog with the option to open and link (cDNA <-> protein) as a
337     // new
338     // alignment, either as a standalone alignment or in a split frame. Returns
339     // true if the new alignment was opened, false if not, because the user
340     // declined the offer.
341     //
342     // 2) UserDefinedColors warning about saving over a name already defined
343     //
344     return JOptionPane.showOptionDialog(parentComponent, message, title,
345             optionType, messageType, icon, options, initialValue);
346   }
347
348   /**
349    * Just an OK message
350    * 
351    * @param message
352    * @throws HeadlessException
353    */
354   public static void showMessageDialog(Component parentComponent,
355           String message) throws HeadlessException
356   {
357     if (!isInteractiveMode())
358     {
359       outputMessage(message);
360       return;
361     }
362
363     // test class only
364
365     JOptionPane.showMessageDialog(parentComponent, message);
366   }
367
368   /**
369    * OK with message, title, and type
370    * 
371    * @param parentComponent
372    * @param message
373    * @param title
374    * @param messageType
375    * @throws HeadlessException
376    */
377   public static void showMessageDialog(Component parentComponent,
378           String message, String title, int messageType)
379           throws HeadlessException
380   {
381     // 30 implementations -- all just fine.
382
383     if (!isInteractiveMode())
384     {
385       outputMessage(message);
386       return;
387     }
388
389     JOptionPane.showMessageDialog(parentComponent,
390             getPrefix(messageType) + message, title, messageType);
391   }
392
393   /**
394    * adds title and icon
395    * 
396    * @param parentComponent
397    * @param message
398    * @param title
399    * @param messageType
400    * @param icon
401    * @throws HeadlessException
402    */
403   public static void showMessageDialog(Component parentComponent,
404           String message, String title, int messageType, Icon icon)
405           throws HeadlessException
406   {
407
408     // test only
409
410     if (!isInteractiveMode())
411     {
412       outputMessage(message);
413       return;
414     }
415
416     JOptionPane.showMessageDialog(parentComponent, message, title,
417             messageType, icon);
418   }
419
420   /**
421    * was internal
422    * 
423    */
424   public static void showInternalMessageDialog(Component parentComponent,
425           Object message)
426   {
427
428     // WsPreferences only
429
430     if (!isInteractiveMode())
431     {
432       outputMessage(message);
433       return;
434     }
435
436     JOptionPane.showMessageDialog(parentComponent, message);
437   }
438
439   /**
440    * Adds title and messageType
441    * 
442    * @param parentComponent
443    * @param message
444    * @param title
445    * @param messageType
446    */
447   public static void showInternalMessageDialog(Component parentComponent,
448           String message, String title, int messageType)
449   {
450
451     // 41 references
452
453     if (!isInteractiveMode())
454     {
455       outputMessage(message);
456       return;
457     }
458
459     JOptionPane.showMessageDialog(parentComponent,
460             getPrefix(messageType) + message, title, messageType);
461   }
462
463   /**
464    * 
465    * @param parentComponent
466    * @param message
467    * @param title
468    * @param messageType
469    * @param icon
470    */
471   public static void showInternalMessageDialog(Component parentComponent,
472           Object message, String title, int messageType, Icon icon)
473   {
474
475     // test only
476
477     if (!isInteractiveMode())
478     {
479       outputMessage(message);
480       return;
481     }
482
483     JOptionPane.showMessageDialog(parentComponent, message, title,
484             messageType, icon);
485   }
486
487   /**
488    * 
489    * @param message
490    * @return
491    * @throws HeadlessException
492    */
493   public static String showInputDialog(Object message)
494           throws HeadlessException
495   {
496     // test only
497
498     if (!isInteractiveMode())
499     {
500       return getMockResponse().toString();
501     }
502
503     return JOptionPane.showInputDialog(message);
504   }
505
506   /**
507    * adds inital selection value
508    * 
509    * @param message
510    * @param initialSelectionValue
511    * @return
512    */
513   public static String showInputDialog(String message,
514           String initialSelectionValue)
515   {
516     if (!isInteractiveMode())
517     {
518       return getMockResponse().toString();
519     }
520
521     // AnnotationPanel character option
522
523     return JOptionPane.showInputDialog(message, initialSelectionValue);
524   }
525
526   /**
527    * adds inital selection value
528    * 
529    * @param message
530    * @param initialSelectionValue
531    * @return
532    */
533   public static String showInputDialog(Object message,
534           Object initialSelectionValue)
535   {
536     if (!isInteractiveMode())
537     {
538       return getMockResponse().toString();
539     }
540
541     // AnnotationPanel character option
542
543     return JOptionPane.showInputDialog(message, initialSelectionValue);
544   }
545
546   /**
547    * centered on parent
548    * 
549    * @param parentComponent
550    * @param message
551    * @return
552    * @throws HeadlessException
553    */
554   public static String showInputDialog(Component parentComponent,
555           String message) throws HeadlessException
556   {
557     // test only
558
559     return isInteractiveMode()
560             ? JOptionPane.showInputDialog(parentComponent, message)
561             : getMockResponse().toString();
562   }
563
564   /**
565    * input with initial selection
566    * 
567    * @param parentComponent
568    * @param message
569    * @param initialSelectionValue
570    * @return
571    */
572   public static String showInputDialog(Component parentComponent,
573           String message, String initialSelectionValue)
574   {
575
576     // AnnotationPanel
577
578     return isInteractiveMode()
579             ? JOptionPane.showInputDialog(parentComponent, message,
580                     initialSelectionValue)
581             : getMockResponse().toString();
582   }
583
584   /**
585    * input with initial selection
586    * 
587    * @param parentComponent
588    * @param message
589    * @param initialSelectionValue
590    * @return
591    */
592   public static String showInputDialog(Component parentComponent,
593           Object message, Object initialSelectionValue)
594   {
595
596     // AnnotationPanel
597
598     return isInteractiveMode()
599             ? JOptionPane.showInputDialog(parentComponent, message,
600                     initialSelectionValue)
601             : getMockResponse().toString();
602   }
603
604   /**
605    * 
606    * @param parentComponent
607    * @param message
608    * @param title
609    * @param messageType
610    * @return
611    * @throws HeadlessException
612    */
613   public static String showInputDialog(Component parentComponent,
614           String message, String title, int messageType)
615           throws HeadlessException
616   {
617
618     // test only
619
620     return isInteractiveMode()
621             ? JOptionPane.showInputDialog(parentComponent, message, title,
622                     messageType)
623             : getMockResponse().toString();
624   }
625
626   /**
627    * Customized input option
628    * 
629    * @param parentComponent
630    * @param message
631    * @param title
632    * @param messageType
633    * @param icon
634    * @param selectionValues
635    * @param initialSelectionValue
636    * @return
637    * @throws HeadlessException
638    */
639   public static Object showInputDialog(Component parentComponent,
640           Object message, String title, int messageType, Icon icon,
641           Object[] selectionValues, Object initialSelectionValue)
642           throws HeadlessException
643   {
644
645     // test only
646
647     return isInteractiveMode()
648             ? JOptionPane.showInputDialog(parentComponent, message, title,
649                     messageType, icon, selectionValues,
650                     initialSelectionValue)
651             : getMockResponse().toString();
652   }
653
654   /**
655    * internal version
656    * 
657    * @param parentComponent
658    * @param message
659    * @return
660    */
661   public static String showInternalInputDialog(Component parentComponent,
662           String message)
663   {
664     // test only
665
666     return isInteractiveMode()
667             ? JOptionPane.showInternalInputDialog(parentComponent, message)
668             : getMockResponse().toString();
669   }
670
671   /**
672    * internal with title and messageType
673    * 
674    * @param parentComponent
675    * @param message
676    * @param title
677    * @param messageType
678    * @return
679    */
680   public static String showInternalInputDialog(Component parentComponent,
681           String message, String title, int messageType)
682   {
683
684     // AlignFrame tabbedPane_mousePressed
685
686     return isInteractiveMode()
687             ? JOptionPane.showInternalInputDialog(parentComponent,
688                     getPrefix(messageType) + message, title, messageType)
689             : getMockResponse().toString();
690   }
691
692   /**
693    * customized internal
694    * 
695    * @param parentComponent
696    * @param message
697    * @param title
698    * @param messageType
699    * @param icon
700    * @param selectionValues
701    * @param initialSelectionValue
702    * @return
703    */
704   public static Object showInternalInputDialog(Component parentComponent,
705           String message, String title, int messageType, Icon icon,
706           Object[] selectionValues, Object initialSelectionValue)
707   {
708     // test only
709
710     return isInteractiveMode()
711             ? JOptionPane.showInternalInputDialog(parentComponent, message,
712                     title, messageType, icon, selectionValues,
713                     initialSelectionValue)
714             : getMockResponse().toString();
715   }
716
717   ///////////// end of options ///////////////
718
719   private static void outputMessage(Object message)
720   {
721     System.out.println(">>> JOption Message : " + message.toString());
722   }
723
724   public static Object getMockResponse()
725   {
726     return mockResponse;
727   }
728
729   public static void setMockResponse(Object mockOption)
730   {
731     JvOptionPane.mockResponse = mockOption;
732   }
733
734   public static void resetMock()
735   {
736     setMockResponse(JvOptionPane.CANCEL_OPTION);
737     setInteractiveMode(true);
738   }
739
740   public static boolean isInteractiveMode()
741   {
742     return interactiveMode;
743   }
744
745   public static void setInteractiveMode(boolean interactive)
746   {
747     JvOptionPane.interactiveMode = interactive;
748   }
749
750   private static String getPrefix(int messageType)
751   {
752     String prefix = "";
753
754     // JavaScript only
755     if (Platform.isJS())
756     {
757       switch (messageType)
758       {
759       case JvOptionPane.WARNING_MESSAGE:
760         prefix = "WARNING! ";
761         break;
762       case JvOptionPane.ERROR_MESSAGE:
763         prefix = "ERROR! ";
764         break;
765       default:
766         prefix = "Note: ";
767       }
768     }
769     return prefix;
770   }
771
772   /**
773    * create a new option dialog that can be used to register responses - along
774    * lines of showOptionDialog
775    * 
776    * @param desktop
777    * @param question
778    * @param string
779    * @param defaultOption
780    * @param plainMessage
781    * @param object
782    * @param options
783    * @param string2
784    * @return
785    */
786   public static JvOptionPane newOptionDialog()
787   {
788     return new JvOptionPane(null);
789   }
790
791   public static JvOptionPane newOptionDialog(Component parentComponent)
792   {
793     return new JvOptionPane(parentComponent);
794   }
795
796   public void showDialog(String message, String title, int optionType,
797           int messageType, Icon icon, Object[] options, Object initialValue)
798   {
799     showDialog(message, title, optionType, messageType, icon, options,
800             initialValue, true);
801   }
802
803   public void showDialog(Object message, String title, int optionType,
804           int messageType, Icon icon, Object[] options, Object initialValue,
805           boolean modal)
806   {
807     showDialog(message, title, optionType, messageType, icon, options,
808             initialValue, modal, null);
809   }
810
811   public void showDialog(Object message, String title, int optionType,
812           int messageType, Icon icon, Object[] options, Object initialValue,
813           boolean modal, JButton[] buttons)
814   {
815     if (!isInteractiveMode())
816     {
817       handleResponse(getMockResponse());
818       return;
819     }
820     // two uses:
821     //
822     // TODO
823     //
824     // 1) AlignViewport for openLinkedAlignment
825     //
826     // Show a dialog with the option to open and link (cDNA <-> protein) as a
827     // new
828     // alignment, either as a standalone alignment or in a split frame. Returns
829     // true if the new alignment was opened, false if not, because the user
830     // declined the offer.
831     //
832     // 2) UserDefinedColors warning about saving over a name already defined
833     //
834
835     ourOptions = Arrays.asList(options);
836
837     if (modal)
838     {
839       boolean useButtons = false;
840       Object initialValueButton = null;
841       NOTNULL: if (buttons != null)
842       {
843         if (buttons.length != options.length)
844         {
845           jalview.bin.Console.error(
846                   "Supplied buttons array not the same length as supplied options array.");
847           break NOTNULL;
848         }
849         int[] buttonActions = { JOptionPane.YES_OPTION,
850             JOptionPane.NO_OPTION, JOptionPane.CANCEL_OPTION };
851         for (int i = 0; i < options.length; i++)
852         {
853           Object o = options[i];
854           jalview.bin.Console.debug(
855                   "Setting button " + i + " to '" + o.toString() + "'");
856           JButton jb = buttons[i];
857
858           if (o.equals(initialValue))
859             initialValueButton = jb;
860
861           int buttonAction = buttonActions[i];
862           Runnable action = callbacks.get(buttonAction);
863           jb.setText((String) o);
864           jb.addActionListener(new ActionListener()
865           {
866             @Override
867             public void actionPerformed(ActionEvent e)
868             {
869
870               Object obj = e.getSource();
871               if (obj == null || !(obj instanceof Component))
872               {
873                 jalview.bin.Console.debug(
874                         "Could not find Component source of event object "
875                                 + obj);
876                 return;
877               }
878               Object joptionpaneObject = SwingUtilities.getAncestorOfClass(
879                       JOptionPane.class, (Component) obj);
880               if (joptionpaneObject == null
881                       || !(joptionpaneObject instanceof JOptionPane))
882               {
883                 jalview.bin.Console.debug(
884                         "Could not find JOptionPane ancestor of event object "
885                                 + obj);
886                 return;
887               }
888               JOptionPane joptionpane = (JOptionPane) joptionpaneObject;
889               joptionpane.setValue(buttonAction);
890               if (action != null)
891                 new Thread(action).start();
892               joptionpane.transferFocusBackward();
893               joptionpane.setVisible(false);
894               // put focus and raise parent window if possible, unless cancel or
895               // no button pressed
896               boolean raiseParent = (parentComponent != null);
897               if (buttonAction == JOptionPane.CANCEL_OPTION)
898                 raiseParent = false;
899               if (optionType == JOptionPane.YES_NO_OPTION
900                       && buttonAction == JOptionPane.NO_OPTION)
901                 raiseParent = false;
902               if (raiseParent)
903               {
904                 parentComponent.requestFocus();
905                 if (parentComponent instanceof JInternalFrame)
906                 {
907                   JInternalFrame jif = (JInternalFrame) parentComponent;
908                   jif.show();
909                   jif.moveToFront();
910                   jif.grabFocus();
911                 }
912                 else if (parentComponent instanceof Window)
913                 {
914                   Window w = (Window) parentComponent;
915                   w.toFront();
916                   w.requestFocus();
917                 }
918               }
919               joptionpane.setVisible(false);
920             }
921           });
922
923         }
924         useButtons = true;
925       }
926       // use a JOptionPane as usual
927       int response = JOptionPane.showOptionDialog(parentComponent, message,
928               title, optionType, messageType, icon,
929               useButtons ? buttons : options,
930               useButtons ? initialValueButton : initialValue);
931
932       /*
933        * In Java, the response is returned to this thread and handled here; (for
934        * Javascript, see propertyChange)
935        */
936       if (!Platform.isJS())
937       /**
938        * Java only
939        * 
940        * @j2sIgnore
941        */
942       {
943         handleResponse(response);
944       }
945     }
946     else
947     {
948       /*
949        * This is java similar to the swingjs handling, with the callbacks attached to
950        * the button press of the dialog. This means we can use a non-modal JDialog for
951        * the confirmation without blocking the GUI.
952        */
953       JOptionPane joptionpane = new JOptionPane();
954       // Make button options
955       int[] buttonActions = { JvOptionPane.YES_OPTION,
956           JvOptionPane.NO_OPTION, JvOptionPane.CANCEL_OPTION };
957
958       // we need the strings to make the buttons with actionEventListener
959       if (options == null)
960       {
961         ArrayList<String> options_default = new ArrayList<>();
962         options_default
963                 .add(UIManager.getString("OptionPane.yesButtonText"));
964         if (optionType == JvOptionPane.YES_NO_OPTION
965                 || optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
966         {
967           options_default
968                   .add(UIManager.getString("OptionPane.noButtonText"));
969         }
970         if (optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
971         {
972           options_default
973                   .add(UIManager.getString("OptionPane.cancelButtonText"));
974         }
975         options = options_default.toArray();
976       }
977
978       ArrayList<JButton> options_btns = new ArrayList<>();
979       Object initialValue_btn = null;
980       if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
981                             // add them here
982       {
983         for (int i = 0; i < options.length && i < 3; i++)
984         {
985           Object o = options[i];
986           int buttonAction = buttonActions[i];
987           Runnable action = callbacks.get(buttonAction);
988           JButton jb = new JButton();
989           jb.setText((String) o);
990           jb.addActionListener(new ActionListener()
991           {
992
993             @Override
994             public void actionPerformed(ActionEvent e)
995             {
996               joptionpane.setValue(buttonAction);
997               if (action != null)
998                 new Thread(action).start();
999               // joptionpane.transferFocusBackward();
1000               joptionpane.transferFocusBackward();
1001               joptionpane.setVisible(false);
1002               // put focus and raise parent window if possible, unless cancel
1003               // button pressed
1004               boolean raiseParent = (parentComponent != null);
1005               if (buttonAction == JvOptionPane.CANCEL_OPTION)
1006                 raiseParent = false;
1007               if (optionType == JvOptionPane.YES_NO_OPTION
1008                       && buttonAction == JvOptionPane.NO_OPTION)
1009                 raiseParent = false;
1010               if (raiseParent)
1011               {
1012                 parentComponent.requestFocus();
1013                 if (parentComponent instanceof JInternalFrame)
1014                 {
1015                   JInternalFrame jif = (JInternalFrame) parentComponent;
1016                   jif.show();
1017                   jif.moveToFront();
1018                   jif.grabFocus();
1019                 }
1020                 else if (parentComponent instanceof Window)
1021                 {
1022                   Window w = (Window) parentComponent;
1023                   w.toFront();
1024                   w.requestFocus();
1025                 }
1026               }
1027               joptionpane.setVisible(false);
1028             }
1029           });
1030           options_btns.add(jb);
1031           if (o.equals(initialValue))
1032             initialValue_btn = jb;
1033         }
1034       }
1035       joptionpane.setMessage(message);
1036       joptionpane.setMessageType(messageType);
1037       joptionpane.setOptionType(optionType);
1038       joptionpane.setIcon(icon);
1039       joptionpane.setOptions(
1040               Platform.isJS() ? options : options_btns.toArray());
1041       joptionpane.setInitialValue(
1042               Platform.isJS() ? initialValue : initialValue_btn);
1043
1044       JDialog dialog = joptionpane.createDialog(parentComponent, title);
1045       dialog.setIconImages(ChannelProperties.getIconList());
1046       dialog.setModalityType(modal ? ModalityType.APPLICATION_MODAL
1047               : ModalityType.MODELESS);
1048       dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
1049       dialog.setVisible(true);
1050       setDialog(dialog);
1051     }
1052   }
1053
1054   public void showInternalDialog(Object mainPanel, String title,
1055           int yesNoCancelOption, int questionMessage, Icon icon,
1056           Object[] options, String initresponse)
1057   {
1058     if (!isInteractiveMode())
1059     {
1060       handleResponse(getMockResponse());
1061     }
1062
1063     // need to set these separately so we can set the title bar icon later
1064     this.setOptionType(yesNoCancelOption);
1065     this.setMessageType(questionMessage);
1066     this.setIcon(icon);
1067     this.setInitialValue(initresponse);
1068     this.setOptions(options);
1069     this.setMessage(mainPanel);
1070
1071     ourOptions = Arrays.asList(options);
1072     if (parentComponent != this
1073             && !(parentComponent == null && Desktop.instance == null))
1074     {
1075       JInternalFrame jif = this.createInternalFrame(
1076               parentComponent != null ? parentComponent : Desktop.instance,
1077               title);
1078       jif.setFrameIcon(null);
1079       jif.addInternalFrameListener(new InternalFrameListener()
1080       {
1081         @Override
1082         public void internalFrameActivated(InternalFrameEvent arg0)
1083         {
1084           System.err.println("##### internalFrameActivated");
1085         }
1086
1087         @Override
1088         public void internalFrameClosed(InternalFrameEvent arg0)
1089         {
1090           System.err.println("##### internalFrameClosed");
1091           JvOptionPane.this.internalDialogHandleResponse();
1092         }
1093
1094         @Override
1095         public void internalFrameClosing(InternalFrameEvent arg0)
1096         {
1097           System.err.println("##### internalFrameClosing");
1098         }
1099
1100         @Override
1101         public void internalFrameDeactivated(InternalFrameEvent arg0)
1102         {
1103           System.err.println("##### internalFrameDeactivated");
1104         }
1105
1106         @Override
1107         public void internalFrameDeiconified(InternalFrameEvent arg0)
1108         {
1109           System.err.println("##### internalFrameDeiconified");
1110         }
1111
1112         @Override
1113         public void internalFrameIconified(InternalFrameEvent arg0)
1114         {
1115           System.err.println("##### internalFrameIconified");
1116         }
1117
1118         @Override
1119         public void internalFrameOpened(InternalFrameEvent arg0)
1120         {
1121           System.err.println("##### internalFrameOpened");
1122         }
1123       });
1124       jif.setVisible(true);
1125       startModal(jif);
1126       return;
1127     }
1128     else
1129     {
1130       JDialog dialog = this.createDialog(parentComponent, title);
1131       dialog.setIconImages(ChannelProperties.getIconList());
1132       dialog.setVisible(true); // blocking
1133       this.internalDialogHandleResponse();
1134       return;
1135     }
1136   }
1137
1138   private void internalDialogHandleResponse()
1139   {
1140     String responseString = (String) this.getValue();
1141     int response = ourOptions.indexOf(responseString);
1142
1143     if (!Platform.isJS())
1144     /**
1145      * Java only
1146      * 
1147      * @j2sIgnore
1148      */
1149     {
1150       handleResponse(response);
1151     }
1152   }
1153
1154   /*
1155    * @Override public JvOptionPane setResponseHandler(Object response, Runnable
1156    * action) { callbacks.put(response, new Callable<Void>() {
1157    * 
1158    * @Override public Void call() { action.run(); return null; } }); return this;
1159    * }
1160    */
1161   @Override
1162   public JvOptionPane setResponseHandler(Object response, Runnable action)
1163   {
1164     if (action == null)
1165     {
1166       action = NULLCALLABLE;
1167     }
1168     callbacks.put(response, action);
1169     return this;
1170   }
1171
1172   public void setDialog(JDialog d)
1173   {
1174     dialog = d;
1175   }
1176
1177   public JDialog getDialog()
1178   {
1179     return dialog;
1180   }
1181
1182   /**
1183    * showDialogOnTop will create a dialog that (attempts to) come to top of OS
1184    * desktop windows
1185    */
1186   public static int showDialogOnTop(String label, String actionString,
1187           int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE)
1188   {
1189     return showDialogOnTop(null, label, actionString, JOPTIONPANE_OPTION,
1190             JOPTIONPANE_MESSAGETYPE);
1191   }
1192
1193   public static int showDialogOnTop(Component dialogParentComponent,
1194           String label, String actionString, int JOPTIONPANE_OPTION,
1195           int JOPTIONPANE_MESSAGETYPE)
1196   {
1197     if (!isInteractiveMode())
1198     {
1199       return (int) getMockResponse();
1200     }
1201     // Ensure Jalview window is brought to front (primarily for Quit
1202     // confirmation window to be visible)
1203
1204     // This method of raising the Jalview window is broken in java
1205     // jalviewDesktop.setVisible(true);
1206     // jalviewDesktop.toFront();
1207
1208     // A better hack which works is to create a new JFrame parent with
1209     // setAlwaysOnTop(true)
1210     JFrame dialogParent = new JFrame();
1211     if (dialogParentComponent == null)
1212     {
1213       dialogParent.setIconImages(ChannelProperties.getIconList());
1214       dialogParent.setAlwaysOnTop(true);
1215     }
1216
1217     int answer = JOptionPane.showConfirmDialog(
1218             dialogParentComponent == null ? dialogParent
1219                     : dialogParentComponent,
1220             label, actionString, JOPTIONPANE_OPTION,
1221             JOPTIONPANE_MESSAGETYPE);
1222
1223     if (dialogParentComponent == null)
1224     {
1225       dialogParent.setAlwaysOnTop(false);
1226       dialogParent.dispose();
1227     }
1228
1229     return answer;
1230   }
1231
1232   public void showDialogOnTopAsync(String label, String actionString,
1233           int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE, Icon icon,
1234           Object[] options, Object initialValue, boolean modal)
1235   {
1236     JFrame frame = new JFrame();
1237     frame.setIconImages(ChannelProperties.getIconList());
1238     showDialogOnTopAsync(frame, label, actionString, JOPTIONPANE_OPTION,
1239             JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal);
1240   }
1241
1242   public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1243           String actionString, int JOPTIONPANE_OPTION,
1244           int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1245           Object initialValue, boolean modal)
1246   {
1247     showDialogOnTopAsync(dialogParent, label, actionString,
1248             JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE, icon, options,
1249             initialValue, modal, null);
1250   }
1251
1252   public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1253           String actionString, int JOPTIONPANE_OPTION,
1254           int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1255           Object initialValue, boolean modal, JButton[] buttons)
1256   {
1257     if (!isInteractiveMode())
1258     {
1259       handleResponse(getMockResponse());
1260       return;
1261     }
1262     // Ensure Jalview window is brought to front (primarily for Quit
1263     // confirmation window to be visible)
1264
1265     // This method of raising the Jalview window is broken in java
1266     // jalviewDesktop.setVisible(true);
1267     // jalviewDesktop.toFront();
1268
1269     // A better hack which works is to create a new JFrame parent with
1270     // setAlwaysOnTop(true)
1271     dialogParent.setAlwaysOnTop(true);
1272     parentComponent = dialogParent;
1273
1274     showDialog(label, actionString, JOPTIONPANE_OPTION,
1275             JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal,
1276             buttons);
1277
1278     dialogParent.setAlwaysOnTop(false);
1279     dialogParent.dispose();
1280   }
1281
1282   /**
1283    * JalviewJS signals option selection by a property change event for the
1284    * option e.g. "OK". This methods responds to that by running the response
1285    * action that corresponds to that option.
1286    * 
1287    * @param evt
1288    */
1289   @Override
1290   public void propertyChange(PropertyChangeEvent evt)
1291   {
1292     Object newValue = evt.getNewValue();
1293     int ourOption = ourOptions.indexOf(newValue);
1294     if (ourOption >= 0)
1295     {
1296       handleResponse(ourOption);
1297     }
1298     else
1299     {
1300       // try our luck..
1301       handleResponse(newValue);
1302     }
1303   }
1304
1305   @Override
1306   public void handleResponse(Object response)
1307   {
1308     /*
1309      * this test is for NaN in Chrome
1310      */
1311     if (response != null && !response.equals(response))
1312     {
1313       return;
1314     }
1315     Runnable action = callbacks.get(response);
1316     if (action != null)
1317     {
1318       try
1319       {
1320         new Thread(action).start();
1321         // action.call();
1322       } catch (Exception e)
1323       {
1324         e.printStackTrace();
1325       }
1326       if (parentComponent != null)
1327         parentComponent.requestFocus();
1328     }
1329   }
1330
1331   /**
1332    * Create a non-modal confirm dialog
1333    */
1334   public JDialog createDialog(Component parentComponent, Object message,
1335           String title, int optionType, int messageType, Icon icon,
1336           Object[] options, Object initialValue, boolean modal)
1337   {
1338     return createDialog(parentComponent, message, title, optionType,
1339             messageType, icon, options, initialValue, modal, null);
1340   }
1341
1342   public JDialog createDialog(Component parentComponent, Object message,
1343           String title, int optionType, int messageType, Icon icon,
1344           Object[] options, Object initialValue, boolean modal,
1345           JButton[] buttons)
1346   {
1347     if (!isInteractiveMode())
1348     {
1349       handleResponse(getMockResponse());
1350       return null;
1351     }
1352     JButton[] optionsButtons = null;
1353     Object initialValueButton = null;
1354     JOptionPane joptionpane = new JOptionPane();
1355     // Make button options
1356     int[] buttonActions = { JOptionPane.YES_OPTION, JOptionPane.NO_OPTION,
1357         JOptionPane.CANCEL_OPTION };
1358
1359     // we need the strings to make the buttons with actionEventListener
1360     if (options == null)
1361     {
1362       ArrayList<String> options_default = new ArrayList<>();
1363       options_default.add(UIManager.getString("OptionPane.yesButtonText"));
1364       if (optionType == JOptionPane.YES_NO_OPTION
1365               || optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1366       {
1367         options_default.add(UIManager.getString("OptionPane.noButtonText"));
1368       }
1369       if (optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1370       {
1371         options_default
1372                 .add(UIManager.getString("OptionPane.cancelButtonText"));
1373       }
1374       options = options_default.toArray();
1375     }
1376     if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
1377                           // add them here
1378     {
1379       if (((optionType == JOptionPane.YES_OPTION
1380               || optionType == JOptionPane.NO_OPTION
1381               || optionType == JOptionPane.CANCEL_OPTION
1382               || optionType == JOptionPane.OK_OPTION
1383               || optionType == JOptionPane.DEFAULT_OPTION)
1384               && options.length < 1)
1385               || ((optionType == JOptionPane.YES_NO_OPTION
1386                       || optionType == JOptionPane.OK_CANCEL_OPTION)
1387                       && options.length < 2)
1388               || (optionType == JOptionPane.YES_NO_CANCEL_OPTION
1389                       && options.length < 3))
1390       {
1391         jalview.bin.Console
1392                 .debug("JvOptionPane: not enough options for dialog type");
1393       }
1394       optionsButtons = new JButton[options.length];
1395       for (int i = 0; i < options.length && i < 3; i++)
1396       {
1397         Object o = options[i];
1398         int buttonAction = buttonActions[i];
1399         Runnable action = callbacks.get(buttonAction);
1400         JButton jb;
1401         if (buttons != null && buttons.length > i && buttons[i] != null)
1402         {
1403           jb = buttons[i];
1404         }
1405         else
1406         {
1407           jb = new JButton();
1408         }
1409         jb.setText((String) o);
1410         jb.addActionListener(new ActionListener()
1411         {
1412           @Override
1413           public void actionPerformed(ActionEvent e)
1414           {
1415             joptionpane.setValue(buttonAction);
1416             if (action != null)
1417               new Thread(action).start();
1418             // joptionpane.transferFocusBackward();
1419             joptionpane.transferFocusBackward();
1420             joptionpane.setVisible(false);
1421             // put focus and raise parent window if possible, unless cancel
1422             // button pressed
1423             boolean raiseParent = (parentComponent != null);
1424             if (buttonAction == JOptionPane.CANCEL_OPTION)
1425               raiseParent = false;
1426             if (optionType == JOptionPane.YES_NO_OPTION
1427                     && buttonAction == JOptionPane.NO_OPTION)
1428               raiseParent = false;
1429             if (raiseParent)
1430             {
1431               parentComponent.requestFocus();
1432               if (parentComponent instanceof JInternalFrame)
1433               {
1434                 JInternalFrame jif = (JInternalFrame) parentComponent;
1435                 jif.show();
1436                 jif.moveToFront();
1437                 jif.grabFocus();
1438               }
1439               else if (parentComponent instanceof Window)
1440               {
1441                 Window w = (Window) parentComponent;
1442                 w.toFront();
1443                 w.requestFocus();
1444               }
1445             }
1446             joptionpane.setVisible(false);
1447           }
1448         });
1449         optionsButtons[i] = jb;
1450         if (o.equals(initialValue))
1451           initialValueButton = jb;
1452       }
1453     }
1454     joptionpane.setMessage(message);
1455     joptionpane.setMessageType(messageType);
1456     joptionpane.setOptionType(optionType);
1457     joptionpane.setIcon(icon);
1458     joptionpane.setOptions(Platform.isJS() ? options : optionsButtons);
1459     joptionpane.setInitialValue(
1460             Platform.isJS() ? initialValue : initialValueButton);
1461
1462     JDialog dialog = joptionpane.createDialog(parentComponent, title);
1463     dialog.setIconImages(ChannelProperties.getIconList());
1464     dialog.setModalityType(
1465             modal ? ModalityType.APPLICATION_MODAL : ModalityType.MODELESS);
1466     dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
1467     setDialog(dialog);
1468     return dialog;
1469   }
1470
1471   /**
1472    * Utility to programmatically click a button on a JOptionPane (as a JFrame)
1473    * 
1474    * returns true if button was found
1475    */
1476   public static boolean clickButton(JFrame frame, int buttonType)
1477   {
1478
1479     return false;
1480   }
1481
1482   /**
1483    * This helper method makes the JInternalFrame wait until it is notified by an
1484    * InternalFrameClosing event. This method also adds the given JOptionPane to
1485    * the JInternalFrame and sizes it according to the JInternalFrame's preferred
1486    * size.
1487    *
1488    * @param f
1489    *          The JInternalFrame to make modal.
1490    */
1491   private static void startModal(JInternalFrame f)
1492   {
1493     // We need to add an additional glasspane-like component directly
1494     // below the frame, which intercepts all mouse events that are not
1495     // directed at the frame itself.
1496     JPanel modalInterceptor = new JPanel();
1497     modalInterceptor.setOpaque(false);
1498     JLayeredPane lp = JLayeredPane.getLayeredPaneAbove(f);
1499     lp.setLayer(modalInterceptor, JLayeredPane.MODAL_LAYER.intValue());
1500     modalInterceptor.setBounds(0, 0, lp.getWidth(), lp.getHeight());
1501     modalInterceptor.addMouseListener(new MouseAdapter()
1502     {
1503     });
1504     modalInterceptor.addMouseMotionListener(new MouseMotionAdapter()
1505     {
1506     });
1507     lp.add(modalInterceptor);
1508     f.toFront();
1509
1510     // We need to explicitly dispatch events when we are blocking the event
1511     // dispatch thread.
1512     EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
1513     try
1514     {
1515       boolean stillModal = true;
1516       while (!f.isClosed() && stillModal)
1517       {
1518         if (EventQueue.isDispatchThread())
1519         {
1520           // The getNextEventMethod() issues wait() when no
1521           // event is available, so we don't need do explicitly wait().
1522           AWTEvent ev = queue.getNextEvent();
1523           // This mimics EventQueue.dispatchEvent(). We can't use
1524           // EventQueue.dispatchEvent() directly, because it is
1525           // protected, unfortunately.
1526           System.out.println(
1527                   "##### ev source=" + ev.getSource().getClass() + "");
1528           if (ev instanceof ActiveEvent)
1529           {
1530             System.err.println("##### 1");
1531             ((ActiveEvent) ev).dispatch();
1532           }
1533           else if (ev.getSource() instanceof MenuComponent)
1534           {
1535             System.err.println("##### 2");
1536             ((MenuComponent) ev.getSource()).dispatchEvent(ev);
1537           }
1538           else if (ev.getSource() instanceof Component)
1539           {
1540             System.err.println("##### 3");
1541             if (ev.getSource().equals(Desktop.getDesktop().getRootPane()))
1542             {
1543               stillModal = false;
1544             }
1545             else
1546             {
1547               ((Component) ev.getSource()).dispatchEvent(ev);
1548             }
1549           }
1550           // Other events are ignored as per spec in
1551           // EventQueue.dispatchEvent
1552           System.err.println("##### 4");
1553         }
1554         else
1555         {
1556           // Give other threads a chance to become active.
1557           System.err.println("##### 5");
1558           Thread.yield();
1559         }
1560       }
1561     } catch (InterruptedException ex)
1562     {
1563       // If we get interrupted, then leave the modal state.
1564       System.err.println("##### 6");
1565     } finally
1566     {
1567       System.err.println("##### 7");
1568       // Clean up the modal interceptor.
1569       lp.remove(modalInterceptor);
1570
1571       f.setVisible(false);
1572
1573       try
1574       {
1575         f.setClosed(true);
1576       } catch (PropertyVetoException e)
1577       {
1578         f.doDefaultCloseAction();
1579       }
1580
1581       // Remove the internal frame from its parent, so it is no longer
1582       // lurking around and clogging memory.
1583       Container parent = f.getParent();
1584       if (parent != null)
1585       {
1586         parent.remove(f);
1587       }
1588     }
1589   }
1590
1591   public static JvOptionPane frameDialog(Object message, String title,
1592           int messageType, String[] buttonsTextS, String defaultButtonS,
1593           List<Runnable> handlers, boolean modal)
1594   {
1595     JFrame parent = new JFrame();
1596     JvOptionPane jvop = JvOptionPane.newOptionDialog();
1597     final String[] buttonsText;
1598     final String defaultButton;
1599     if (buttonsTextS == null)
1600     {
1601       String ok = MessageManager.getString("action.ok");
1602       buttonsText = new String[] { ok };
1603       defaultButton = ok;
1604     }
1605     else
1606     {
1607       buttonsText = buttonsTextS;
1608       defaultButton = defaultButtonS;
1609     }
1610     JButton[] buttons = new JButton[buttonsText.length];
1611     for (int i = 0; i < buttonsText.length; i++)
1612     {
1613       buttons[i] = new JButton();
1614       buttons[i].setText(buttonsText[i]);
1615       Console.debug("DISABLING BUTTON " + buttons[i].getText());
1616       buttons[i].setEnabled(false);
1617       buttons[i].setVisible(false);
1618     }
1619
1620     int dialogType = -1;
1621     if (buttonsText.length == 1)
1622     {
1623       dialogType = JOptionPane.OK_OPTION;
1624     }
1625     else if (buttonsText.length == 2)
1626     {
1627       dialogType = JOptionPane.YES_NO_OPTION;
1628     }
1629     else
1630     {
1631       dialogType = JOptionPane.YES_NO_CANCEL_OPTION;
1632     }
1633     jvop.setResponseHandler(JOptionPane.YES_OPTION,
1634             (handlers != null && handlers.size() > 0) ? handlers.get(0)
1635                     : NULLCALLABLE);
1636     if (dialogType == JOptionPane.YES_NO_OPTION
1637             || dialogType == JOptionPane.YES_NO_CANCEL_OPTION)
1638     {
1639       jvop.setResponseHandler(JOptionPane.NO_OPTION,
1640               (handlers != null && handlers.size() > 1) ? handlers.get(1)
1641                       : NULLCALLABLE);
1642     }
1643     if (dialogType == JOptionPane.YES_NO_CANCEL_OPTION)
1644     {
1645       jvop.setResponseHandler(JOptionPane.CANCEL_OPTION,
1646               (handlers != null && handlers.size() > 2) ? handlers.get(2)
1647                       : NULLCALLABLE);
1648     }
1649
1650     final int dt = dialogType;
1651     new Thread(() -> {
1652       jvop.showDialog(message, title, dt, messageType, null, buttonsText,
1653               defaultButton, modal, buttons);
1654     }).start();
1655
1656     return jvop;
1657   }
1658 }