JAL-3048 first pass at JvOptionDialog as property listener for dialog runner rather...
[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 jalview.util.dialogrunner.DialogRunner;
25 import jalview.util.dialogrunner.DialogRunnerI;
26 import jalview.util.dialogrunner.RunResponse;
27
28 import java.awt.Component;
29 import java.awt.HeadlessException;
30 import java.beans.PropertyChangeEvent;
31 import java.beans.PropertyChangeListener;
32 import java.util.Arrays;
33 import java.util.List;
34
35 import javax.swing.Icon;
36 import javax.swing.JOptionPane;
37
38 public class JvOptionPane extends JOptionPane
39         implements DialogRunnerI, PropertyChangeListener
40 {
41   // BH 2018 no changes needed here.
42
43   private static final long serialVersionUID = -3019167117756785229L;
44
45   private static Object mockResponse = JvOptionPane.CANCEL_OPTION;
46
47   private static boolean interactiveMode = true;
48
49   private Component parentComponent;
50
51   public JvOptionPane(final Component parentComponent)
52   {
53     
54     this.parentComponent = parentComponent;
55   }
56
57   public static int showConfirmDialog(Component parentComponent,
58           Object message) throws HeadlessException
59   {
60     // only called by test
61     return isInteractiveMode()
62             ? JOptionPane.showConfirmDialog(parentComponent, message)
63             : (int) getMockResponse();
64   }
65
66   /**
67    * Message, title, optionType
68    * 
69    * @param parentComponent
70    * @param message
71    * @param title
72    * @param optionType
73    * @return
74    * @throws HeadlessException
75    */
76   public static int showConfirmDialog(Component parentComponent,
77           Object message, String title, int optionType)
78           throws HeadlessException
79   {
80     if (!isInteractiveMode())
81     {
82       return (int) getMockResponse();
83     }
84     switch (optionType)
85     {
86     case JvOptionPane.YES_NO_CANCEL_OPTION:
87       // FeatureRenderer amendFeatures  ?? TODO ??
88       // Chimera close
89       // PromptUserConfig
90       // $FALL-THROUGH$
91     default:
92     case JvOptionPane.YES_NO_OPTION:
93       // PromptUserConfig usage stats
94       // for now treated as "OK CANCEL"
95       // $FALL-THROUGH$
96     case JvOptionPane.OK_CANCEL_OPTION:
97       // will fall back to simple HTML
98       return JOptionPane.showConfirmDialog(parentComponent, message, title,
99               optionType);
100     }
101   }
102
103   /**
104    * Adds a message type. Fallback is to just add it in the beginning.
105    * 
106    * @param parentComponent
107    * @param message
108    * @param title
109    * @param optionType
110    * @param messageType
111    * @return
112    * @throws HeadlessException
113    */
114   public static int showConfirmDialog(Component parentComponent,
115           Object message, String title, int optionType, int messageType)
116           throws HeadlessException
117   {
118     // JalviewServicesChanged
119     // PromptUserConfig raiseDialog
120     return isInteractiveMode()
121             ? JOptionPane.showConfirmDialog(parentComponent, message, title,
122                     optionType, messageType)
123             : (int) getMockResponse();
124   }
125
126   /**
127    * Adds an icon
128    * 
129    * @param parentComponent
130    * @param message
131    * @param title
132    * @param optionType
133    * @param messageType
134    * @param icon
135    * @return
136    * @throws HeadlessException
137    */
138   public static int showConfirmDialog(Component parentComponent,
139           Object message, String title, int optionType, int messageType,
140           Icon icon) throws HeadlessException
141   {
142     // JvOptionPaneTest only
143     return isInteractiveMode()
144             ? JOptionPane.showConfirmDialog(parentComponent, message, title,
145                     optionType, messageType, icon)
146             : (int) getMockResponse();
147   }
148
149   /**
150    * Internal version "OK"
151    * 
152    * @param parentComponent
153    * @param message
154    * @return
155    */
156   public static int showInternalConfirmDialog(Component parentComponent,
157           Object message)
158   {
159     // JvOptionPaneTest only;
160     return isInteractiveMode() ? JOptionPane.showInternalConfirmDialog(
161             parentComponent, message) : (int) getMockResponse();
162   }
163
164   /**
165    * Internal version -- changed to standard version for now
166    * 
167    * @param parentComponent
168    * @param message
169    * @param title
170    * @param optionType
171    * @return
172    */
173   public static int showInternalConfirmDialog(Component parentComponent,
174           String message, String title, int optionType)
175   {
176     if (!isInteractiveMode())
177     {
178       return (int) getMockResponse();
179     }
180     switch (optionType)
181     {
182     case JvOptionPane.YES_NO_CANCEL_OPTION:
183       // ColourMenuHelper.addMenuItmers.offerRemoval TODO
184     case JvOptionPane.YES_NO_OPTION:
185       // UserDefinedColoursSave -- relevant? TODO
186       // $FALL-THROUGH$
187     default:
188     case JvOptionPane.OK_CANCEL_OPTION:
189
190       // EditNameDialog --- uses panel for messsage TODO
191
192       // Desktop.inputURLMenuItem
193       // WsPreferenses
194       return JOptionPane.showConfirmDialog(parentComponent, message, title,
195               optionType);
196     }
197   }
198
199   /**
200    * 
201    * @param parentComponent
202    * @param message
203    * @param title
204    * @param optionType
205    * @param messageType
206    * @return
207    */
208   public static int showInternalConfirmDialog(Component parentComponent,
209           Object message, String title, int optionType, int messageType)
210   {
211     if (!isInteractiveMode())
212     {
213       return (int) getMockResponse();
214     }
215     switch (optionType)
216     {
217     case JvOptionPane.YES_NO_CANCEL_OPTION:
218     case JvOptionPane.YES_NO_OPTION:
219       // UserQuestionanaireCheck
220       // VamsasApplication
221       // $FALL-THROUGH$
222     default:
223     case JvOptionPane.OK_CANCEL_OPTION:
224       // will fall back to simple HTML
225       return JOptionPane.showConfirmDialog(parentComponent, message, title,
226               optionType, messageType);
227     }
228   }
229
230   /**
231    * adds icon; no longer internal
232    * 
233    * @param parentComponent
234    * @param message
235    * @param title
236    * @param optionType
237    * @param messageType
238    * @param icon
239    * @return
240    */
241   public static int showInternalConfirmDialog(Component parentComponent,
242           Object message, String title, int optionType, int messageType,
243           Icon icon)
244   {
245     if (!isInteractiveMode())
246     {
247       return (int) getMockResponse();
248     }
249     switch (optionType)
250     {
251     case JvOptionPane.YES_NO_CANCEL_OPTION:
252     case JvOptionPane.YES_NO_OPTION:
253       //$FALL-THROUGH$
254     default:
255     case JvOptionPane.OK_CANCEL_OPTION:
256       // Preferences editLink/newLink
257       return JOptionPane.showConfirmDialog(parentComponent, message, title,
258               optionType, messageType, icon);
259     }
260
261   }
262
263   /**
264    * custom options full-featured
265    * 
266    * @param parentComponent
267    * @param message
268    * @param title
269    * @param optionType
270    * @param messageType
271    * @param icon
272    * @param options
273    * @param initialValue
274    * @return
275    * @throws HeadlessException
276    */
277   public static int showOptionDialog(Component parentComponent,
278           String message, String title, int optionType, int messageType,
279           Icon icon, Object[] options, Object initialValue)
280           throws HeadlessException
281   {
282     if (!isInteractiveMode())
283     {
284       return (int) getMockResponse();
285     }
286     // two uses:
287     //
288     // TODO
289     //
290     // 1) AlignViewport for openLinkedAlignment
291     //
292     // Show a dialog with the option to open and link (cDNA <-> protein) as a
293     // new
294     // alignment, either as a standalone alignment or in a split frame. Returns
295     // true if the new alignment was opened, false if not, because the user
296     // declined the offer.
297     //
298     // 2) UserDefinedColors warning about saving over a name already defined
299     //
300     return JOptionPane.showOptionDialog(parentComponent, message, title,
301             optionType, messageType, icon, options, initialValue);
302   }
303
304   /**
305    * Just an OK message
306    * 
307    * @param message
308    * @throws HeadlessException
309    */
310   public static void showMessageDialog(Component parentComponent,
311           String message) throws HeadlessException
312   {
313     if (!isInteractiveMode())
314     {
315       outputMessage(message);
316       return;
317     }
318
319     // test class only
320
321     JOptionPane.showMessageDialog(parentComponent, message);
322   }
323
324   /**
325    * OK with message, title, and type
326    * 
327    * @param parentComponent
328    * @param message
329    * @param title
330    * @param messageType
331    * @throws HeadlessException
332    */
333   public static void showMessageDialog(Component parentComponent,
334           String message, String title, int messageType)
335           throws HeadlessException
336   {
337     // 30 implementations -- all just fine.
338
339     if (!isInteractiveMode())
340     {
341       outputMessage(message);
342       return;
343     }
344
345     JOptionPane.showMessageDialog(parentComponent,
346             getPrefix(messageType) + message, title, messageType);
347   }
348
349   /**
350    * adds title and icon
351    * 
352    * @param parentComponent
353    * @param message
354    * @param title
355    * @param messageType
356    * @param icon
357    * @throws HeadlessException
358    */
359   public static void showMessageDialog(Component parentComponent,
360           String message, String title, int messageType, Icon icon)
361           throws HeadlessException
362   {
363
364     // test only
365
366     if (!isInteractiveMode())
367     {
368       outputMessage(message);
369       return;
370     }
371
372     JOptionPane.showMessageDialog(parentComponent, message, title,
373             messageType, icon);
374   }
375
376   /**
377    * was internal
378    * 
379    */
380   public static void showInternalMessageDialog(Component parentComponent,
381           Object message)
382   {
383
384     // WsPreferences only
385
386     if (!isInteractiveMode())
387     {
388       outputMessage(message);
389       return;
390     }
391
392     JOptionPane.showMessageDialog(parentComponent, message);
393   }
394
395
396   /**
397    * Adds title and messageType
398    * 
399    * @param parentComponent
400    * @param message
401    * @param title
402    * @param messageType
403    */
404   public static void showInternalMessageDialog(Component parentComponent,
405           String message, String title, int messageType)
406   {
407
408     // 41 references
409
410     if (!isInteractiveMode())
411     {
412       outputMessage(message);
413       return;
414     }
415
416     JOptionPane.showMessageDialog(parentComponent,
417             getPrefix(messageType) + message, title, messageType);
418   }
419
420   /**
421    * 
422    * @param parentComponent
423    * @param message
424    * @param title
425    * @param messageType
426    * @param icon
427    */
428   public static void showInternalMessageDialog(Component parentComponent,
429           Object message, String title, int messageType, Icon icon)
430   {
431
432     // test only
433
434     if (!isInteractiveMode())
435     {
436       outputMessage(message);
437       return;
438     }
439
440     JOptionPane.showMessageDialog(parentComponent, message, title,
441             messageType, icon);
442   }
443
444   /**
445    * 
446    * @param message
447    * @return
448    * @throws HeadlessException
449    */
450   public static String showInputDialog(Object message)
451           throws HeadlessException
452   {
453     // test only
454
455     if (!isInteractiveMode())
456     {
457       return getMockResponse().toString();
458     }
459
460     return JOptionPane.showInputDialog(message);
461   }
462
463   /**
464    * adds inital selection value
465    * 
466    * @param message
467    * @param initialSelectionValue
468    * @return
469    */
470   public static String showInputDialog(String message,
471           String initialSelectionValue)
472   {
473     if (!isInteractiveMode())
474     {
475       return getMockResponse().toString();
476     }
477
478     // AnnotationPanel character option
479
480     return JOptionPane.showInputDialog(message, initialSelectionValue);
481   }
482
483   /**
484    * centered on parent
485    * 
486    * @param parentComponent
487    * @param message
488    * @return
489    * @throws HeadlessException
490    */
491   public static String showInputDialog(Component parentComponent,
492           String message) throws HeadlessException
493   {
494     // test only
495
496     return isInteractiveMode()
497             ? JOptionPane.showInputDialog(parentComponent, message)
498             : getMockResponse().toString();
499   }
500
501   /**
502    * input with initial selection
503    * 
504    * @param parentComponent
505    * @param message
506    * @param initialSelectionValue
507    * @return
508    */
509   public static String showInputDialog(Component parentComponent,
510           String message, String initialSelectionValue)
511   {
512     
513     // AnnotationPanel
514     
515     return isInteractiveMode()
516             ? JOptionPane.showInputDialog(parentComponent, message,
517                     initialSelectionValue)
518             : getMockResponse().toString();
519   }
520
521   /**
522    * 
523    * @param parentComponent
524    * @param message
525    * @param title
526    * @param messageType
527    * @return
528    * @throws HeadlessException
529    */
530   public static String showInputDialog(Component parentComponent,
531           String message, String title, int messageType)
532           throws HeadlessException
533   {
534
535     // test only
536
537     return isInteractiveMode() ? JOptionPane
538             .showInputDialog(parentComponent, message, title, messageType)
539             : getMockResponse().toString();
540   }
541
542   /**
543    * Customized input option 
544    * 
545    * @param parentComponent
546    * @param message
547    * @param title
548    * @param messageType
549    * @param icon
550    * @param selectionValues
551    * @param initialSelectionValue
552    * @return
553    * @throws HeadlessException
554    */
555   public static Object showInputDialog(Component parentComponent,
556           Object message, String title, int messageType, Icon icon,
557           Object[] selectionValues, Object initialSelectionValue)
558           throws HeadlessException
559   {
560     
561     // test only
562     
563     return isInteractiveMode()
564             ? JOptionPane.showInputDialog(parentComponent, message, title,
565                     messageType, icon, selectionValues,
566                     initialSelectionValue)
567             : getMockResponse().toString();
568   }
569
570   
571   
572   /**
573    * internal version
574    * 
575    * @param parentComponent
576    * @param message
577    * @return
578    */
579   public static String showInternalInputDialog(Component parentComponent,
580           String message)
581   {
582     // test only
583     
584     return isInteractiveMode()
585             ? JOptionPane.showInternalInputDialog(parentComponent, message)
586             : getMockResponse().toString();
587   }
588
589   
590   /**
591    * internal with title and messageType
592    * 
593    * @param parentComponent
594    * @param message
595    * @param title
596    * @param messageType
597    * @return
598    */
599   public static String showInternalInputDialog(Component parentComponent,
600           String message, String title, int messageType)
601   {
602     
603     // AlignFrame tabbedPane_mousePressed
604     
605     return isInteractiveMode()
606             ? JOptionPane.showInternalInputDialog(parentComponent,
607                     getPrefix(messageType) + message, title, messageType)
608             : getMockResponse().toString();
609   }
610
611   /**
612    * customized internal
613    * 
614    * @param parentComponent
615    * @param message
616    * @param title
617    * @param messageType
618    * @param icon
619    * @param selectionValues
620    * @param initialSelectionValue
621    * @return
622    */
623   public static Object showInternalInputDialog(Component parentComponent,
624           String message, String title, int messageType, Icon icon,
625           Object[] selectionValues, Object initialSelectionValue)
626   {
627     // test only
628     
629     return isInteractiveMode()
630             ? JOptionPane.showInternalInputDialog(parentComponent, message,
631                     title, messageType, icon, selectionValues,
632                     initialSelectionValue)
633             : getMockResponse().toString();
634   }
635
636   
637   ///////////// end of options ///////////////
638   
639   
640   private static void outputMessage(Object message)
641   {
642     System.out.println(">>> JOption Message : " + message.toString());
643   }
644
645   public static Object getMockResponse()
646   {
647     return mockResponse;
648   }
649
650   public static void setMockResponse(Object mockOption)
651   {
652     JvOptionPane.mockResponse = mockOption;
653   }
654
655   public static void resetMock()
656   {
657     setMockResponse(JvOptionPane.CANCEL_OPTION);
658     setInteractiveMode(true);
659   }
660
661   public static boolean isInteractiveMode()
662   {
663     return interactiveMode;
664   }
665
666   public static void setInteractiveMode(boolean interactiveMode)
667   {
668     JvOptionPane.interactiveMode = interactiveMode;
669   }
670
671   @SuppressWarnings("unused")
672   private static String getPrefix(int messageType)
673   {
674     String prefix = ""; // JavaScript only
675
676     if (/** @j2sNative true || */
677     false)
678     {
679       switch (messageType)
680       {
681       default:
682       case JvOptionPane.INFORMATION_MESSAGE:
683         prefix = "Note: ";
684         break;
685       case JvOptionPane.WARNING_MESSAGE:
686         prefix = "WARNING! ";
687         break;
688       case JvOptionPane.ERROR_MESSAGE:
689         prefix = "ERRROR! ";
690         break;
691       }
692     }
693     return prefix;
694   }
695
696   DialogRunner<JvOptionPane> runner = new DialogRunner(this);
697
698   private List<Object> ourOptions;
699   /**
700    * create a new option dialog that can be used to register responses - along
701    * lines of showOptionDialog
702    * 
703    * @param desktop
704    * @param question
705    * @param string
706    * @param defaultOption
707    * @param plainMessage
708    * @param object
709    * @param options
710    * @param string2
711    * @return
712    */
713   public static JvOptionPane newOptionDialog(Component parentComponent)
714   {
715     return new JvOptionPane(parentComponent);
716   }
717
718   public void showDialog(
719           String message, String title, int optionType, int messageType,
720           Icon icon, Object[] options, Object initialValue)
721   {
722
723     if (!isInteractiveMode())
724     {
725       runner.firstRun((int) getMockResponse());
726     }
727     // two uses:
728     //
729     // TODO
730     //
731     // 1) AlignViewport for openLinkedAlignment
732     //
733     // Show a dialog with the option to open and link (cDNA <-> protein) as a
734     // new
735     // alignment, either as a standalone alignment or in a split frame. Returns
736     // true if the new alignment was opened, false if not, because the user
737     // declined the offer.
738     //
739     // 2) UserDefinedColors warning about saving over a name already defined
740     //
741     Component parent;
742     /**
743      * @j2sNative
744      * parent = this;
745      */
746     {
747       parent = parentComponent;
748     }
749     ;
750     ourOptions = Arrays.asList(options);
751     int response = JOptionPane.showOptionDialog(parent, message, title,
752             optionType, messageType, icon, options, initialValue);
753     /**
754      * @j2sNative
755      */
756     {
757       runner.firstRun(response);
758     }
759
760   }
761
762   @Override
763   public JvOptionPane response(RunResponse action)
764   {
765
766     runner.response(action);
767     return this;
768   }
769
770   public JvOptionPane defaultResponse(Runnable runnable)
771   {
772     runner.setDefaultResponse(runnable);
773     return this;
774   }
775
776   @Override
777   public void propertyChange(PropertyChangeEvent evt)
778   {
779     int ourOption = ourOptions.indexOf(evt.getNewValue());
780     if (ourOption == -1)
781     {
782       // try our luck..
783       runner.run(evt.getNewValue());
784     }
785     else
786     {
787       runner.run(ourOption);
788     }
789   }
790
791 }