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