JAL-4285 Better checkFiles processing. Better dialog.
[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
726             .outPrintln(">>> JOption Message : " + message.toString());
727   }
728
729   public static Object getMockResponse()
730   {
731     return mockResponse;
732   }
733
734   public static void setMockResponse(Object mockOption)
735   {
736     JvOptionPane.mockResponse = mockOption;
737   }
738
739   public static void resetMock()
740   {
741     setMockResponse(JvOptionPane.CANCEL_OPTION);
742     setInteractiveMode(true);
743   }
744
745   public static boolean isInteractiveMode()
746   {
747     return interactiveMode;
748   }
749
750   public static void setInteractiveMode(boolean interactive)
751   {
752     JvOptionPane.interactiveMode = interactive;
753   }
754
755   private static String getPrefix(int messageType)
756   {
757     String prefix = "";
758
759     // JavaScript only
760     if (Platform.isJS())
761     {
762       switch (messageType)
763       {
764       case JvOptionPane.WARNING_MESSAGE:
765         prefix = "WARNING! ";
766         break;
767       case JvOptionPane.ERROR_MESSAGE:
768         prefix = "ERROR! ";
769         break;
770       default:
771         prefix = "Note: ";
772       }
773     }
774     return prefix;
775   }
776
777   /**
778    * create a new option dialog that can be used to register responses - along
779    * lines of showOptionDialog
780    * 
781    * @param desktop
782    * @param question
783    * @param string
784    * @param defaultOption
785    * @param plainMessage
786    * @param object
787    * @param options
788    * @param string2
789    * @return
790    */
791   public static JvOptionPane newOptionDialog()
792   {
793     return new JvOptionPane(null);
794   }
795
796   public static JvOptionPane newOptionDialog(Component parentComponent)
797   {
798     return new JvOptionPane(parentComponent);
799   }
800
801   public void showDialog(String message, String title, int optionType,
802           int messageType, Icon icon, Object[] options, Object initialValue)
803   {
804     showDialog(message, title, optionType, messageType, icon, options,
805             initialValue, true);
806   }
807
808   public void showDialog(Object message, String title, int optionType,
809           int messageType, Icon icon, Object[] options, Object initialValue,
810           boolean modal)
811   {
812     showDialog(message, title, optionType, messageType, icon, options,
813             initialValue, modal, null);
814   }
815
816   public void showDialog(Object message, String title, int optionType,
817           int messageType, Icon icon, Object[] options, Object initialValue,
818           boolean modal, JButton[] buttons)
819   {
820     if (!isInteractiveMode())
821     {
822       handleResponse(getMockResponse());
823       return;
824     }
825     // two uses:
826     //
827     // TODO
828     //
829     // 1) AlignViewport for openLinkedAlignment
830     //
831     // Show a dialog with the option to open and link (cDNA <-> protein) as a
832     // new
833     // alignment, either as a standalone alignment or in a split frame. Returns
834     // true if the new alignment was opened, false if not, because the user
835     // declined the offer.
836     //
837     // 2) UserDefinedColors warning about saving over a name already defined
838     //
839
840     ourOptions = Arrays.asList(options);
841
842     if (modal)
843     {
844       boolean useButtons = false;
845       Object initialValueButton = null;
846       NOTNULL: if (buttons != null)
847       {
848         if (buttons.length != options.length)
849         {
850           jalview.bin.Console.error(
851                   "Supplied buttons array not the same length as supplied options array.");
852           break NOTNULL;
853         }
854         int[] buttonActions = { JOptionPane.YES_OPTION,
855             JOptionPane.NO_OPTION, JOptionPane.CANCEL_OPTION };
856         for (int i = 0; i < options.length; i++)
857         {
858           Object o = options[i];
859           jalview.bin.Console.debug(
860                   "Setting button " + i + " to '" + o.toString() + "'");
861           JButton jb = buttons[i];
862
863           if (o.equals(initialValue))
864             initialValueButton = jb;
865
866           int buttonAction = buttonActions[i];
867           Runnable action = callbacks.get(buttonAction);
868           jb.setText((String) o);
869           jb.addActionListener(new ActionListener()
870           {
871             @Override
872             public void actionPerformed(ActionEvent e)
873             {
874
875               Object obj = e.getSource();
876               if (obj == null || !(obj instanceof Component))
877               {
878                 jalview.bin.Console.debug(
879                         "Could not find Component source of event object "
880                                 + obj);
881                 return;
882               }
883               Object joptionpaneObject = SwingUtilities.getAncestorOfClass(
884                       JOptionPane.class, (Component) obj);
885               if (joptionpaneObject == null
886                       || !(joptionpaneObject instanceof JOptionPane))
887               {
888                 jalview.bin.Console.debug(
889                         "Could not find JOptionPane ancestor of event object "
890                                 + obj);
891                 return;
892               }
893               JOptionPane joptionpane = (JOptionPane) joptionpaneObject;
894               joptionpane.setValue(buttonAction);
895               if (action != null)
896                 new Thread(action).start();
897               joptionpane.transferFocusBackward();
898               joptionpane.setVisible(false);
899               // put focus and raise parent window if possible, unless cancel or
900               // no button pressed
901               boolean raiseParent = (parentComponent != null);
902               if (buttonAction == JOptionPane.CANCEL_OPTION)
903                 raiseParent = false;
904               if (optionType == JOptionPane.YES_NO_OPTION
905                       && buttonAction == JOptionPane.NO_OPTION)
906                 raiseParent = false;
907               if (raiseParent)
908               {
909                 parentComponent.requestFocus();
910                 if (parentComponent instanceof JInternalFrame)
911                 {
912                   JInternalFrame jif = (JInternalFrame) parentComponent;
913                   jif.show();
914                   jif.moveToFront();
915                   jif.grabFocus();
916                 }
917                 else if (parentComponent instanceof Window)
918                 {
919                   Window w = (Window) parentComponent;
920                   w.toFront();
921                   w.requestFocus();
922                 }
923               }
924               joptionpane.setVisible(false);
925             }
926           });
927
928         }
929         useButtons = true;
930       }
931       // use a JOptionPane as usual
932       int response = JOptionPane.showOptionDialog(parentComponent, message,
933               title, optionType, messageType, icon,
934               useButtons ? buttons : options,
935               useButtons ? initialValueButton : initialValue);
936
937       /*
938        * In Java, the response is returned to this thread and handled here; (for
939        * Javascript, see propertyChange)
940        */
941       if (!Platform.isJS())
942       /**
943        * Java only
944        * 
945        * @j2sIgnore
946        */
947       {
948         handleResponse(response);
949       }
950     }
951     else
952     {
953       /*
954        * This is java similar to the swingjs handling, with the callbacks attached to
955        * the button press of the dialog. This means we can use a non-modal JDialog for
956        * the confirmation without blocking the GUI.
957        */
958       JOptionPane joptionpane = new JOptionPane();
959       // Make button options
960       int[] buttonActions = { JvOptionPane.YES_OPTION,
961           JvOptionPane.NO_OPTION, JvOptionPane.CANCEL_OPTION };
962
963       // we need the strings to make the buttons with actionEventListener
964       if (options == null)
965       {
966         ArrayList<String> options_default = new ArrayList<>();
967         options_default
968                 .add(UIManager.getString("OptionPane.yesButtonText"));
969         if (optionType == JvOptionPane.YES_NO_OPTION
970                 || optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
971         {
972           options_default
973                   .add(UIManager.getString("OptionPane.noButtonText"));
974         }
975         if (optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
976         {
977           options_default
978                   .add(UIManager.getString("OptionPane.cancelButtonText"));
979         }
980         options = options_default.toArray();
981       }
982
983       ArrayList<JButton> options_btns = new ArrayList<>();
984       Object initialValue_btn = null;
985       if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
986                             // add them here
987       {
988         for (int i = 0; i < options.length && i < 3; i++)
989         {
990           Object o = options[i];
991           int buttonAction = buttonActions[i];
992           Runnable action = callbacks.get(buttonAction);
993           JButton jb = new JButton();
994           jb.setText((String) o);
995           jb.addActionListener(new ActionListener()
996           {
997
998             @Override
999             public void actionPerformed(ActionEvent e)
1000             {
1001               joptionpane.setValue(buttonAction);
1002               if (action != null)
1003                 new Thread(action).start();
1004               // joptionpane.transferFocusBackward();
1005               joptionpane.transferFocusBackward();
1006               joptionpane.setVisible(false);
1007               // put focus and raise parent window if possible, unless cancel
1008               // button pressed
1009               boolean raiseParent = (parentComponent != null);
1010               if (buttonAction == JvOptionPane.CANCEL_OPTION)
1011                 raiseParent = false;
1012               if (optionType == JvOptionPane.YES_NO_OPTION
1013                       && buttonAction == JvOptionPane.NO_OPTION)
1014                 raiseParent = false;
1015               if (raiseParent)
1016               {
1017                 parentComponent.requestFocus();
1018                 if (parentComponent instanceof JInternalFrame)
1019                 {
1020                   JInternalFrame jif = (JInternalFrame) parentComponent;
1021                   jif.show();
1022                   jif.moveToFront();
1023                   jif.grabFocus();
1024                 }
1025                 else if (parentComponent instanceof Window)
1026                 {
1027                   Window w = (Window) parentComponent;
1028                   w.toFront();
1029                   w.requestFocus();
1030                 }
1031               }
1032               joptionpane.setVisible(false);
1033             }
1034           });
1035           options_btns.add(jb);
1036           if (o.equals(initialValue))
1037             initialValue_btn = jb;
1038         }
1039       }
1040       joptionpane.setMessage(message);
1041       joptionpane.setMessageType(messageType);
1042       joptionpane.setOptionType(optionType);
1043       joptionpane.setIcon(icon);
1044       joptionpane.setOptions(
1045               Platform.isJS() ? options : options_btns.toArray());
1046       joptionpane.setInitialValue(
1047               Platform.isJS() ? initialValue : initialValue_btn);
1048
1049       JDialog dialog = joptionpane.createDialog(parentComponent, title);
1050       dialog.setIconImages(ChannelProperties.getIconList());
1051       dialog.setModalityType(modal ? ModalityType.APPLICATION_MODAL
1052               : ModalityType.MODELESS);
1053       dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
1054       dialog.setVisible(true);
1055       setDialog(dialog);
1056     }
1057   }
1058
1059   public void showInternalDialog(Object mainPanel, String title,
1060           int yesNoCancelOption, int questionMessage, Icon icon,
1061           Object[] options, String initresponse)
1062   {
1063     if (!isInteractiveMode())
1064     {
1065       handleResponse(getMockResponse());
1066     }
1067
1068     // need to set these separately so we can set the title bar icon later
1069     this.setOptionType(yesNoCancelOption);
1070     this.setMessageType(questionMessage);
1071     this.setIcon(icon);
1072     this.setInitialValue(initresponse);
1073     this.setOptions(options);
1074     this.setMessage(mainPanel);
1075
1076     ourOptions = Arrays.asList(options);
1077     if (parentComponent != this
1078             && !(parentComponent == null && Desktop.instance == null))
1079     {
1080       // note the parent goes back to a JRootPane so is probably
1081       // Desktop.getDesktop()
1082       JInternalFrame jif = this.createInternalFrame(
1083               parentComponent != null ? parentComponent : Desktop.instance,
1084               title);
1085       // connect to the alignFrame using a map in Desktop
1086       if (parentComponent instanceof AlignFrame)
1087       {
1088         Desktop.addModal((AlignFrame) parentComponent, jif);
1089       }
1090       jif.setFrameIcon(null);
1091       jif.addInternalFrameListener(new InternalFrameListener()
1092       {
1093         @Override
1094         public void internalFrameActivated(InternalFrameEvent arg0)
1095         {
1096         }
1097
1098         @Override
1099         public void internalFrameClosed(InternalFrameEvent arg0)
1100         {
1101           JvOptionPane.this.internalDialogHandleResponse();
1102         }
1103
1104         @Override
1105         public void internalFrameClosing(InternalFrameEvent arg0)
1106         {
1107         }
1108
1109         @Override
1110         public void internalFrameDeactivated(InternalFrameEvent arg0)
1111         {
1112         }
1113
1114         @Override
1115         public void internalFrameDeiconified(InternalFrameEvent arg0)
1116         {
1117         }
1118
1119         @Override
1120         public void internalFrameIconified(InternalFrameEvent arg0)
1121         {
1122         }
1123
1124         @Override
1125         public void internalFrameOpened(InternalFrameEvent arg0)
1126         {
1127         }
1128       });
1129       jif.setVisible(true);
1130       startModal(jif);
1131       return;
1132     }
1133     else
1134     {
1135       JDialog dialog = this.createDialog(parentComponent, title);
1136       dialog.setIconImages(ChannelProperties.getIconList());
1137       dialog.setVisible(true); // blocking
1138       this.internalDialogHandleResponse();
1139       return;
1140     }
1141   }
1142
1143   private void internalDialogHandleResponse()
1144   {
1145     Object value = this.getValue();
1146     if (value == null
1147             || (value instanceof Integer && (Integer) value == -1))
1148     {
1149       return;
1150     }
1151     String responseString = value.toString();
1152     int response = ourOptions.indexOf(responseString);
1153
1154     if (!Platform.isJS())
1155     /**
1156      * Java only
1157      * 
1158      * @j2sIgnore
1159      */
1160     {
1161       handleResponse(response);
1162     }
1163   }
1164
1165   /*
1166    * @Override public JvOptionPane setResponseHandler(Object response, Runnable
1167    * action) { callbacks.put(response, new Callable<Void>() {
1168    * 
1169    * @Override public Void call() { action.run(); return null; } }); return this;
1170    * }
1171    */
1172   @Override
1173   public JvOptionPane setResponseHandler(Object response, Runnable action)
1174   {
1175     if (action == null)
1176     {
1177       action = NULLCALLABLE;
1178     }
1179     callbacks.put(response, action);
1180     return this;
1181   }
1182
1183   public void setDialog(JDialog d)
1184   {
1185     dialog = d;
1186   }
1187
1188   public JDialog getDialog()
1189   {
1190     return dialog;
1191   }
1192
1193   /**
1194    * showDialogOnTop will create a dialog that (attempts to) come to top of OS
1195    * desktop windows
1196    */
1197   public static int showDialogOnTop(String label, String actionString,
1198           int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE)
1199   {
1200     return showDialogOnTop(null, label, actionString, JOPTIONPANE_OPTION,
1201             JOPTIONPANE_MESSAGETYPE);
1202   }
1203
1204   public static int showDialogOnTop(Component dialogParentComponent,
1205           String label, String actionString, int JOPTIONPANE_OPTION,
1206           int JOPTIONPANE_MESSAGETYPE)
1207   {
1208     if (!isInteractiveMode())
1209     {
1210       return (int) getMockResponse();
1211     }
1212     // Ensure Jalview window is brought to front (primarily for Quit
1213     // confirmation window to be visible)
1214
1215     // This method of raising the Jalview window is broken in java
1216     // jalviewDesktop.setVisible(true);
1217     // jalviewDesktop.toFront();
1218
1219     // A better hack which works is to create a new JFrame parent with
1220     // setAlwaysOnTop(true)
1221     JFrame dialogParent = new JFrame();
1222     if (dialogParentComponent == null)
1223     {
1224       dialogParent.setIconImages(ChannelProperties.getIconList());
1225       dialogParent.setAlwaysOnTop(true);
1226     }
1227
1228     int answer = JOptionPane.showConfirmDialog(
1229             dialogParentComponent == null ? dialogParent
1230                     : dialogParentComponent,
1231             label, actionString, JOPTIONPANE_OPTION,
1232             JOPTIONPANE_MESSAGETYPE);
1233
1234     if (dialogParentComponent == null)
1235     {
1236       dialogParent.setAlwaysOnTop(false);
1237       dialogParent.dispose();
1238     }
1239
1240     return answer;
1241   }
1242
1243   public void showDialogOnTopAsync(String label, String actionString,
1244           int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE, Icon icon,
1245           Object[] options, Object initialValue, boolean modal)
1246   {
1247     JFrame frame = new JFrame();
1248     frame.setIconImages(ChannelProperties.getIconList());
1249     showDialogOnTopAsync(frame, label, actionString, JOPTIONPANE_OPTION,
1250             JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal);
1251   }
1252
1253   public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1254           String actionString, int JOPTIONPANE_OPTION,
1255           int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1256           Object initialValue, boolean modal)
1257   {
1258     showDialogOnTopAsync(dialogParent, label, actionString,
1259             JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE, icon, options,
1260             initialValue, modal, null);
1261   }
1262
1263   public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1264           String actionString, int JOPTIONPANE_OPTION,
1265           int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1266           Object initialValue, boolean modal, JButton[] buttons)
1267   {
1268     showDialogOnTopAsync(dialogParent, label, actionString,
1269             JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE, icon, options,
1270             initialValue, modal, buttons, true);
1271   }
1272
1273   public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1274           String actionString, int JOPTIONPANE_OPTION,
1275           int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1276           Object initialValue, boolean modal, JButton[] buttons,
1277           boolean dispose)
1278   {
1279     if (!isInteractiveMode())
1280     {
1281       handleResponse(getMockResponse());
1282       return;
1283     }
1284     // Ensure Jalview window is brought to front (primarily for Quit
1285     // confirmation window to be visible)
1286
1287     // This method of raising the Jalview window is broken in java
1288     // jalviewDesktop.setVisible(true);
1289     // jalviewDesktop.toFront();
1290
1291     // A better hack which works is to create a new JFrame parent with
1292     // setAlwaysOnTop(true)
1293     dialogParent.setAlwaysOnTop(true);
1294     parentComponent = dialogParent;
1295
1296     showDialog(label, actionString, JOPTIONPANE_OPTION,
1297             JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal,
1298             buttons);
1299
1300     if (dispose)
1301     {
1302       dialogParent.setAlwaysOnTop(false);
1303       dialogParent.dispose();
1304     }
1305   }
1306
1307   /**
1308    * JalviewJS signals option selection by a property change event for the
1309    * option e.g. "OK". This methods responds to that by running the response
1310    * action that corresponds to that option.
1311    * 
1312    * @param evt
1313    */
1314   @Override
1315   public void propertyChange(PropertyChangeEvent evt)
1316   {
1317     Object newValue = evt.getNewValue();
1318     int ourOption = ourOptions.indexOf(newValue);
1319     if (ourOption >= 0)
1320     {
1321       handleResponse(ourOption);
1322     }
1323     else
1324     {
1325       // try our luck..
1326       handleResponse(newValue);
1327     }
1328   }
1329
1330   @Override
1331   public void handleResponse(Object response)
1332   {
1333     /*
1334      * this test is for NaN in Chrome
1335      */
1336     if (response != null && !response.equals(response))
1337     {
1338       return;
1339     }
1340     Runnable action = callbacks.get(response);
1341     if (action != null)
1342     {
1343       try
1344       {
1345         new Thread(action).start();
1346         // action.call();
1347       } catch (Exception e)
1348       {
1349         e.printStackTrace();
1350       }
1351       if (parentComponent != null)
1352         parentComponent.requestFocus();
1353     }
1354   }
1355
1356   /**
1357    * Create a non-modal confirm dialog
1358    */
1359   public JDialog createDialog(Component parentComponent, Object message,
1360           String title, int optionType, int messageType, Icon icon,
1361           Object[] options, Object initialValue, boolean modal)
1362   {
1363     return createDialog(parentComponent, message, title, optionType,
1364             messageType, icon, options, initialValue, modal, null);
1365   }
1366
1367   public JDialog createDialog(Component parentComponent, Object message,
1368           String title, int optionType, int messageType, Icon icon,
1369           Object[] options, Object initialValue, boolean modal,
1370           JButton[] buttons)
1371   {
1372     if (!isInteractiveMode())
1373     {
1374       handleResponse(getMockResponse());
1375       return null;
1376     }
1377     JButton[] optionsButtons = null;
1378     Object initialValueButton = null;
1379     JOptionPane joptionpane = new JOptionPane();
1380     // Make button options
1381     int[] buttonActions = { JOptionPane.YES_OPTION, JOptionPane.NO_OPTION,
1382         JOptionPane.CANCEL_OPTION };
1383
1384     // we need the strings to make the buttons with actionEventListener
1385     if (options == null)
1386     {
1387       ArrayList<String> options_default = new ArrayList<>();
1388       options_default.add(UIManager.getString("OptionPane.yesButtonText"));
1389       if (optionType == JOptionPane.YES_NO_OPTION
1390               || optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1391       {
1392         options_default.add(UIManager.getString("OptionPane.noButtonText"));
1393       }
1394       if (optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1395       {
1396         options_default
1397                 .add(UIManager.getString("OptionPane.cancelButtonText"));
1398       }
1399       options = options_default.toArray();
1400     }
1401     if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
1402                           // add them here
1403     {
1404       if (((optionType == JOptionPane.YES_OPTION
1405               || optionType == JOptionPane.NO_OPTION
1406               || optionType == JOptionPane.CANCEL_OPTION
1407               || optionType == JOptionPane.OK_OPTION
1408               || optionType == JOptionPane.DEFAULT_OPTION)
1409               && options.length < 1)
1410               || ((optionType == JOptionPane.YES_NO_OPTION
1411                       || optionType == JOptionPane.OK_CANCEL_OPTION)
1412                       && options.length < 2)
1413               || (optionType == JOptionPane.YES_NO_CANCEL_OPTION
1414                       && options.length < 3))
1415       {
1416         jalview.bin.Console
1417                 .debug("JvOptionPane: not enough options for dialog type");
1418       }
1419       optionsButtons = new JButton[options.length];
1420       for (int i = 0; i < options.length && i < 3; i++)
1421       {
1422         Object o = options[i];
1423         int buttonAction = buttonActions[i];
1424         Runnable action = callbacks.get(buttonAction);
1425         JButton jb;
1426         if (buttons != null && buttons.length > i && buttons[i] != null)
1427         {
1428           jb = buttons[i];
1429         }
1430         else
1431         {
1432           jb = new JButton();
1433         }
1434         jb.setText((String) o);
1435         jb.addActionListener(new ActionListener()
1436         {
1437           @Override
1438           public void actionPerformed(ActionEvent e)
1439           {
1440             joptionpane.setValue(buttonAction);
1441             if (action != null)
1442               new Thread(action).start();
1443             // joptionpane.transferFocusBackward();
1444             joptionpane.transferFocusBackward();
1445             joptionpane.setVisible(false);
1446             // put focus and raise parent window if possible, unless cancel
1447             // button pressed
1448             boolean raiseParent = (parentComponent != null);
1449             if (buttonAction == JOptionPane.CANCEL_OPTION)
1450               raiseParent = false;
1451             if (optionType == JOptionPane.YES_NO_OPTION
1452                     && buttonAction == JOptionPane.NO_OPTION)
1453               raiseParent = false;
1454             if (raiseParent)
1455             {
1456               parentComponent.requestFocus();
1457               if (parentComponent instanceof JInternalFrame)
1458               {
1459                 JInternalFrame jif = (JInternalFrame) parentComponent;
1460                 jif.show();
1461                 jif.moveToFront();
1462                 jif.grabFocus();
1463               }
1464               else if (parentComponent instanceof Window)
1465               {
1466                 Window w = (Window) parentComponent;
1467                 w.toFront();
1468                 w.requestFocus();
1469               }
1470             }
1471             joptionpane.setVisible(false);
1472           }
1473         });
1474         optionsButtons[i] = jb;
1475         if (o.equals(initialValue))
1476           initialValueButton = jb;
1477       }
1478     }
1479     joptionpane.setMessage(message);
1480     joptionpane.setMessageType(messageType);
1481     joptionpane.setOptionType(optionType);
1482     joptionpane.setIcon(icon);
1483     joptionpane.setOptions(Platform.isJS() ? options : optionsButtons);
1484     joptionpane.setInitialValue(
1485             Platform.isJS() ? initialValue : initialValueButton);
1486
1487     JDialog dialog = joptionpane.createDialog(parentComponent, title);
1488     dialog.setIconImages(ChannelProperties.getIconList());
1489     dialog.setModalityType(
1490             modal ? ModalityType.APPLICATION_MODAL : ModalityType.MODELESS);
1491     dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
1492     setDialog(dialog);
1493     return dialog;
1494   }
1495
1496   /**
1497    * Utility to programmatically click a button on a JOptionPane (as a JFrame)
1498    * 
1499    * returns true if button was found
1500    */
1501   public static boolean clickButton(JFrame frame, int buttonType)
1502   {
1503
1504     return false;
1505   }
1506
1507   /**
1508    * This helper method makes the JInternalFrame wait until it is notified by an
1509    * InternalFrameClosing event. This method also adds the given JOptionPane to
1510    * the JInternalFrame and sizes it according to the JInternalFrame's preferred
1511    * size.
1512    *
1513    * @param f
1514    *          The JInternalFrame to make modal.
1515    */
1516   private static void startModal(JInternalFrame f)
1517   {
1518     // We need to add an additional glasspane-like component directly
1519     // below the frame, which intercepts all mouse events that are not
1520     // directed at the frame itself.
1521     JPanel modalInterceptor = new JPanel();
1522     modalInterceptor.setOpaque(false);
1523     JLayeredPane lp = JLayeredPane.getLayeredPaneAbove(f);
1524     lp.setLayer(modalInterceptor, JLayeredPane.MODAL_LAYER.intValue());
1525     modalInterceptor.setBounds(0, 0, lp.getWidth(), lp.getHeight());
1526     modalInterceptor.addMouseListener(new MouseAdapter()
1527     {
1528     });
1529     modalInterceptor.addMouseMotionListener(new MouseMotionAdapter()
1530     {
1531     });
1532     lp.add(modalInterceptor);
1533     f.toFront();
1534
1535     // disable the main menu bar if in Linux
1536     JMenuBar menubar = null;
1537     if (Platform.isLinux())
1538     {
1539       JRootPane rootpane = Desktop.getDesktop().getRootPane();
1540       menubar = rootpane.getJMenuBar();
1541     }
1542
1543     // We need to explicitly dispatch events when we are blocking the event
1544     // dispatch thread.
1545     EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
1546     try
1547     {
1548       if (menubar != null)
1549       {
1550         // don't allow clicks on main menu on linux due to a hanging bug.
1551         // see JAL-4214.
1552         setMenusEnabled(menubar, false);
1553       }
1554
1555       while (!f.isClosed())
1556       {
1557         if (EventQueue.isDispatchThread())
1558         {
1559           // The getNextEventMethod() issues wait() when no
1560           // event is available, so we don't need do explicitly wait().
1561           AWTEvent ev = queue.getNextEvent();
1562           // This mimics EventQueue.dispatchEvent(). We can't use
1563           // EventQueue.dispatchEvent() directly, because it is
1564           // protected, unfortunately.
1565           if (ev instanceof ActiveEvent)
1566           {
1567             ((ActiveEvent) ev).dispatch();
1568           }
1569           else if (ev instanceof KeyEvent && ((KeyEvent) ev).isControlDown()
1570                   && menubar != null)
1571           {
1572             // temporarily enable menus to send Ctrl+? KeyEvents
1573             setMenusEnabled(menubar, true);
1574             ((Component) ev.getSource()).dispatchEvent(ev);
1575             setMenusEnabled(menubar, false);
1576           }
1577           else if (ev.getSource() instanceof MenuComponent)
1578           {
1579             ((MenuComponent) ev.getSource()).dispatchEvent(ev);
1580           }
1581           else if (ev.getSource() instanceof Component)
1582           {
1583             ((Component) ev.getSource()).dispatchEvent(ev);
1584           }
1585           // Other events are ignored as per spec in
1586           // EventQueue.dispatchEvent
1587         }
1588         else
1589         {
1590           // Give other threads a chance to become active.
1591           Thread.yield();
1592         }
1593       }
1594     } catch (InterruptedException ex)
1595     {
1596       // If we get interrupted, then leave the modal state.
1597     } finally
1598     {
1599       // re-enable the main menu bar
1600       if (menubar != null)
1601       {
1602         setMenusEnabled(menubar, true);
1603       }
1604
1605       // Clean up the modal interceptor.
1606       lp.remove(modalInterceptor);
1607
1608       // unpaint the frame
1609       f.setVisible(false);
1610
1611       // close the frame
1612       try
1613       {
1614         f.setClosed(true);
1615       } catch (PropertyVetoException e)
1616       {
1617         f.doDefaultCloseAction();
1618       }
1619
1620       // Remove the internal frame from its parent, so it is no longer
1621       // lurking around and clogging memory.
1622       Container parent = f.getParent();
1623       if (parent != null)
1624       {
1625         parent.remove(f);
1626       }
1627     }
1628   }
1629
1630   public static JvOptionPane frameDialog(Object message, String title,
1631           int messageType, String[] buttonsTextS, String defaultButtonS,
1632           List<Runnable> handlers, boolean modal)
1633   {
1634     JFrame parent = new JFrame();
1635     JvOptionPane jvop = JvOptionPane.newOptionDialog();
1636     final String[] buttonsText;
1637     final String defaultButton;
1638     if (buttonsTextS == null)
1639     {
1640       String ok = MessageManager.getString("action.ok");
1641       buttonsText = new String[] { ok };
1642       defaultButton = ok;
1643     }
1644     else
1645     {
1646       buttonsText = buttonsTextS;
1647       defaultButton = defaultButtonS;
1648     }
1649     JButton[] buttons = new JButton[buttonsText.length];
1650     for (int i = 0; i < buttonsText.length; i++)
1651     {
1652       buttons[i] = new JButton();
1653       buttons[i].setText(buttonsText[i]);
1654       Console.debug("DISABLING BUTTON " + buttons[i].getText());
1655       buttons[i].setEnabled(false);
1656       buttons[i].setVisible(false);
1657     }
1658
1659     int dialogType = -1;
1660     if (buttonsText.length == 1)
1661     {
1662       dialogType = JOptionPane.OK_OPTION;
1663     }
1664     else if (buttonsText.length == 2)
1665     {
1666       dialogType = JOptionPane.YES_NO_OPTION;
1667     }
1668     else
1669     {
1670       dialogType = JOptionPane.YES_NO_CANCEL_OPTION;
1671     }
1672     jvop.setResponseHandler(JOptionPane.YES_OPTION,
1673             (handlers != null && handlers.size() > 0) ? handlers.get(0)
1674                     : NULLCALLABLE);
1675     if (dialogType == JOptionPane.YES_NO_OPTION
1676             || dialogType == JOptionPane.YES_NO_CANCEL_OPTION)
1677     {
1678       jvop.setResponseHandler(JOptionPane.NO_OPTION,
1679               (handlers != null && handlers.size() > 1) ? handlers.get(1)
1680                       : NULLCALLABLE);
1681     }
1682     if (dialogType == JOptionPane.YES_NO_CANCEL_OPTION)
1683     {
1684       jvop.setResponseHandler(JOptionPane.CANCEL_OPTION,
1685               (handlers != null && handlers.size() > 2) ? handlers.get(2)
1686                       : NULLCALLABLE);
1687     }
1688
1689     final int dt = dialogType;
1690     new Thread(() -> {
1691       jvop.showDialog(message, title, dt, messageType, null, buttonsText,
1692               defaultButton, modal, buttons);
1693     }).start();
1694
1695     return jvop;
1696   }
1697
1698   private static void setMenusEnabled(JMenuBar menubar, boolean b)
1699   {
1700     for (int i = 0; i < menubar.getMenuCount(); i++)
1701     {
1702       JMenu menu = menubar.getMenu(i);
1703       menu.setEnabled(b);
1704     }
1705   }
1706
1707 }