Merge branch 'bug/JAL-4214_system_freeze_when_opening_file_using_gui_in_linux' into...
[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     jalview.bin.Console.outPrintln(">>> 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     Object value = this.getValue();
1141     if (value == null
1142             || (value instanceof Integer && (Integer) value == -1))
1143     {
1144       return;
1145     }
1146     String responseString = value.toString();
1147     int response = ourOptions.indexOf(responseString);
1148
1149     if (!Platform.isJS())
1150     /**
1151      * Java only
1152      * 
1153      * @j2sIgnore
1154      */
1155     {
1156       handleResponse(response);
1157     }
1158   }
1159
1160   /*
1161    * @Override public JvOptionPane setResponseHandler(Object response, Runnable
1162    * action) { callbacks.put(response, new Callable<Void>() {
1163    * 
1164    * @Override public Void call() { action.run(); return null; } }); return this;
1165    * }
1166    */
1167   @Override
1168   public JvOptionPane setResponseHandler(Object response, Runnable action)
1169   {
1170     if (action == null)
1171     {
1172       action = NULLCALLABLE;
1173     }
1174     callbacks.put(response, action);
1175     return this;
1176   }
1177
1178   public void setDialog(JDialog d)
1179   {
1180     dialog = d;
1181   }
1182
1183   public JDialog getDialog()
1184   {
1185     return dialog;
1186   }
1187
1188   /**
1189    * showDialogOnTop will create a dialog that (attempts to) come to top of OS
1190    * desktop windows
1191    */
1192   public static int showDialogOnTop(String label, String actionString,
1193           int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE)
1194   {
1195     return showDialogOnTop(null, label, actionString, JOPTIONPANE_OPTION,
1196             JOPTIONPANE_MESSAGETYPE);
1197   }
1198
1199   public static int showDialogOnTop(Component dialogParentComponent,
1200           String label, String actionString, int JOPTIONPANE_OPTION,
1201           int JOPTIONPANE_MESSAGETYPE)
1202   {
1203     if (!isInteractiveMode())
1204     {
1205       return (int) getMockResponse();
1206     }
1207     // Ensure Jalview window is brought to front (primarily for Quit
1208     // confirmation window to be visible)
1209
1210     // This method of raising the Jalview window is broken in java
1211     // jalviewDesktop.setVisible(true);
1212     // jalviewDesktop.toFront();
1213
1214     // A better hack which works is to create a new JFrame parent with
1215     // setAlwaysOnTop(true)
1216     JFrame dialogParent = new JFrame();
1217     if (dialogParentComponent == null)
1218     {
1219       dialogParent.setIconImages(ChannelProperties.getIconList());
1220       dialogParent.setAlwaysOnTop(true);
1221     }
1222
1223     int answer = JOptionPane.showConfirmDialog(
1224             dialogParentComponent == null ? dialogParent
1225                     : dialogParentComponent,
1226             label, actionString, JOPTIONPANE_OPTION,
1227             JOPTIONPANE_MESSAGETYPE);
1228
1229     if (dialogParentComponent == null)
1230     {
1231       dialogParent.setAlwaysOnTop(false);
1232       dialogParent.dispose();
1233     }
1234
1235     return answer;
1236   }
1237
1238   public void showDialogOnTopAsync(String label, String actionString,
1239           int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE, Icon icon,
1240           Object[] options, Object initialValue, boolean modal)
1241   {
1242     JFrame frame = new JFrame();
1243     frame.setIconImages(ChannelProperties.getIconList());
1244     showDialogOnTopAsync(frame, label, actionString, JOPTIONPANE_OPTION,
1245             JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal);
1246   }
1247
1248   public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1249           String actionString, int JOPTIONPANE_OPTION,
1250           int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1251           Object initialValue, boolean modal)
1252   {
1253     showDialogOnTopAsync(dialogParent, label, actionString,
1254             JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE, icon, options,
1255             initialValue, modal, null);
1256   }
1257
1258   public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1259           String actionString, int JOPTIONPANE_OPTION,
1260           int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1261           Object initialValue, boolean modal, JButton[] buttons)
1262   {
1263     if (!isInteractiveMode())
1264     {
1265       handleResponse(getMockResponse());
1266       return;
1267     }
1268     // Ensure Jalview window is brought to front (primarily for Quit
1269     // confirmation window to be visible)
1270
1271     // This method of raising the Jalview window is broken in java
1272     // jalviewDesktop.setVisible(true);
1273     // jalviewDesktop.toFront();
1274
1275     // A better hack which works is to create a new JFrame parent with
1276     // setAlwaysOnTop(true)
1277     dialogParent.setAlwaysOnTop(true);
1278     parentComponent = dialogParent;
1279
1280     showDialog(label, actionString, JOPTIONPANE_OPTION,
1281             JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal,
1282             buttons);
1283
1284     dialogParent.setAlwaysOnTop(false);
1285     dialogParent.dispose();
1286   }
1287
1288   /**
1289    * JalviewJS signals option selection by a property change event for the
1290    * option e.g. "OK". This methods responds to that by running the response
1291    * action that corresponds to that option.
1292    * 
1293    * @param evt
1294    */
1295   @Override
1296   public void propertyChange(PropertyChangeEvent evt)
1297   {
1298     Object newValue = evt.getNewValue();
1299     int ourOption = ourOptions.indexOf(newValue);
1300     if (ourOption >= 0)
1301     {
1302       handleResponse(ourOption);
1303     }
1304     else
1305     {
1306       // try our luck..
1307       handleResponse(newValue);
1308     }
1309   }
1310
1311   @Override
1312   public void handleResponse(Object response)
1313   {
1314     /*
1315      * this test is for NaN in Chrome
1316      */
1317     if (response != null && !response.equals(response))
1318     {
1319       return;
1320     }
1321     Runnable action = callbacks.get(response);
1322     if (action != null)
1323     {
1324       try
1325       {
1326         new Thread(action).start();
1327         // action.call();
1328       } catch (Exception e)
1329       {
1330         e.printStackTrace();
1331       }
1332       if (parentComponent != null)
1333         parentComponent.requestFocus();
1334     }
1335   }
1336
1337   /**
1338    * Create a non-modal confirm dialog
1339    */
1340   public JDialog createDialog(Component parentComponent, Object message,
1341           String title, int optionType, int messageType, Icon icon,
1342           Object[] options, Object initialValue, boolean modal)
1343   {
1344     return createDialog(parentComponent, message, title, optionType,
1345             messageType, icon, options, initialValue, modal, null);
1346   }
1347
1348   public JDialog createDialog(Component parentComponent, Object message,
1349           String title, int optionType, int messageType, Icon icon,
1350           Object[] options, Object initialValue, boolean modal,
1351           JButton[] buttons)
1352   {
1353     if (!isInteractiveMode())
1354     {
1355       handleResponse(getMockResponse());
1356       return null;
1357     }
1358     JButton[] optionsButtons = null;
1359     Object initialValueButton = null;
1360     JOptionPane joptionpane = new JOptionPane();
1361     // Make button options
1362     int[] buttonActions = { JOptionPane.YES_OPTION, JOptionPane.NO_OPTION,
1363         JOptionPane.CANCEL_OPTION };
1364
1365     // we need the strings to make the buttons with actionEventListener
1366     if (options == null)
1367     {
1368       ArrayList<String> options_default = new ArrayList<>();
1369       options_default.add(UIManager.getString("OptionPane.yesButtonText"));
1370       if (optionType == JOptionPane.YES_NO_OPTION
1371               || optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1372       {
1373         options_default.add(UIManager.getString("OptionPane.noButtonText"));
1374       }
1375       if (optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1376       {
1377         options_default
1378                 .add(UIManager.getString("OptionPane.cancelButtonText"));
1379       }
1380       options = options_default.toArray();
1381     }
1382     if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
1383                           // add them here
1384     {
1385       if (((optionType == JOptionPane.YES_OPTION
1386               || optionType == JOptionPane.NO_OPTION
1387               || optionType == JOptionPane.CANCEL_OPTION
1388               || optionType == JOptionPane.OK_OPTION
1389               || optionType == JOptionPane.DEFAULT_OPTION)
1390               && options.length < 1)
1391               || ((optionType == JOptionPane.YES_NO_OPTION
1392                       || optionType == JOptionPane.OK_CANCEL_OPTION)
1393                       && options.length < 2)
1394               || (optionType == JOptionPane.YES_NO_CANCEL_OPTION
1395                       && options.length < 3))
1396       {
1397         jalview.bin.Console
1398                 .debug("JvOptionPane: not enough options for dialog type");
1399       }
1400       optionsButtons = new JButton[options.length];
1401       for (int i = 0; i < options.length && i < 3; i++)
1402       {
1403         Object o = options[i];
1404         int buttonAction = buttonActions[i];
1405         Runnable action = callbacks.get(buttonAction);
1406         JButton jb;
1407         if (buttons != null && buttons.length > i && buttons[i] != null)
1408         {
1409           jb = buttons[i];
1410         }
1411         else
1412         {
1413           jb = new JButton();
1414         }
1415         jb.setText((String) o);
1416         jb.addActionListener(new ActionListener()
1417         {
1418           @Override
1419           public void actionPerformed(ActionEvent e)
1420           {
1421             joptionpane.setValue(buttonAction);
1422             if (action != null)
1423               new Thread(action).start();
1424             // joptionpane.transferFocusBackward();
1425             joptionpane.transferFocusBackward();
1426             joptionpane.setVisible(false);
1427             // put focus and raise parent window if possible, unless cancel
1428             // button pressed
1429             boolean raiseParent = (parentComponent != null);
1430             if (buttonAction == JOptionPane.CANCEL_OPTION)
1431               raiseParent = false;
1432             if (optionType == JOptionPane.YES_NO_OPTION
1433                     && buttonAction == JOptionPane.NO_OPTION)
1434               raiseParent = false;
1435             if (raiseParent)
1436             {
1437               parentComponent.requestFocus();
1438               if (parentComponent instanceof JInternalFrame)
1439               {
1440                 JInternalFrame jif = (JInternalFrame) parentComponent;
1441                 jif.show();
1442                 jif.moveToFront();
1443                 jif.grabFocus();
1444               }
1445               else if (parentComponent instanceof Window)
1446               {
1447                 Window w = (Window) parentComponent;
1448                 w.toFront();
1449                 w.requestFocus();
1450               }
1451             }
1452             joptionpane.setVisible(false);
1453           }
1454         });
1455         optionsButtons[i] = jb;
1456         if (o.equals(initialValue))
1457           initialValueButton = jb;
1458       }
1459     }
1460     joptionpane.setMessage(message);
1461     joptionpane.setMessageType(messageType);
1462     joptionpane.setOptionType(optionType);
1463     joptionpane.setIcon(icon);
1464     joptionpane.setOptions(Platform.isJS() ? options : optionsButtons);
1465     joptionpane.setInitialValue(
1466             Platform.isJS() ? initialValue : initialValueButton);
1467
1468     JDialog dialog = joptionpane.createDialog(parentComponent, title);
1469     dialog.setIconImages(ChannelProperties.getIconList());
1470     dialog.setModalityType(
1471             modal ? ModalityType.APPLICATION_MODAL : ModalityType.MODELESS);
1472     dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
1473     setDialog(dialog);
1474     return dialog;
1475   }
1476
1477   /**
1478    * Utility to programmatically click a button on a JOptionPane (as a JFrame)
1479    * 
1480    * returns true if button was found
1481    */
1482   public static boolean clickButton(JFrame frame, int buttonType)
1483   {
1484
1485     return false;
1486   }
1487
1488   /**
1489    * This helper method makes the JInternalFrame wait until it is notified by an
1490    * InternalFrameClosing event. This method also adds the given JOptionPane to
1491    * the JInternalFrame and sizes it according to the JInternalFrame's preferred
1492    * size.
1493    *
1494    * @param f
1495    *          The JInternalFrame to make modal.
1496    */
1497   private static void startModal(JInternalFrame f)
1498   {
1499     // We need to add an additional glasspane-like component directly
1500     // below the frame, which intercepts all mouse events that are not
1501     // directed at the frame itself.
1502     JPanel modalInterceptor = new JPanel();
1503     modalInterceptor.setOpaque(false);
1504     JLayeredPane lp = JLayeredPane.getLayeredPaneAbove(f);
1505     lp.setLayer(modalInterceptor, JLayeredPane.MODAL_LAYER.intValue());
1506     modalInterceptor.setBounds(0, 0, lp.getWidth(), lp.getHeight());
1507     modalInterceptor.addMouseListener(new MouseAdapter()
1508     {
1509     });
1510     modalInterceptor.addMouseMotionListener(new MouseMotionAdapter()
1511     {
1512     });
1513     lp.add(modalInterceptor);
1514     f.toFront();
1515
1516     // We need to explicitly dispatch events when we are blocking the event
1517     // dispatch thread.
1518     EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
1519     try
1520     {
1521       boolean stillModal = true;
1522       while (!f.isClosed() && stillModal)
1523       {
1524         if (EventQueue.isDispatchThread())
1525         {
1526           // The getNextEventMethod() issues wait() when no
1527           // event is available, so we don't need do explicitly wait().
1528           AWTEvent ev = queue.getNextEvent();
1529           // This mimics EventQueue.dispatchEvent(). We can't use
1530           // EventQueue.dispatchEvent() directly, because it is
1531           // protected, unfortunately.
1532           System.out.println(
1533                   "##### ev source=" + ev.getSource().getClass() + "");
1534           if (ev instanceof ActiveEvent)
1535           {
1536             System.err.println("##### 1");
1537             ((ActiveEvent) ev).dispatch();
1538           }
1539           else if (ev.getSource() instanceof MenuComponent)
1540           {
1541             System.err.println("##### 2");
1542             ((MenuComponent) ev.getSource()).dispatchEvent(ev);
1543           }
1544           else if (ev.getSource() instanceof Component)
1545           {
1546             System.err.println("##### 3");
1547             if (ev.getSource().equals(Desktop.getDesktop().getRootPane()))
1548             {
1549               stillModal = false;
1550             }
1551             else
1552             {
1553               ((Component) ev.getSource()).dispatchEvent(ev);
1554             }
1555           }
1556           // Other events are ignored as per spec in
1557           // EventQueue.dispatchEvent
1558           System.err.println("##### 4");
1559         }
1560         else
1561         {
1562           // Give other threads a chance to become active.
1563           System.err.println("##### 5");
1564           Thread.yield();
1565         }
1566       }
1567     } catch (InterruptedException ex)
1568     {
1569       // If we get interrupted, then leave the modal state.
1570       System.err.println("##### 6");
1571     } finally
1572     {
1573       System.err.println("##### 7");
1574       // Clean up the modal interceptor.
1575       lp.remove(modalInterceptor);
1576
1577       f.setVisible(false);
1578
1579       try
1580       {
1581         f.setClosed(true);
1582       } catch (PropertyVetoException e)
1583       {
1584         f.doDefaultCloseAction();
1585       }
1586
1587       // Remove the internal frame from its parent, so it is no longer
1588       // lurking around and clogging memory.
1589       Container parent = f.getParent();
1590       if (parent != null)
1591       {
1592         parent.remove(f);
1593       }
1594     }
1595   }
1596
1597   public static JvOptionPane frameDialog(Object message, String title,
1598           int messageType, String[] buttonsTextS, String defaultButtonS,
1599           List<Runnable> handlers, boolean modal)
1600   {
1601     JFrame parent = new JFrame();
1602     JvOptionPane jvop = JvOptionPane.newOptionDialog();
1603     final String[] buttonsText;
1604     final String defaultButton;
1605     if (buttonsTextS == null)
1606     {
1607       String ok = MessageManager.getString("action.ok");
1608       buttonsText = new String[] { ok };
1609       defaultButton = ok;
1610     }
1611     else
1612     {
1613       buttonsText = buttonsTextS;
1614       defaultButton = defaultButtonS;
1615     }
1616     JButton[] buttons = new JButton[buttonsText.length];
1617     for (int i = 0; i < buttonsText.length; i++)
1618     {
1619       buttons[i] = new JButton();
1620       buttons[i].setText(buttonsText[i]);
1621       Console.debug("DISABLING BUTTON " + buttons[i].getText());
1622       buttons[i].setEnabled(false);
1623       buttons[i].setVisible(false);
1624     }
1625
1626     int dialogType = -1;
1627     if (buttonsText.length == 1)
1628     {
1629       dialogType = JOptionPane.OK_OPTION;
1630     }
1631     else if (buttonsText.length == 2)
1632     {
1633       dialogType = JOptionPane.YES_NO_OPTION;
1634     }
1635     else
1636     {
1637       dialogType = JOptionPane.YES_NO_CANCEL_OPTION;
1638     }
1639     jvop.setResponseHandler(JOptionPane.YES_OPTION,
1640             (handlers != null && handlers.size() > 0) ? handlers.get(0)
1641                     : NULLCALLABLE);
1642     if (dialogType == JOptionPane.YES_NO_OPTION
1643             || dialogType == JOptionPane.YES_NO_CANCEL_OPTION)
1644     {
1645       jvop.setResponseHandler(JOptionPane.NO_OPTION,
1646               (handlers != null && handlers.size() > 1) ? handlers.get(1)
1647                       : NULLCALLABLE);
1648     }
1649     if (dialogType == JOptionPane.YES_NO_CANCEL_OPTION)
1650     {
1651       jvop.setResponseHandler(JOptionPane.CANCEL_OPTION,
1652               (handlers != null && handlers.size() > 2) ? handlers.get(2)
1653                       : NULLCALLABLE);
1654     }
1655
1656     final int dt = dialogType;
1657     new Thread(() -> {
1658       jvop.showDialog(message, title, dt, messageType, null, buttonsText,
1659               defaultButton, modal, buttons);
1660     }).start();
1661
1662     return jvop;
1663   }
1664 }