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