JAL-3416 Remove all JFrame/JInternalFrame icons (set to null)
[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
22 package jalview.gui;
23
24 import java.awt.AWTEvent;
25 import java.awt.ActiveEvent;
26 import java.awt.Component;
27 import java.awt.Container;
28 import java.awt.Dialog.ModalityType;
29 import java.awt.EventQueue;
30 import java.awt.HeadlessException;
31 import java.awt.MenuComponent;
32 import java.awt.Toolkit;
33 import java.awt.Window;
34 import java.awt.event.ActionEvent;
35 import java.awt.event.ActionListener;
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.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.HashMap;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.concurrent.Executors;
46
47 import javax.swing.Icon;
48 import javax.swing.JButton;
49 import javax.swing.JDialog;
50 import javax.swing.JInternalFrame;
51 import javax.swing.JLayeredPane;
52 import javax.swing.JOptionPane;
53 import javax.swing.JPanel;
54 import javax.swing.UIManager;
55 import javax.swing.event.InternalFrameEvent;
56 import javax.swing.event.InternalFrameListener;
57
58 import jalview.util.Platform;
59 import jalview.util.dialogrunner.DialogRunnerI;
60
61 public class JvOptionPane extends JOptionPane
62         implements DialogRunnerI, PropertyChangeListener
63 {
64   private static final long serialVersionUID = -3019167117756785229L;
65
66   private static Object mockResponse = JvOptionPane.CANCEL_OPTION;
67
68   private static boolean interactiveMode = true;
69
70   private Component parentComponent;
71
72   private Map<Object, Runnable> callbacks = new HashMap<>();
73
74   /*
75    * JalviewJS reports user choice in the dialog as the selected
76    * option (text); this list allows conversion to index (int)
77    */
78   List<Object> ourOptions;
79
80   public JvOptionPane(final Component parent)
81   {
82     this.parentComponent = Platform.isJS() ? this : parent;
83     this.setIcon(null);
84   }
85
86   public static int showConfirmDialog(Component parentComponent,
87           Object message) throws HeadlessException
88   {
89     // only called by test
90     return isInteractiveMode()
91             ? JOptionPane.showConfirmDialog(parentComponent, message)
92             : (int) getMockResponse();
93   }
94
95   /**
96    * Message, title, optionType
97    * 
98    * @param parentComponent
99    * @param message
100    * @param title
101    * @param optionType
102    * @return
103    * @throws HeadlessException
104    */
105   public static int showConfirmDialog(Component parentComponent,
106           Object message, String title, int optionType)
107           throws HeadlessException
108   {
109     if (!isInteractiveMode())
110     {
111       return (int) getMockResponse();
112     }
113     switch (optionType)
114     {
115     case JvOptionPane.YES_NO_CANCEL_OPTION:
116       // FeatureRenderer amendFeatures ?? TODO ??
117       // Chimera close
118       // PromptUserConfig
119       // $FALL-THROUGH$
120     default:
121     case JvOptionPane.YES_NO_OPTION:
122       // PromptUserConfig usage stats
123       // for now treated as "OK CANCEL"
124       // $FALL-THROUGH$
125     case JvOptionPane.OK_CANCEL_OPTION:
126       // will fall back to simple HTML
127       return JOptionPane.showConfirmDialog(parentComponent, message, title,
128               optionType);
129     }
130   }
131
132   /**
133    * Adds a message type. Fallback is to just add it in the beginning.
134    * 
135    * @param parentComponent
136    * @param message
137    * @param title
138    * @param optionType
139    * @param messageType
140    * @return
141    * @throws HeadlessException
142    */
143   public static int showConfirmDialog(Component parentComponent,
144           Object message, String title, int optionType, int messageType)
145           throws HeadlessException
146   {
147     // JalviewServicesChanged
148     // PromptUserConfig raiseDialog
149     return isInteractiveMode()
150             ? JOptionPane.showConfirmDialog(parentComponent, message, title,
151                     optionType, messageType)
152             : (int) getMockResponse();
153   }
154
155   /**
156    * Adds an icon
157    * 
158    * @param parentComponent
159    * @param message
160    * @param title
161    * @param optionType
162    * @param messageType
163    * @param icon
164    * @return
165    * @throws HeadlessException
166    */
167   public static int showConfirmDialog(Component parentComponent,
168           Object message, String title, int optionType, int messageType,
169           Icon icon) throws HeadlessException
170   {
171     // JvOptionPaneTest only
172     return isInteractiveMode()
173             ? JOptionPane.showConfirmDialog(parentComponent, message, title,
174                     optionType, messageType, icon)
175             : (int) getMockResponse();
176   }
177
178   /**
179    * Internal version "OK"
180    * 
181    * @param parentComponent
182    * @param message
183    * @return
184    */
185   public static int showInternalConfirmDialog(Component parentComponent,
186           Object message)
187   {
188     // JvOptionPaneTest only;
189     return isInteractiveMode()
190             ? JOptionPane.showInternalConfirmDialog(parentComponent,
191                     message)
192             : (int) getMockResponse();
193   }
194
195   /**
196    * Internal version -- changed to standard version for now
197    * 
198    * @param parentComponent
199    * @param message
200    * @param title
201    * @param optionType
202    * @return
203    */
204   public static int showInternalConfirmDialog(Component parentComponent,
205           String message, String title, int optionType)
206   {
207     if (!isInteractiveMode())
208     {
209       return (int) getMockResponse();
210     }
211     switch (optionType)
212     {
213     case JvOptionPane.YES_NO_CANCEL_OPTION:
214       // ColourMenuHelper.addMenuItmers.offerRemoval TODO
215     case JvOptionPane.YES_NO_OPTION:
216       // UserDefinedColoursSave -- relevant? TODO
217       // $FALL-THROUGH$
218     default:
219     case JvOptionPane.OK_CANCEL_OPTION:
220
221       // EditNameDialog --- uses panel for messsage TODO
222
223       // Desktop.inputURLMenuItem
224       // WsPreferenses
225       return JOptionPane.showConfirmDialog(parentComponent, message, title,
226               optionType);
227     }
228   }
229
230   /**
231    * 
232    * @param parentComponent
233    * @param message
234    * @param title
235    * @param optionType
236    * @param messageType
237    * @return
238    */
239   public static int showInternalConfirmDialog(Component parentComponent,
240           Object message, String title, int optionType, int messageType)
241   {
242     if (!isInteractiveMode())
243     {
244       return (int) getMockResponse();
245     }
246     switch (optionType)
247     {
248     case JvOptionPane.YES_NO_CANCEL_OPTION:
249     case JvOptionPane.YES_NO_OPTION:
250       // UserQuestionanaireCheck
251       // VamsasApplication
252       // $FALL-THROUGH$
253     default:
254     case JvOptionPane.OK_CANCEL_OPTION:
255       // will fall back to simple HTML
256       return JOptionPane.showConfirmDialog(parentComponent, message, title,
257               optionType, messageType);
258     }
259   }
260
261   /**
262    * adds icon; no longer internal
263    * 
264    * @param parentComponent
265    * @param message
266    * @param title
267    * @param optionType
268    * @param messageType
269    * @param icon
270    * @return
271    */
272   public static int showInternalConfirmDialog(Component parentComponent,
273           Object message, String title, int optionType, int messageType,
274           Icon icon)
275   {
276     if (!isInteractiveMode())
277     {
278       return (int) getMockResponse();
279     }
280     switch (optionType)
281     {
282     case JvOptionPane.YES_NO_CANCEL_OPTION:
283     case JvOptionPane.YES_NO_OPTION:
284       //$FALL-THROUGH$
285     default:
286     case JvOptionPane.OK_CANCEL_OPTION:
287       // Preferences editLink/newLink
288       return JOptionPane.showConfirmDialog(parentComponent, message, title,
289               optionType, messageType, icon);
290     }
291
292   }
293
294   /**
295    * custom options full-featured
296    * 
297    * @param parentComponent
298    * @param message
299    * @param title
300    * @param optionType
301    * @param messageType
302    * @param icon
303    * @param options
304    * @param initialValue
305    * @return
306    * @throws HeadlessException
307    */
308   public static int showOptionDialog(Component parentComponent,
309           String message, String title, int optionType, int messageType,
310           Icon icon, Object[] options, Object initialValue)
311           throws HeadlessException
312   {
313     if (!isInteractiveMode())
314     {
315       return (int) getMockResponse();
316     }
317     // two uses:
318     //
319     // TODO
320     //
321     // 1) AlignViewport for openLinkedAlignment
322     //
323     // Show a dialog with the option to open and link (cDNA <-> protein) as a
324     // new
325     // alignment, either as a standalone alignment or in a split frame. Returns
326     // true if the new alignment was opened, false if not, because the user
327     // declined the offer.
328     //
329     // 2) UserDefinedColors warning about saving over a name already defined
330     //
331     return JOptionPane.showOptionDialog(parentComponent, message, title,
332             optionType, messageType, icon, options, initialValue);
333   }
334
335   /**
336    * Just an OK message
337    * 
338    * @param message
339    * @throws HeadlessException
340    */
341   public static void showMessageDialog(Component parentComponent,
342           String message) throws HeadlessException
343   {
344     if (!isInteractiveMode())
345     {
346       outputMessage(message);
347       return;
348     }
349
350     // test class only
351
352     JOptionPane.showMessageDialog(parentComponent, message);
353   }
354
355   /**
356    * OK with message, title, and type
357    * 
358    * @param parentComponent
359    * @param message
360    * @param title
361    * @param messageType
362    * @throws HeadlessException
363    */
364   public static void showMessageDialog(Component parentComponent,
365           String message, String title, int messageType)
366           throws HeadlessException
367   {
368     // 30 implementations -- all just fine.
369
370     if (!isInteractiveMode())
371     {
372       outputMessage(message);
373       return;
374     }
375
376     JOptionPane.showMessageDialog(parentComponent,
377             getPrefix(messageType) + message, title, messageType);
378   }
379
380   /**
381    * adds title and icon
382    * 
383    * @param parentComponent
384    * @param message
385    * @param title
386    * @param messageType
387    * @param icon
388    * @throws HeadlessException
389    */
390   public static void showMessageDialog(Component parentComponent,
391           String message, String title, int messageType, Icon icon)
392           throws HeadlessException
393   {
394
395     // test only
396
397     if (!isInteractiveMode())
398     {
399       outputMessage(message);
400       return;
401     }
402
403     JOptionPane.showMessageDialog(parentComponent, message, title,
404             messageType, icon);
405   }
406
407   /**
408    * was internal
409    * 
410    */
411   public static void showInternalMessageDialog(Component parentComponent,
412           Object message)
413   {
414
415     // WsPreferences only
416
417     if (!isInteractiveMode())
418     {
419       outputMessage(message);
420       return;
421     }
422
423     JOptionPane.showMessageDialog(parentComponent, message);
424   }
425
426   /**
427    * Adds title and messageType
428    * 
429    * @param parentComponent
430    * @param message
431    * @param title
432    * @param messageType
433    */
434   public static void showInternalMessageDialog(Component parentComponent,
435           String message, String title, int messageType)
436   {
437
438     // 41 references
439
440     if (!isInteractiveMode())
441     {
442       outputMessage(message);
443       return;
444     }
445
446     JOptionPane.showMessageDialog(parentComponent,
447             getPrefix(messageType) + message, title, messageType);
448   }
449
450   /**
451    * 
452    * @param parentComponent
453    * @param message
454    * @param title
455    * @param messageType
456    * @param icon
457    */
458   public static void showInternalMessageDialog(Component parentComponent,
459           Object message, String title, int messageType, Icon icon)
460   {
461
462     // test only
463
464     if (!isInteractiveMode())
465     {
466       outputMessage(message);
467       return;
468     }
469
470     JOptionPane.showMessageDialog(parentComponent, message, title,
471             messageType, icon);
472   }
473
474   /**
475    * 
476    * @param message
477    * @return
478    * @throws HeadlessException
479    */
480   public static String showInputDialog(Object message)
481           throws HeadlessException
482   {
483     // test only
484
485     if (!isInteractiveMode())
486     {
487       return getMockResponse().toString();
488     }
489
490     return JOptionPane.showInputDialog(message);
491   }
492
493   /**
494    * adds inital selection value
495    * 
496    * @param message
497    * @param initialSelectionValue
498    * @return
499    */
500   public static String showInputDialog(String message,
501           String initialSelectionValue)
502   {
503     if (!isInteractiveMode())
504     {
505       return getMockResponse().toString();
506     }
507
508     // AnnotationPanel character option
509
510     return JOptionPane.showInputDialog(message, initialSelectionValue);
511   }
512
513   /**
514    * adds inital selection value
515    * 
516    * @param message
517    * @param initialSelectionValue
518    * @return
519    */
520   public static String showInputDialog(Object message,
521           Object initialSelectionValue)
522   {
523     if (!isInteractiveMode())
524     {
525       return getMockResponse().toString();
526     }
527
528     // AnnotationPanel character option
529
530     return JOptionPane.showInputDialog(message, initialSelectionValue);
531   }
532
533   /**
534    * centered on parent
535    * 
536    * @param parentComponent
537    * @param message
538    * @return
539    * @throws HeadlessException
540    */
541   public static String showInputDialog(Component parentComponent,
542           String message) throws HeadlessException
543   {
544     // test only
545
546     return isInteractiveMode()
547             ? JOptionPane.showInputDialog(parentComponent, message)
548             : getMockResponse().toString();
549   }
550
551   /**
552    * input with initial selection
553    * 
554    * @param parentComponent
555    * @param message
556    * @param initialSelectionValue
557    * @return
558    */
559   public static String showInputDialog(Component parentComponent,
560           String message, String initialSelectionValue)
561   {
562
563     // AnnotationPanel
564
565     return isInteractiveMode()
566             ? JOptionPane.showInputDialog(parentComponent, message,
567                     initialSelectionValue)
568             : getMockResponse().toString();
569   }
570
571   /**
572    * input with initial selection
573    * 
574    * @param parentComponent
575    * @param message
576    * @param initialSelectionValue
577    * @return
578    */
579   public static String showInputDialog(Component parentComponent,
580           Object message, Object initialSelectionValue)
581   {
582
583     // AnnotationPanel
584
585     return isInteractiveMode()
586             ? JOptionPane.showInputDialog(parentComponent, message,
587                     initialSelectionValue)
588             : getMockResponse().toString();
589   }
590
591   /**
592    * 
593    * @param parentComponent
594    * @param message
595    * @param title
596    * @param messageType
597    * @return
598    * @throws HeadlessException
599    */
600   public static String showInputDialog(Component parentComponent,
601           String message, String title, int messageType)
602           throws HeadlessException
603   {
604
605     // test only
606
607     return isInteractiveMode()
608             ? JOptionPane.showInputDialog(parentComponent, message, title,
609                     messageType)
610             : getMockResponse().toString();
611   }
612
613   /**
614    * Customized input option
615    * 
616    * @param parentComponent
617    * @param message
618    * @param title
619    * @param messageType
620    * @param icon
621    * @param selectionValues
622    * @param initialSelectionValue
623    * @return
624    * @throws HeadlessException
625    */
626   public static Object showInputDialog(Component parentComponent,
627           Object message, String title, int messageType, Icon icon,
628           Object[] selectionValues, Object initialSelectionValue)
629           throws HeadlessException
630   {
631
632     // test only
633
634     return isInteractiveMode()
635             ? JOptionPane.showInputDialog(parentComponent, message, title,
636                     messageType, icon, selectionValues,
637                     initialSelectionValue)
638             : getMockResponse().toString();
639   }
640
641   /**
642    * internal version
643    * 
644    * @param parentComponent
645    * @param message
646    * @return
647    */
648   public static String showInternalInputDialog(Component parentComponent,
649           String message)
650   {
651     // test only
652
653     return isInteractiveMode()
654             ? JOptionPane.showInternalInputDialog(parentComponent, message)
655             : getMockResponse().toString();
656   }
657
658   /**
659    * internal with title and messageType
660    * 
661    * @param parentComponent
662    * @param message
663    * @param title
664    * @param messageType
665    * @return
666    */
667   public static String showInternalInputDialog(Component parentComponent,
668           String message, String title, int messageType)
669   {
670
671     // AlignFrame tabbedPane_mousePressed
672
673     return isInteractiveMode()
674             ? JOptionPane.showInternalInputDialog(parentComponent,
675                     getPrefix(messageType) + message, title, messageType)
676             : getMockResponse().toString();
677   }
678
679   /**
680    * customized internal
681    * 
682    * @param parentComponent
683    * @param message
684    * @param title
685    * @param messageType
686    * @param icon
687    * @param selectionValues
688    * @param initialSelectionValue
689    * @return
690    */
691   public static Object showInternalInputDialog(Component parentComponent,
692           String message, String title, int messageType, Icon icon,
693           Object[] selectionValues, Object initialSelectionValue)
694   {
695     // test only
696
697     return isInteractiveMode()
698             ? JOptionPane.showInternalInputDialog(parentComponent, message,
699                     title, messageType, icon, selectionValues,
700                     initialSelectionValue)
701             : getMockResponse().toString();
702   }
703
704   ///////////// end of options ///////////////
705
706   private static void outputMessage(Object message)
707   {
708     System.out.println(">>> JOption Message : " + message.toString());
709   }
710
711   public static Object getMockResponse()
712   {
713     return mockResponse;
714   }
715
716   public static void setMockResponse(Object mockOption)
717   {
718     JvOptionPane.mockResponse = mockOption;
719   }
720
721   public static void resetMock()
722   {
723     setMockResponse(JvOptionPane.CANCEL_OPTION);
724     setInteractiveMode(true);
725   }
726
727   public static boolean isInteractiveMode()
728   {
729     return interactiveMode;
730   }
731
732   public static void setInteractiveMode(boolean interactive)
733   {
734     JvOptionPane.interactiveMode = interactive;
735   }
736
737   private static String getPrefix(int messageType)
738   {
739     String prefix = "";
740
741     // JavaScript only
742     if (Platform.isJS())
743     {
744       switch (messageType)
745       {
746       case JvOptionPane.WARNING_MESSAGE:
747         prefix = "WARNING! ";
748         break;
749       case JvOptionPane.ERROR_MESSAGE:
750         prefix = "ERROR! ";
751         break;
752       default:
753         prefix = "Note: ";
754       }
755     }
756     return prefix;
757   }
758
759   /**
760    * create a new option dialog that can be used to register responses - along
761    * lines of showOptionDialog
762    * 
763    * @param desktop
764    * @param question
765    * @param string
766    * @param defaultOption
767    * @param plainMessage
768    * @param object
769    * @param options
770    * @param string2
771    * @return
772    */
773   public static JvOptionPane newOptionDialog(Component parentComponent)
774   {
775     return new JvOptionPane(parentComponent);
776   }
777
778   public void showDialog(String message, String title, int optionType,
779           int messageType, Icon icon, Object[] options, Object initialValue)
780   {
781     showDialog(message, title, optionType, messageType, icon, options,
782             initialValue, true);
783   }
784
785   public void showDialog(String message, String title, int optionType,
786           int messageType, Icon icon, Object[] options, Object initialValue,
787           boolean modal)
788   {
789     if (!isInteractiveMode())
790     {
791       handleResponse(getMockResponse());
792     }
793     // two uses:
794     //
795     // TODO
796     //
797     // 1) AlignViewport for openLinkedAlignment
798     //
799     // Show a dialog with the option to open and link (cDNA <-> protein) as a
800     // new
801     // alignment, either as a standalone alignment or in a split frame. Returns
802     // true if the new alignment was opened, false if not, because the user
803     // declined the offer.
804     //
805     // 2) UserDefinedColors warning about saving over a name already defined
806     //
807
808     ourOptions = Arrays.asList(options);
809
810     if (modal)
811     {
812       // use a JOptionPane as usual
813       int response = JOptionPane.showOptionDialog(parentComponent, message,
814               title, optionType, messageType, icon, options, initialValue);
815
816       /*
817        * In Java, the response is returned to this thread and handled here;
818        * (for Javascript, see propertyChange)
819        */
820       if (!Platform.isJS())
821       /**
822        * Java only
823        * 
824        * @j2sIgnore
825        */
826       {
827         handleResponse(response);
828       }
829     }
830     else
831     {
832       /*
833        * This is java similar to the swingjs handling, with the callbacks
834        * attached to the button press of the dialog.  This means we can use
835        * a non-modal JDialog for the confirmation without blocking the GUI.
836        */
837       JOptionPane joptionpane = new JOptionPane();
838       // Make button options
839       int[] buttonActions = { JvOptionPane.YES_OPTION,
840           JvOptionPane.NO_OPTION, JvOptionPane.CANCEL_OPTION };
841
842       // we need the strings to make the buttons with actionEventListener
843       if (options == null)
844       {
845         ArrayList<String> options_default = new ArrayList<>();
846         options_default
847                 .add(UIManager.getString("OptionPane.yesButtonText"));
848         if (optionType == JvOptionPane.YES_NO_OPTION
849                 || optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
850         {
851           options_default
852                   .add(UIManager.getString("OptionPane.noButtonText"));
853         }
854         if (optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
855         {
856           options_default
857                   .add(UIManager.getString("OptionPane.cancelButtonText"));
858         }
859         options = options_default.toArray();
860       }
861
862       ArrayList<JButton> options_btns = new ArrayList<>();
863       Object initialValue_btn = null;
864       if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
865                             // add them here
866       {
867         for (int i = 0; i < options.length && i < 3; i++)
868         {
869           Object o = options[i];
870           int buttonAction = buttonActions[i];
871           Runnable action = callbacks.get(buttonAction);
872           JButton jb = new JButton();
873           jb.setText((String) o);
874           jb.addActionListener(new ActionListener()
875           {
876             @Override
877             public void actionPerformed(ActionEvent e)
878             {
879               joptionpane.setValue(buttonAction);
880               if (action != null)
881                 Executors.defaultThreadFactory().newThread(action).start();
882               // joptionpane.transferFocusBackward();
883               joptionpane.transferFocusBackward();
884               joptionpane.setVisible(false);
885               // put focus and raise parent window if possible, unless cancel
886               // button pressed
887               boolean raiseParent = (parentComponent != null);
888               if (buttonAction == JvOptionPane.CANCEL_OPTION)
889                 raiseParent = false;
890               if (optionType == JvOptionPane.YES_NO_OPTION
891                       && buttonAction == JvOptionPane.NO_OPTION)
892                 raiseParent = false;
893               if (raiseParent)
894               {
895                 parentComponent.requestFocus();
896                 if (parentComponent instanceof JInternalFrame)
897                 {
898                   JInternalFrame jif = (JInternalFrame) parentComponent;
899                   jif.show();
900                   jif.moveToFront();
901                   jif.grabFocus();
902                 }
903                 else if (parentComponent instanceof Window)
904                 {
905                   Window w = (Window) parentComponent;
906                   w.toFront();
907                   w.requestFocus();
908                 }
909               }
910               joptionpane.setVisible(false);
911             }
912           });
913           options_btns.add(jb);
914           if (o.equals(initialValue))
915             initialValue_btn = jb;
916         }
917       }
918       joptionpane.setMessage(message);
919       joptionpane.setMessageType(messageType);
920       joptionpane.setOptionType(optionType);
921       joptionpane.setIcon(icon);
922       joptionpane.setOptions(
923               Platform.isJS() ? options : options_btns.toArray());
924       joptionpane.setInitialValue(
925               Platform.isJS() ? initialValue : initialValue_btn);
926
927       JDialog dialog = joptionpane.createDialog(parentComponent, title);
928       dialog.setIconImage(null);
929       dialog.setModalityType(modal ? ModalityType.APPLICATION_MODAL
930               : ModalityType.MODELESS);
931       dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
932       dialog.setVisible(true);
933     }
934   }
935
936   public void showInternalDialog(JPanel mainPanel, String title,
937           int yesNoCancelOption, int questionMessage, Icon icon,
938           Object[] options, String initresponse)
939   {
940     if (!isInteractiveMode())
941     {
942       handleResponse(getMockResponse());
943     }
944
945     // need to set these separately so we can set the title bar icon later
946     this.setOptionType(yesNoCancelOption);
947     this.setMessageType(questionMessage);
948     this.setIcon(icon);
949     this.setInitialValue(initresponse);
950     this.setOptions(options);
951     this.setMessage(mainPanel);
952
953     ourOptions = Arrays.asList(options);
954     if (parentComponent != this)
955     {
956       JInternalFrame jif = this.createInternalFrame(parentComponent, title);
957       jif.setFrameIcon(null);
958       jif.addInternalFrameListener(new InternalFrameListener()
959       {
960         @Override
961         public void internalFrameActivated(InternalFrameEvent arg0)
962         {
963         }
964
965         @Override
966         public void internalFrameClosed(InternalFrameEvent arg0)
967         {
968           JvOptionPane.this.internalDialogHandleResponse();
969         }
970
971         @Override
972         public void internalFrameClosing(InternalFrameEvent arg0)
973         {
974         }
975
976         @Override
977         public void internalFrameDeactivated(InternalFrameEvent arg0)
978         {
979         }
980
981         @Override
982         public void internalFrameDeiconified(InternalFrameEvent arg0)
983         {
984         }
985
986         @Override
987         public void internalFrameIconified(InternalFrameEvent arg0)
988         {
989         }
990
991         @Override
992         public void internalFrameOpened(InternalFrameEvent arg0)
993         {
994         }
995       });
996       jif.setVisible(true);
997       startModal(jif);
998       return;
999     }
1000     else
1001     {
1002       JDialog dialog = this.createDialog(parentComponent, title);
1003       dialog.setIconImage(null);
1004       dialog.setVisible(true); // blocking
1005       this.internalDialogHandleResponse();
1006       return;
1007     }
1008   }
1009
1010   private void internalDialogHandleResponse()
1011   {
1012     String responseString = (String) this.getValue();
1013     int response = ourOptions.indexOf(responseString);
1014
1015     if (!Platform.isJS())
1016     /**
1017      * Java only
1018      * 
1019      * @j2sIgnore
1020      */
1021     {
1022       handleResponse(response);
1023     }
1024   }
1025
1026   @Override
1027   public JvOptionPane setResponseHandler(Object response, Runnable action)
1028   {
1029     callbacks.put(response, action);
1030     return this;
1031   }
1032
1033   /**
1034    * JalviewJS signals option selection by a property change event for the
1035    * option e.g. "OK". This methods responds to that by running the response
1036    * action that corresponds to that option.
1037    * 
1038    * @param evt
1039    */
1040   @Override
1041   public void propertyChange(PropertyChangeEvent evt)
1042   {
1043     Object newValue = evt.getNewValue();
1044     int ourOption = ourOptions.indexOf(newValue);
1045     if (ourOption >= 0)
1046     {
1047       handleResponse(ourOption);
1048     }
1049     else
1050     {
1051       // try our luck..
1052       handleResponse(newValue);
1053     }
1054   }
1055
1056   @Override
1057   public void handleResponse(Object response)
1058   {
1059     /*
1060     * this test is for NaN in Chrome
1061     */
1062     if (response != null && !response.equals(response))
1063     {
1064       return;
1065     }
1066     Runnable action = callbacks.get(response);
1067     if (action != null)
1068     {
1069       action.run();
1070       parentComponent.requestFocus();
1071     }
1072   }
1073
1074   /**
1075    * This helper method makes the JInternalFrame wait until it is notified by an
1076    * InternalFrameClosing event. This method also adds the given JOptionPane to
1077    * the JInternalFrame and sizes it according to the JInternalFrame's preferred
1078    * size.
1079    *
1080    * @param f
1081    *          The JInternalFrame to make modal.
1082    */
1083   private static void startModal(JInternalFrame f)
1084   {
1085     // We need to add an additional glasspane-like component directly
1086     // below the frame, which intercepts all mouse events that are not
1087     // directed at the frame itself.
1088     JPanel modalInterceptor = new JPanel();
1089     modalInterceptor.setOpaque(false);
1090     JLayeredPane lp = JLayeredPane.getLayeredPaneAbove(f);
1091     lp.setLayer(modalInterceptor, JLayeredPane.MODAL_LAYER.intValue());
1092     modalInterceptor.setBounds(0, 0, lp.getWidth(), lp.getHeight());
1093     modalInterceptor.addMouseListener(new MouseAdapter()
1094     {
1095     });
1096     modalInterceptor.addMouseMotionListener(new MouseMotionAdapter()
1097     {
1098     });
1099     lp.add(modalInterceptor);
1100     f.toFront();
1101
1102     // We need to explicitly dispatch events when we are blocking the event
1103     // dispatch thread.
1104     EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
1105     try
1106     {
1107       while (!f.isClosed())
1108       {
1109         if (EventQueue.isDispatchThread())
1110         {
1111           // The getNextEventMethod() issues wait() when no
1112           // event is available, so we don't need do explicitly wait().
1113           AWTEvent ev = queue.getNextEvent();
1114           // This mimics EventQueue.dispatchEvent(). We can't use
1115           // EventQueue.dispatchEvent() directly, because it is
1116           // protected, unfortunately.
1117           if (ev instanceof ActiveEvent)
1118             ((ActiveEvent) ev).dispatch();
1119           else if (ev.getSource() instanceof Component)
1120             ((Component) ev.getSource()).dispatchEvent(ev);
1121           else if (ev.getSource() instanceof MenuComponent)
1122             ((MenuComponent) ev.getSource()).dispatchEvent(ev);
1123           // Other events are ignored as per spec in
1124           // EventQueue.dispatchEvent
1125         }
1126         else
1127         {
1128           // Give other threads a chance to become active.
1129           Thread.yield();
1130         }
1131       }
1132     } catch (InterruptedException ex)
1133     {
1134       // If we get interrupted, then leave the modal state.
1135     } finally
1136     {
1137       // Clean up the modal interceptor.
1138       lp.remove(modalInterceptor);
1139
1140       // Remove the internal frame from its parent, so it is no longer
1141       // lurking around and clogging memory.
1142       Container parent = f.getParent();
1143       if (parent != null)
1144         parent.remove(f);
1145     }
1146   }
1147 }